Annotation of embedaddon/sqlite3/src/notify.c, revision 1.1

1.1     ! misho       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>