Home | History | Annotate | Download | only in src
      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
    333