File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sqlite3 / src / notify.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 17:04:17 2012 UTC (12 years, 8 months ago) by misho
Branches: sqlite3, MAIN
CVS tags: v3_7_10, HEAD
sqlite3

    1: /*
    2: ** 2009 March 3
    3: **
    4: ** The author disclaims copyright to this source code.  In place of
    5: ** a legal notice, here is a blessing:
    6: **
    7: **    May you do good and not evil.
    8: **    May you find forgiveness for yourself and forgive others.
    9: **    May you share freely, never taking more than you give.
   10: **
   11: *************************************************************************
   12: **
   13: ** This file contains the implementation of the sqlite3_unlock_notify()
   14: ** API method and its associated functionality.
   15: */
   16: #include "sqliteInt.h"
   17: #include "btreeInt.h"
   18: 
   19: /* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */
   20: #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
   21: 
   22: /*
   23: ** Public interfaces:
   24: **
   25: **   sqlite3ConnectionBlocked()
   26: **   sqlite3ConnectionUnlocked()
   27: **   sqlite3ConnectionClosed()
   28: **   sqlite3_unlock_notify()
   29: */
   30: 
   31: #define assertMutexHeld() \
   32:   assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)) )
   33: 
   34: /*
   35: ** Head of a linked list of all sqlite3 objects created by this process
   36: ** for which either sqlite3.pBlockingConnection or sqlite3.pUnlockConnection
   37: ** is not NULL. This variable may only accessed while the STATIC_MASTER
   38: ** mutex is held.
   39: */
   40: static sqlite3 *SQLITE_WSD sqlite3BlockedList = 0;
   41: 
   42: #ifndef NDEBUG
   43: /*
   44: ** This function is a complex assert() that verifies the following 
   45: ** properties of the blocked connections list:
   46: **
   47: **   1) Each entry in the list has a non-NULL value for either 
   48: **      pUnlockConnection or pBlockingConnection, or both.
   49: **
   50: **   2) All entries in the list that share a common value for 
   51: **      xUnlockNotify are grouped together.
   52: **
   53: **   3) If the argument db is not NULL, then none of the entries in the
   54: **      blocked connections list have pUnlockConnection or pBlockingConnection
   55: **      set to db. This is used when closing connection db.
   56: */
   57: static void checkListProperties(sqlite3 *db){
   58:   sqlite3 *p;
   59:   for(p=sqlite3BlockedList; p; p=p->pNextBlocked){
   60:     int seen = 0;
   61:     sqlite3 *p2;
   62: 
   63:     /* Verify property (1) */
   64:     assert( p->pUnlockConnection || p->pBlockingConnection );
   65: 
   66:     /* Verify property (2) */
   67:     for(p2=sqlite3BlockedList; p2!=p; p2=p2->pNextBlocked){
   68:       if( p2->xUnlockNotify==p->xUnlockNotify ) seen = 1;
   69:       assert( p2->xUnlockNotify==p->xUnlockNotify || !seen );
   70:       assert( db==0 || p->pUnlockConnection!=db );
   71:       assert( db==0 || p->pBlockingConnection!=db );
   72:     }
   73:   }
   74: }
   75: #else
   76: # define checkListProperties(x)
   77: #endif
   78: 
   79: /*
   80: ** Remove connection db from the blocked connections list. If connection
   81: ** db is not currently a part of the list, this function is a no-op.
   82: */
   83: static void removeFromBlockedList(sqlite3 *db){
   84:   sqlite3 **pp;
   85:   assertMutexHeld();
   86:   for(pp=&sqlite3BlockedList; *pp; pp = &(*pp)->pNextBlocked){
   87:     if( *pp==db ){
   88:       *pp = (*pp)->pNextBlocked;
   89:       break;
   90:     }
   91:   }
   92: }
   93: 
   94: /*
   95: ** Add connection db to the blocked connections list. It is assumed
   96: ** that it is not already a part of the list.
   97: */
   98: static void addToBlockedList(sqlite3 *db){
   99:   sqlite3 **pp;
  100:   assertMutexHeld();
  101:   for(
  102:     pp=&sqlite3BlockedList; 
  103:     *pp && (*pp)->xUnlockNotify!=db->xUnlockNotify; 
  104:     pp=&(*pp)->pNextBlocked
  105:   );
  106:   db->pNextBlocked = *pp;
  107:   *pp = db;
  108: }
  109: 
  110: /*
  111: ** Obtain the STATIC_MASTER mutex.
  112: */
  113: static void enterMutex(void){
  114:   sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
  115:   checkListProperties(0);
  116: }
  117: 
  118: /*
  119: ** Release the STATIC_MASTER mutex.
  120: */
  121: static void leaveMutex(void){
  122:   assertMutexHeld();
  123:   checkListProperties(0);
  124:   sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
  125: }
  126: 
  127: /*
  128: ** Register an unlock-notify callback.
  129: **
  130: ** This is called after connection "db" has attempted some operation
  131: ** but has received an SQLITE_LOCKED error because another connection
  132: ** (call it pOther) in the same process was busy using the same shared
  133: ** cache.  pOther is found by looking at db->pBlockingConnection.
  134: **
  135: ** If there is no blocking connection, the callback is invoked immediately,
  136: ** before this routine returns.
  137: **
  138: ** If pOther is already blocked on db, then report SQLITE_LOCKED, to indicate
  139: ** a deadlock.
  140: **
  141: ** Otherwise, make arrangements to invoke xNotify when pOther drops
  142: ** its locks.
  143: **
  144: ** Each call to this routine overrides any prior callbacks registered
  145: ** on the same "db".  If xNotify==0 then any prior callbacks are immediately
  146: ** cancelled.
  147: */
  148: int sqlite3_unlock_notify(
  149:   sqlite3 *db,
  150:   void (*xNotify)(void **, int),
  151:   void *pArg
  152: ){
  153:   int rc = SQLITE_OK;
  154: 
  155:   sqlite3_mutex_enter(db->mutex);
  156:   enterMutex();
  157: 
  158:   if( xNotify==0 ){
  159:     removeFromBlockedList(db);
  160:     db->pBlockingConnection = 0;
  161:     db->pUnlockConnection = 0;
  162:     db->xUnlockNotify = 0;
  163:     db->pUnlockArg = 0;
  164:   }else if( 0==db->pBlockingConnection ){
  165:     /* The blocking transaction has been concluded. Or there never was a 
  166:     ** blocking transaction. In either case, invoke the notify callback
  167:     ** immediately. 
  168:     */
  169:     xNotify(&pArg, 1);
  170:   }else{
  171:     sqlite3 *p;
  172: 
  173:     for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection){}
  174:     if( p ){
  175:       rc = SQLITE_LOCKED;              /* Deadlock detected. */
  176:     }else{
  177:       db->pUnlockConnection = db->pBlockingConnection;
  178:       db->xUnlockNotify = xNotify;
  179:       db->pUnlockArg = pArg;
  180:       removeFromBlockedList(db);
  181:       addToBlockedList(db);
  182:     }
  183:   }
  184: 
  185:   leaveMutex();
  186:   assert( !db->mallocFailed );
  187:   sqlite3Error(db, rc, (rc?"database is deadlocked":0));
  188:   sqlite3_mutex_leave(db->mutex);
  189:   return rc;
  190: }
  191: 
  192: /*
  193: ** This function is called while stepping or preparing a statement 
  194: ** associated with connection db. The operation will return SQLITE_LOCKED
  195: ** to the user because it requires a lock that will not be available
  196: ** until connection pBlocker concludes its current transaction.
  197: */
  198: void sqlite3ConnectionBlocked(sqlite3 *db, sqlite3 *pBlocker){
  199:   enterMutex();
  200:   if( db->pBlockingConnection==0 && db->pUnlockConnection==0 ){
  201:     addToBlockedList(db);
  202:   }
  203:   db->pBlockingConnection = pBlocker;
  204:   leaveMutex();
  205: }
  206: 
  207: /*
  208: ** This function is called when
  209: ** the transaction opened by database db has just finished. Locks held 
  210: ** by database connection db have been released.
  211: **
  212: ** This function loops through each entry in the blocked connections
  213: ** list and does the following:
  214: **
  215: **   1) If the sqlite3.pBlockingConnection member of a list entry is
  216: **      set to db, then set pBlockingConnection=0.
  217: **
  218: **   2) If the sqlite3.pUnlockConnection member of a list entry is
  219: **      set to db, then invoke the configured unlock-notify callback and
  220: **      set pUnlockConnection=0.
  221: **
  222: **   3) If the two steps above mean that pBlockingConnection==0 and
  223: **      pUnlockConnection==0, remove the entry from the blocked connections
  224: **      list.
  225: */
  226: void sqlite3ConnectionUnlocked(sqlite3 *db){
  227:   void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */
  228:   int nArg = 0;                            /* Number of entries in aArg[] */
  229:   sqlite3 **pp;                            /* Iterator variable */
  230:   void **aArg;               /* Arguments to the unlock callback */
  231:   void **aDyn = 0;           /* Dynamically allocated space for aArg[] */
  232:   void *aStatic[16];         /* Starter space for aArg[].  No malloc required */
  233: 
  234:   aArg = aStatic;
  235:   enterMutex();         /* Enter STATIC_MASTER mutex */
  236: 
  237:   /* This loop runs once for each entry in the blocked-connections list. */
  238:   for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){
  239:     sqlite3 *p = *pp;
  240: 
  241:     /* Step 1. */
  242:     if( p->pBlockingConnection==db ){
  243:       p->pBlockingConnection = 0;
  244:     }
  245: 
  246:     /* Step 2. */
  247:     if( p->pUnlockConnection==db ){
  248:       assert( p->xUnlockNotify );
  249:       if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){
  250:         xUnlockNotify(aArg, nArg);
  251:         nArg = 0;
  252:       }
  253: 
  254:       sqlite3BeginBenignMalloc();
  255:       assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) );
  256:       assert( nArg<=(int)ArraySize(aStatic) || aArg==aDyn );
  257:       if( (!aDyn && nArg==(int)ArraySize(aStatic))
  258:        || (aDyn && nArg==(int)(sqlite3MallocSize(aDyn)/sizeof(void*)))
  259:       ){
  260:         /* The aArg[] array needs to grow. */
  261:         void **pNew = (void **)sqlite3Malloc(nArg*sizeof(void *)*2);
  262:         if( pNew ){
  263:           memcpy(pNew, aArg, nArg*sizeof(void *));
  264:           sqlite3_free(aDyn);
  265:           aDyn = aArg = pNew;
  266:         }else{
  267:           /* This occurs when the array of context pointers that need to
  268:           ** be passed to the unlock-notify callback is larger than the
  269:           ** aStatic[] array allocated on the stack and the attempt to 
  270:           ** allocate a larger array from the heap has failed.
  271:           **
  272:           ** This is a difficult situation to handle. Returning an error
  273:           ** code to the caller is insufficient, as even if an error code
  274:           ** is returned the transaction on connection db will still be
  275:           ** closed and the unlock-notify callbacks on blocked connections
  276:           ** will go unissued. This might cause the application to wait
  277:           ** indefinitely for an unlock-notify callback that will never 
  278:           ** arrive.
  279:           **
  280:           ** Instead, invoke the unlock-notify callback with the context
  281:           ** array already accumulated. We can then clear the array and
  282:           ** begin accumulating any further context pointers without 
  283:           ** requiring any dynamic allocation. This is sub-optimal because
  284:           ** it means that instead of one callback with a large array of
  285:           ** context pointers the application will receive two or more
  286:           ** callbacks with smaller arrays of context pointers, which will
  287:           ** reduce the applications ability to prioritize multiple 
  288:           ** connections. But it is the best that can be done under the
  289:           ** circumstances.
  290:           */
  291:           xUnlockNotify(aArg, nArg);
  292:           nArg = 0;
  293:         }
  294:       }
  295:       sqlite3EndBenignMalloc();
  296: 
  297:       aArg[nArg++] = p->pUnlockArg;
  298:       xUnlockNotify = p->xUnlockNotify;
  299:       p->pUnlockConnection = 0;
  300:       p->xUnlockNotify = 0;
  301:       p->pUnlockArg = 0;
  302:     }
  303: 
  304:     /* Step 3. */
  305:     if( p->pBlockingConnection==0 && p->pUnlockConnection==0 ){
  306:       /* Remove connection p from the blocked connections list. */
  307:       *pp = p->pNextBlocked;
  308:       p->pNextBlocked = 0;
  309:     }else{
  310:       pp = &p->pNextBlocked;
  311:     }
  312:   }
  313: 
  314:   if( nArg!=0 ){
  315:     xUnlockNotify(aArg, nArg);
  316:   }
  317:   sqlite3_free(aDyn);
  318:   leaveMutex();         /* Leave STATIC_MASTER mutex */
  319: }
  320: 
  321: /*
  322: ** This is called when the database connection passed as an argument is 
  323: ** being closed. The connection is removed from the blocked list.
  324: */
  325: void sqlite3ConnectionClosed(sqlite3 *db){
  326:   sqlite3ConnectionUnlocked(db);
  327:   enterMutex();
  328:   removeFromBlockedList(db);
  329:   checkListProperties(db);
  330:   leaveMutex();
  331: }
  332: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>