extensions/sqlite/sqlite-source/btmutex.c
author Asher Baker <asherkin@gmail.com>
Thu Apr 26 16:58:18 2012 +0100 (2012-04-26)
changeset 3488 bee90c8ef6c3
parent 1646 5e9221257115
permissions -rw-r--r--
Fixed client serials not being unique on Windows (bug 5285, r=psychonic).
dvander@1646
     1
/*
dvander@1646
     2
** 2007 August 27
dvander@1646
     3
**
dvander@1646
     4
** The author disclaims copyright to this source code.  In place of
dvander@1646
     5
** a legal notice, here is a blessing:
dvander@1646
     6
**
dvander@1646
     7
**    May you do good and not evil.
dvander@1646
     8
**    May you find forgiveness for yourself and forgive others.
dvander@1646
     9
**    May you share freely, never taking more than you give.
dvander@1646
    10
**
dvander@1646
    11
*************************************************************************
dvander@1646
    12
**
dvander@1646
    13
** $Id$
dvander@1646
    14
**
dvander@1646
    15
** This file contains code used to implement mutexes on Btree objects.
dvander@1646
    16
** This code really belongs in btree.c.  But btree.c is getting too
dvander@1646
    17
** big and we want to break it down some.  This packaged seemed like
dvander@1646
    18
** a good breakout.
dvander@1646
    19
*/
dvander@1646
    20
#include "btreeInt.h"
dvander@1646
    21
#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
dvander@1646
    22
dvander@1646
    23
dvander@1646
    24
/*
dvander@1646
    25
** Enter a mutex on the given BTree object.
dvander@1646
    26
**
dvander@1646
    27
** If the object is not sharable, then no mutex is ever required
dvander@1646
    28
** and this routine is a no-op.  The underlying mutex is non-recursive.
dvander@1646
    29
** But we keep a reference count in Btree.wantToLock so the behavior
dvander@1646
    30
** of this interface is recursive.
dvander@1646
    31
**
dvander@1646
    32
** To avoid deadlocks, multiple Btrees are locked in the same order
dvander@1646
    33
** by all database connections.  The p->pNext is a list of other
dvander@1646
    34
** Btrees belonging to the same database connection as the p Btree
dvander@1646
    35
** which need to be locked after p.  If we cannot get a lock on
dvander@1646
    36
** p, then first unlock all of the others on p->pNext, then wait
dvander@1646
    37
** for the lock to become available on p, then relock all of the
dvander@1646
    38
** subsequent Btrees that desire a lock.
dvander@1646
    39
*/
dvander@1646
    40
void sqlite3BtreeEnter(Btree *p){
dvander@1646
    41
  Btree *pLater;
dvander@1646
    42
dvander@1646
    43
  /* Some basic sanity checking on the Btree.  The list of Btrees
dvander@1646
    44
  ** connected by pNext and pPrev should be in sorted order by
dvander@1646
    45
  ** Btree.pBt value. All elements of the list should belong to
dvander@1646
    46
  ** the same connection. Only shared Btrees are on the list. */
dvander@1646
    47
  assert( p->pNext==0 || p->pNext->pBt>p->pBt );
dvander@1646
    48
  assert( p->pPrev==0 || p->pPrev->pBt<p->pBt );
dvander@1646
    49
  assert( p->pNext==0 || p->pNext->pSqlite==p->pSqlite );
dvander@1646
    50
  assert( p->pPrev==0 || p->pPrev->pSqlite==p->pSqlite );
dvander@1646
    51
  assert( p->sharable || (p->pNext==0 && p->pPrev==0) );
dvander@1646
    52
dvander@1646
    53
  /* Check for locking consistency */
dvander@1646
    54
  assert( !p->locked || p->wantToLock>0 );
dvander@1646
    55
  assert( p->sharable || p->wantToLock==0 );
dvander@1646
    56
dvander@1646
    57
  /* We should already hold a lock on the database connection */
dvander@1646
    58
  assert( sqlite3_mutex_held(p->pSqlite->mutex) );
dvander@1646
    59
dvander@1646
    60
  if( !p->sharable ) return;
dvander@1646
    61
  p->wantToLock++;
dvander@1646
    62
  if( p->locked ) return;
dvander@1646
    63
dvander@1646
    64
  /* In most cases, we should be able to acquire the lock we
dvander@1646
    65
  ** want without having to go throught the ascending lock
dvander@1646
    66
  ** procedure that follows.  Just be sure not to block.
dvander@1646
    67
  */
dvander@1646
    68
  if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){
dvander@1646
    69
    p->locked = 1;
dvander@1646
    70
    return;
dvander@1646
    71
  }
dvander@1646
    72
dvander@1646
    73
  /* To avoid deadlock, first release all locks with a larger
dvander@1646
    74
  ** BtShared address.  Then acquire our lock.  Then reacquire
dvander@1646
    75
  ** the other BtShared locks that we used to hold in ascending
dvander@1646
    76
  ** order.
dvander@1646
    77
  */
dvander@1646
    78
  for(pLater=p->pNext; pLater; pLater=pLater->pNext){
dvander@1646
    79
    assert( pLater->sharable );
dvander@1646
    80
    assert( pLater->pNext==0 || pLater->pNext->pBt>pLater->pBt );
dvander@1646
    81
    assert( !pLater->locked || pLater->wantToLock>0 );
dvander@1646
    82
    if( pLater->locked ){
dvander@1646
    83
      sqlite3_mutex_leave(pLater->pBt->mutex);
dvander@1646
    84
      pLater->locked = 0;
dvander@1646
    85
    }
dvander@1646
    86
  }
dvander@1646
    87
  sqlite3_mutex_enter(p->pBt->mutex);
dvander@1646
    88
  p->locked = 1;
dvander@1646
    89
  for(pLater=p->pNext; pLater; pLater=pLater->pNext){
dvander@1646
    90
    if( pLater->wantToLock ){
dvander@1646
    91
      sqlite3_mutex_enter(pLater->pBt->mutex);
dvander@1646
    92
      pLater->locked = 1;
dvander@1646
    93
    }
dvander@1646
    94
  }
dvander@1646
    95
}
dvander@1646
    96
dvander@1646
    97
/*
dvander@1646
    98
** Exit the recursive mutex on a Btree.
dvander@1646
    99
*/
dvander@1646
   100
void sqlite3BtreeLeave(Btree *p){
dvander@1646
   101
  if( p->sharable ){
dvander@1646
   102
    assert( p->wantToLock>0 );
dvander@1646
   103
    p->wantToLock--;
dvander@1646
   104
    if( p->wantToLock==0 ){
dvander@1646
   105
      assert( p->locked );
dvander@1646
   106
      sqlite3_mutex_leave(p->pBt->mutex);
dvander@1646
   107
      p->locked = 0;
dvander@1646
   108
    }
dvander@1646
   109
  }
dvander@1646
   110
}
dvander@1646
   111
dvander@1646
   112
#ifndef NDEBUG
dvander@1646
   113
/*
dvander@1646
   114
** Return true if the BtShared mutex is held on the btree.  
dvander@1646
   115
**
dvander@1646
   116
** This routine makes no determination one why or another if the
dvander@1646
   117
** database connection mutex is held.
dvander@1646
   118
**
dvander@1646
   119
** This routine is used only from within assert() statements.
dvander@1646
   120
*/
dvander@1646
   121
int sqlite3BtreeHoldsMutex(Btree *p){
dvander@1646
   122
  return (p->sharable==0 ||
dvander@1646
   123
             (p->locked && p->wantToLock && sqlite3_mutex_held(p->pBt->mutex)));
dvander@1646
   124
}
dvander@1646
   125
#endif
dvander@1646
   126
dvander@1646
   127
dvander@1646
   128
#ifndef SQLITE_OMIT_INCRBLOB
dvander@1646
   129
/*
dvander@1646
   130
** Enter and leave a mutex on a Btree given a cursor owned by that
dvander@1646
   131
** Btree.  These entry points are used by incremental I/O and can be
dvander@1646
   132
** omitted if that module is not used.
dvander@1646
   133
*/
dvander@1646
   134
void sqlite3BtreeEnterCursor(BtCursor *pCur){
dvander@1646
   135
  sqlite3BtreeEnter(pCur->pBtree);
dvander@1646
   136
}
dvander@1646
   137
void sqlite3BtreeLeaveCursor(BtCursor *pCur){
dvander@1646
   138
  sqlite3BtreeLeave(pCur->pBtree);
dvander@1646
   139
}
dvander@1646
   140
#endif /* SQLITE_OMIT_INCRBLOB */
dvander@1646
   141
dvander@1646
   142
dvander@1646
   143
/*
dvander@1646
   144
** Enter the mutex on every Btree associated with a database
dvander@1646
   145
** connection.  This is needed (for example) prior to parsing
dvander@1646
   146
** a statement since we will be comparing table and column names
dvander@1646
   147
** against all schemas and we do not want those schemas being
dvander@1646
   148
** reset out from under us.
dvander@1646
   149
**
dvander@1646
   150
** There is a corresponding leave-all procedures.
dvander@1646
   151
**
dvander@1646
   152
** Enter the mutexes in accending order by BtShared pointer address
dvander@1646
   153
** to avoid the possibility of deadlock when two threads with
dvander@1646
   154
** two or more btrees in common both try to lock all their btrees
dvander@1646
   155
** at the same instant.
dvander@1646
   156
*/
dvander@1646
   157
void sqlite3BtreeEnterAll(sqlite3 *db){
dvander@1646
   158
  int i;
dvander@1646
   159
  Btree *p, *pLater;
dvander@1646
   160
  assert( sqlite3_mutex_held(db->mutex) );
dvander@1646
   161
  for(i=0; i<db->nDb; i++){
dvander@1646
   162
    p = db->aDb[i].pBt;
dvander@1646
   163
    if( p && p->sharable ){
dvander@1646
   164
      p->wantToLock++;
dvander@1646
   165
      if( !p->locked ){
dvander@1646
   166
        assert( p->wantToLock==1 );
dvander@1646
   167
        while( p->pPrev ) p = p->pPrev;
dvander@1646
   168
        while( p->locked && p->pNext ) p = p->pNext;
dvander@1646
   169
        for(pLater = p->pNext; pLater; pLater=pLater->pNext){
dvander@1646
   170
          if( pLater->locked ){
dvander@1646
   171
            sqlite3_mutex_leave(pLater->pBt->mutex);
dvander@1646
   172
            pLater->locked = 0;
dvander@1646
   173
          }
dvander@1646
   174
        }
dvander@1646
   175
        while( p ){
dvander@1646
   176
          sqlite3_mutex_enter(p->pBt->mutex);
dvander@1646
   177
          p->locked++;
dvander@1646
   178
          p = p->pNext;
dvander@1646
   179
        }
dvander@1646
   180
      }
dvander@1646
   181
    }
dvander@1646
   182
  }
dvander@1646
   183
}
dvander@1646
   184
void sqlite3BtreeLeaveAll(sqlite3 *db){
dvander@1646
   185
  int i;
dvander@1646
   186
  Btree *p;
dvander@1646
   187
  assert( sqlite3_mutex_held(db->mutex) );
dvander@1646
   188
  for(i=0; i<db->nDb; i++){
dvander@1646
   189
    p = db->aDb[i].pBt;
dvander@1646
   190
    if( p && p->sharable ){
dvander@1646
   191
      assert( p->wantToLock>0 );
dvander@1646
   192
      p->wantToLock--;
dvander@1646
   193
      if( p->wantToLock==0 ){
dvander@1646
   194
        assert( p->locked );
dvander@1646
   195
        sqlite3_mutex_leave(p->pBt->mutex);
dvander@1646
   196
        p->locked = 0;
dvander@1646
   197
      }
dvander@1646
   198
    }
dvander@1646
   199
  }
dvander@1646
   200
}
dvander@1646
   201
dvander@1646
   202
#ifndef NDEBUG
dvander@1646
   203
/*
dvander@1646
   204
** Return true if the current thread holds the database connection
dvander@1646
   205
** mutex and all required BtShared mutexes.
dvander@1646
   206
**
dvander@1646
   207
** This routine is used inside assert() statements only.
dvander@1646
   208
*/
dvander@1646
   209
int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){
dvander@1646
   210
  int i;
dvander@1646
   211
  if( !sqlite3_mutex_held(db->mutex) ){
dvander@1646
   212
    return 0;
dvander@1646
   213
  }
dvander@1646
   214
  for(i=0; i<db->nDb; i++){
dvander@1646
   215
    Btree *p;
dvander@1646
   216
    p = db->aDb[i].pBt;
dvander@1646
   217
    if( p && p->sharable &&
dvander@1646
   218
         (p->wantToLock==0 || !sqlite3_mutex_held(p->pBt->mutex)) ){
dvander@1646
   219
      return 0;
dvander@1646
   220
    }
dvander@1646
   221
  }
dvander@1646
   222
  return 1;
dvander@1646
   223
}
dvander@1646
   224
#endif /* NDEBUG */
dvander@1646
   225
dvander@1646
   226
/*
dvander@1646
   227
** Potentially dd a new Btree pointer to a BtreeMutexArray.
dvander@1646
   228
** Really only add the Btree if it can possibly be shared with
dvander@1646
   229
** another database connection.
dvander@1646
   230
**
dvander@1646
   231
** The Btrees are kept in sorted order by pBtree->pBt.  That
dvander@1646
   232
** way when we go to enter all the mutexes, we can enter them
dvander@1646
   233
** in order without every having to backup and retry and without
dvander@1646
   234
** worrying about deadlock.
dvander@1646
   235
**
dvander@1646
   236
** The number of shared btrees will always be small (usually 0 or 1)
dvander@1646
   237
** so an insertion sort is an adequate algorithm here.
dvander@1646
   238
*/
dvander@1646
   239
void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pArray, Btree *pBtree){
dvander@1646
   240
  int i, j;
dvander@1646
   241
  BtShared *pBt;
dvander@1646
   242
  if( pBtree==0 || pBtree->sharable==0 ) return;
dvander@1646
   243
#ifndef NDEBUG
dvander@1646
   244
  {
dvander@1646
   245
    for(i=0; i<pArray->nMutex; i++){
dvander@1646
   246
      assert( pArray->aBtree[i]!=pBtree );
dvander@1646
   247
    }
dvander@1646
   248
  }
dvander@1646
   249
#endif
dvander@1646
   250
  assert( pArray->nMutex>=0 );
dvander@1646
   251
  assert( pArray->nMutex<sizeof(pArray->aBtree)/sizeof(pArray->aBtree[0])-1 );
dvander@1646
   252
  pBt = pBtree->pBt;
dvander@1646
   253
  for(i=0; i<pArray->nMutex; i++){
dvander@1646
   254
    assert( pArray->aBtree[i]!=pBtree );
dvander@1646
   255
    if( pArray->aBtree[i]->pBt>pBt ){
dvander@1646
   256
      for(j=pArray->nMutex; j>i; j--){
dvander@1646
   257
        pArray->aBtree[j] = pArray->aBtree[j-1];
dvander@1646
   258
      }
dvander@1646
   259
      pArray->aBtree[i] = pBtree;
dvander@1646
   260
      pArray->nMutex++;
dvander@1646
   261
      return;
dvander@1646
   262
    }
dvander@1646
   263
  }
dvander@1646
   264
  pArray->aBtree[pArray->nMutex++] = pBtree;
dvander@1646
   265
}
dvander@1646
   266
dvander@1646
   267
/*
dvander@1646
   268
** Enter the mutex of every btree in the array.  This routine is
dvander@1646
   269
** called at the beginning of sqlite3VdbeExec().  The mutexes are
dvander@1646
   270
** exited at the end of the same function.
dvander@1646
   271
*/
dvander@1646
   272
void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pArray){
dvander@1646
   273
  int i;
dvander@1646
   274
  for(i=0; i<pArray->nMutex; i++){
dvander@1646
   275
    Btree *p = pArray->aBtree[i];
dvander@1646
   276
    /* Some basic sanity checking */
dvander@1646
   277
    assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt );
dvander@1646
   278
    assert( !p->locked || p->wantToLock>0 );
dvander@1646
   279
dvander@1646
   280
    /* We should already hold a lock on the database connection */
dvander@1646
   281
    assert( sqlite3_mutex_held(p->pSqlite->mutex) );
dvander@1646
   282
dvander@1646
   283
    p->wantToLock++;
dvander@1646
   284
    if( !p->locked && p->sharable ){
dvander@1646
   285
      sqlite3_mutex_enter(p->pBt->mutex);
dvander@1646
   286
      p->locked = 1;
dvander@1646
   287
    }
dvander@1646
   288
  }
dvander@1646
   289
}
dvander@1646
   290
dvander@1646
   291
/*
dvander@1646
   292
** Leave the mutex of every btree in the group.
dvander@1646
   293
*/
dvander@1646
   294
void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pArray){
dvander@1646
   295
  int i;
dvander@1646
   296
  for(i=0; i<pArray->nMutex; i++){
dvander@1646
   297
    Btree *p = pArray->aBtree[i];
dvander@1646
   298
    /* Some basic sanity checking */
dvander@1646
   299
    assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt );
dvander@1646
   300
    assert( p->locked || !p->sharable );
dvander@1646
   301
    assert( p->wantToLock>0 );
dvander@1646
   302
dvander@1646
   303
    /* We should already hold a lock on the database connection */
dvander@1646
   304
    assert( sqlite3_mutex_held(p->pSqlite->mutex) );
dvander@1646
   305
dvander@1646
   306
    p->wantToLock--;
dvander@1646
   307
    if( p->wantToLock==0 && p->locked ){
dvander@1646
   308
      sqlite3_mutex_leave(p->pBt->mutex);
dvander@1646
   309
      p->locked = 0;
dvander@1646
   310
    }
dvander@1646
   311
  }
dvander@1646
   312
}
dvander@1646
   313
dvander@1646
   314
dvander@1646
   315
#endif  /* SQLITE_THREADSAFE && !SQLITE_OMIT_SHARED_CACHE */