| /* | 
 | ** 2009 March 3 | 
 | ** | 
 | ** The author disclaims copyright to this source code.  In place of | 
 | ** a legal notice, here is a blessing: | 
 | ** | 
 | **    May you do good and not evil. | 
 | **    May you find forgiveness for yourself and forgive others. | 
 | **    May you share freely, never taking more than you give. | 
 | ** | 
 | ************************************************************************* | 
 | ** | 
 | ** This file contains the implementation of the sqlite3_unlock_notify() | 
 | ** API method and its associated functionality. | 
 | */ | 
 | #include "sqliteInt.h" | 
 | #include "btreeInt.h" | 
 |  | 
 | /* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */ | 
 | #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY | 
 |  | 
 | /* | 
 | ** Public interfaces: | 
 | ** | 
 | **   sqlite3ConnectionBlocked() | 
 | **   sqlite3ConnectionUnlocked() | 
 | **   sqlite3ConnectionClosed() | 
 | **   sqlite3_unlock_notify() | 
 | */ | 
 |  | 
 | #define assertMutexHeld() \ | 
 |   assert( sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)) ) | 
 |  | 
 | /* | 
 | ** Head of a linked list of all sqlite3 objects created by this process | 
 | ** for which either sqlite3.pBlockingConnection or sqlite3.pUnlockConnection | 
 | ** is not NULL. This variable may only accessed while the STATIC_MAIN | 
 | ** mutex is held. | 
 | */ | 
 | static sqlite3 *SQLITE_WSD sqlite3BlockedList = 0; | 
 |  | 
 | #ifndef NDEBUG | 
 | /* | 
 | ** This function is a complex assert() that verifies the following  | 
 | ** properties of the blocked connections list: | 
 | ** | 
 | **   1) Each entry in the list has a non-NULL value for either  | 
 | **      pUnlockConnection or pBlockingConnection, or both. | 
 | ** | 
 | **   2) All entries in the list that share a common value for  | 
 | **      xUnlockNotify are grouped together. | 
 | ** | 
 | **   3) If the argument db is not NULL, then none of the entries in the | 
 | **      blocked connections list have pUnlockConnection or pBlockingConnection | 
 | **      set to db. This is used when closing connection db. | 
 | */ | 
 | static void checkListProperties(sqlite3 *db){ | 
 |   sqlite3 *p; | 
 |   for(p=sqlite3BlockedList; p; p=p->pNextBlocked){ | 
 |     int seen = 0; | 
 |     sqlite3 *p2; | 
 |  | 
 |     /* Verify property (1) */ | 
 |     assert( p->pUnlockConnection || p->pBlockingConnection ); | 
 |  | 
 |     /* Verify property (2) */ | 
 |     for(p2=sqlite3BlockedList; p2!=p; p2=p2->pNextBlocked){ | 
 |       if( p2->xUnlockNotify==p->xUnlockNotify ) seen = 1; | 
 |       assert( p2->xUnlockNotify==p->xUnlockNotify || !seen ); | 
 |       assert( db==0 || p->pUnlockConnection!=db ); | 
 |       assert( db==0 || p->pBlockingConnection!=db ); | 
 |     } | 
 |   } | 
 | } | 
 | #else | 
 | # define checkListProperties(x) | 
 | #endif | 
 |  | 
 | /* | 
 | ** Remove connection db from the blocked connections list. If connection | 
 | ** db is not currently a part of the list, this function is a no-op. | 
 | */ | 
 | static void removeFromBlockedList(sqlite3 *db){ | 
 |   sqlite3 **pp; | 
 |   assertMutexHeld(); | 
 |   for(pp=&sqlite3BlockedList; *pp; pp = &(*pp)->pNextBlocked){ | 
 |     if( *pp==db ){ | 
 |       *pp = (*pp)->pNextBlocked; | 
 |       break; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | /* | 
 | ** Add connection db to the blocked connections list. It is assumed | 
 | ** that it is not already a part of the list. | 
 | */ | 
 | static void addToBlockedList(sqlite3 *db){ | 
 |   sqlite3 **pp; | 
 |   assertMutexHeld(); | 
 |   for( | 
 |     pp=&sqlite3BlockedList;  | 
 |     *pp && (*pp)->xUnlockNotify!=db->xUnlockNotify;  | 
 |     pp=&(*pp)->pNextBlocked | 
 |   ); | 
 |   db->pNextBlocked = *pp; | 
 |   *pp = db; | 
 | } | 
 |  | 
 | /* | 
 | ** Obtain the STATIC_MAIN mutex. | 
 | */ | 
 | static void enterMutex(void){ | 
 |   sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)); | 
 |   checkListProperties(0); | 
 | } | 
 |  | 
 | /* | 
 | ** Release the STATIC_MAIN mutex. | 
 | */ | 
 | static void leaveMutex(void){ | 
 |   assertMutexHeld(); | 
 |   checkListProperties(0); | 
 |   sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MAIN)); | 
 | } | 
 |  | 
 | /* | 
 | ** Register an unlock-notify callback. | 
 | ** | 
 | ** This is called after connection "db" has attempted some operation | 
 | ** but has received an SQLITE_LOCKED error because another connection | 
 | ** (call it pOther) in the same process was busy using the same shared | 
 | ** cache.  pOther is found by looking at db->pBlockingConnection. | 
 | ** | 
 | ** If there is no blocking connection, the callback is invoked immediately, | 
 | ** before this routine returns. | 
 | ** | 
 | ** If pOther is already blocked on db, then report SQLITE_LOCKED, to indicate | 
 | ** a deadlock. | 
 | ** | 
 | ** Otherwise, make arrangements to invoke xNotify when pOther drops | 
 | ** its locks. | 
 | ** | 
 | ** Each call to this routine overrides any prior callbacks registered | 
 | ** on the same "db".  If xNotify==0 then any prior callbacks are immediately | 
 | ** cancelled. | 
 | */ | 
 | int sqlite3_unlock_notify( | 
 |   sqlite3 *db, | 
 |   void (*xNotify)(void **, int), | 
 |   void *pArg | 
 | ){ | 
 |   int rc = SQLITE_OK; | 
 |  | 
 |   sqlite3_mutex_enter(db->mutex); | 
 |   enterMutex(); | 
 |  | 
 |   if( xNotify==0 ){ | 
 |     removeFromBlockedList(db); | 
 |     db->pBlockingConnection = 0; | 
 |     db->pUnlockConnection = 0; | 
 |     db->xUnlockNotify = 0; | 
 |     db->pUnlockArg = 0; | 
 |   }else if( 0==db->pBlockingConnection ){ | 
 |     /* The blocking transaction has been concluded. Or there never was a  | 
 |     ** blocking transaction. In either case, invoke the notify callback | 
 |     ** immediately.  | 
 |     */ | 
 |     xNotify(&pArg, 1); | 
 |   }else{ | 
 |     sqlite3 *p; | 
 |  | 
 |     for(p=db->pBlockingConnection; p && p!=db; p=p->pUnlockConnection){} | 
 |     if( p ){ | 
 |       rc = SQLITE_LOCKED;              /* Deadlock detected. */ | 
 |     }else{ | 
 |       db->pUnlockConnection = db->pBlockingConnection; | 
 |       db->xUnlockNotify = xNotify; | 
 |       db->pUnlockArg = pArg; | 
 |       removeFromBlockedList(db); | 
 |       addToBlockedList(db); | 
 |     } | 
 |   } | 
 |  | 
 |   leaveMutex(); | 
 |   assert( !db->mallocFailed ); | 
 |   sqlite3ErrorWithMsg(db, rc, (rc?"database is deadlocked":0)); | 
 |   sqlite3_mutex_leave(db->mutex); | 
 |   return rc; | 
 | } | 
 |  | 
 | /* | 
 | ** This function is called while stepping or preparing a statement  | 
 | ** associated with connection db. The operation will return SQLITE_LOCKED | 
 | ** to the user because it requires a lock that will not be available | 
 | ** until connection pBlocker concludes its current transaction. | 
 | */ | 
 | void sqlite3ConnectionBlocked(sqlite3 *db, sqlite3 *pBlocker){ | 
 |   enterMutex(); | 
 |   if( db->pBlockingConnection==0 && db->pUnlockConnection==0 ){ | 
 |     addToBlockedList(db); | 
 |   } | 
 |   db->pBlockingConnection = pBlocker; | 
 |   leaveMutex(); | 
 | } | 
 |  | 
 | /* | 
 | ** This function is called when | 
 | ** the transaction opened by database db has just finished. Locks held  | 
 | ** by database connection db have been released. | 
 | ** | 
 | ** This function loops through each entry in the blocked connections | 
 | ** list and does the following: | 
 | ** | 
 | **   1) If the sqlite3.pBlockingConnection member of a list entry is | 
 | **      set to db, then set pBlockingConnection=0. | 
 | ** | 
 | **   2) If the sqlite3.pUnlockConnection member of a list entry is | 
 | **      set to db, then invoke the configured unlock-notify callback and | 
 | **      set pUnlockConnection=0. | 
 | ** | 
 | **   3) If the two steps above mean that pBlockingConnection==0 and | 
 | **      pUnlockConnection==0, remove the entry from the blocked connections | 
 | **      list. | 
 | */ | 
 | void sqlite3ConnectionUnlocked(sqlite3 *db){ | 
 |   void (*xUnlockNotify)(void **, int) = 0; /* Unlock-notify cb to invoke */ | 
 |   int nArg = 0;                            /* Number of entries in aArg[] */ | 
 |   sqlite3 **pp;                            /* Iterator variable */ | 
 |   void **aArg;               /* Arguments to the unlock callback */ | 
 |   void **aDyn = 0;           /* Dynamically allocated space for aArg[] */ | 
 |   void *aStatic[16];         /* Starter space for aArg[].  No malloc required */ | 
 |  | 
 |   aArg = aStatic; | 
 |   enterMutex();         /* Enter STATIC_MAIN mutex */ | 
 |  | 
 |   /* This loop runs once for each entry in the blocked-connections list. */ | 
 |   for(pp=&sqlite3BlockedList; *pp; /* no-op */ ){ | 
 |     sqlite3 *p = *pp; | 
 |  | 
 |     /* Step 1. */ | 
 |     if( p->pBlockingConnection==db ){ | 
 |       p->pBlockingConnection = 0; | 
 |     } | 
 |  | 
 |     /* Step 2. */ | 
 |     if( p->pUnlockConnection==db ){ | 
 |       assert( p->xUnlockNotify ); | 
 |       if( p->xUnlockNotify!=xUnlockNotify && nArg!=0 ){ | 
 |         xUnlockNotify(aArg, nArg); | 
 |         nArg = 0; | 
 |       } | 
 |  | 
 |       sqlite3BeginBenignMalloc(); | 
 |       assert( aArg==aDyn || (aDyn==0 && aArg==aStatic) ); | 
 |       assert( nArg<=(int)ArraySize(aStatic) || aArg==aDyn ); | 
 |       if( (!aDyn && nArg==(int)ArraySize(aStatic)) | 
 |        || (aDyn && nArg==(int)(sqlite3MallocSize(aDyn)/sizeof(void*))) | 
 |       ){ | 
 |         /* The aArg[] array needs to grow. */ | 
 |         void **pNew = (void **)sqlite3Malloc(nArg*sizeof(void *)*2); | 
 |         if( pNew ){ | 
 |           memcpy(pNew, aArg, nArg*sizeof(void *)); | 
 |           sqlite3_free(aDyn); | 
 |           aDyn = aArg = pNew; | 
 |         }else{ | 
 |           /* This occurs when the array of context pointers that need to | 
 |           ** be passed to the unlock-notify callback is larger than the | 
 |           ** aStatic[] array allocated on the stack and the attempt to  | 
 |           ** allocate a larger array from the heap has failed. | 
 |           ** | 
 |           ** This is a difficult situation to handle. Returning an error | 
 |           ** code to the caller is insufficient, as even if an error code | 
 |           ** is returned the transaction on connection db will still be | 
 |           ** closed and the unlock-notify callbacks on blocked connections | 
 |           ** will go unissued. This might cause the application to wait | 
 |           ** indefinitely for an unlock-notify callback that will never  | 
 |           ** arrive. | 
 |           ** | 
 |           ** Instead, invoke the unlock-notify callback with the context | 
 |           ** array already accumulated. We can then clear the array and | 
 |           ** begin accumulating any further context pointers without  | 
 |           ** requiring any dynamic allocation. This is sub-optimal because | 
 |           ** it means that instead of one callback with a large array of | 
 |           ** context pointers the application will receive two or more | 
 |           ** callbacks with smaller arrays of context pointers, which will | 
 |           ** reduce the applications ability to prioritize multiple  | 
 |           ** connections. But it is the best that can be done under the | 
 |           ** circumstances. | 
 |           */ | 
 |           xUnlockNotify(aArg, nArg); | 
 |           nArg = 0; | 
 |         } | 
 |       } | 
 |       sqlite3EndBenignMalloc(); | 
 |  | 
 |       aArg[nArg++] = p->pUnlockArg; | 
 |       xUnlockNotify = p->xUnlockNotify; | 
 |       p->pUnlockConnection = 0; | 
 |       p->xUnlockNotify = 0; | 
 |       p->pUnlockArg = 0; | 
 |     } | 
 |  | 
 |     /* Step 3. */ | 
 |     if( p->pBlockingConnection==0 && p->pUnlockConnection==0 ){ | 
 |       /* Remove connection p from the blocked connections list. */ | 
 |       *pp = p->pNextBlocked; | 
 |       p->pNextBlocked = 0; | 
 |     }else{ | 
 |       pp = &p->pNextBlocked; | 
 |     } | 
 |   } | 
 |  | 
 |   if( nArg!=0 ){ | 
 |     xUnlockNotify(aArg, nArg); | 
 |   } | 
 |   sqlite3_free(aDyn); | 
 |   leaveMutex();         /* Leave STATIC_MAIN mutex */ | 
 | } | 
 |  | 
 | /* | 
 | ** This is called when the database connection passed as an argument is  | 
 | ** being closed. The connection is removed from the blocked list. | 
 | */ | 
 | void sqlite3ConnectionClosed(sqlite3 *db){ | 
 |   sqlite3ConnectionUnlocked(db); | 
 |   enterMutex(); | 
 |   removeFromBlockedList(db); | 
 |   checkListProperties(db); | 
 |   leaveMutex(); | 
 | } | 
 | #endif |