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