SQLite4
Check-in [1336c5d00a]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add support for multiple processes to bt.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 1336c5d00a9e09a41851447de72883ac501f7a81
User & Date: dan 2013-11-11 20:06:20
Context
2013-11-11
20:27
Add the BT_CONTROL_MULTIPROC option. check-in: 7343be21c9 user: dan tags: trunk
20:06
Add support for multiple processes to bt. check-in: 1336c5d00a user: dan tags: trunk
2013-11-09
17:32
Add BT_CONTROL directives to configure the safety-level and auto-checkpoint parameter. Fix bugs. check-in: 15856cf080 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to lsm-test/lsmtest_tdb4.c.

40
41
42
43
44
45
46


47
48
49
50
51
52
53
..
73
74
75
76
77
78
79

80

81
82


83
84
85
86


87
88
89
90
91
92
93
...
232
233
234
235
236
237
238






239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
...
281
282
283
284
285
286
287

288
289


290
291
292
293
294
295
296
...
641
642
643
644
645
646
647
648
649
650
651
652
653

654
655
656
657
658
659
660
  bt_db *pBt;                     /* bt database handle */
  sqlite4_env *pEnv;              /* SQLite environment (for malloc/free) */
  bt_env *pVfs;                   /* Underlying VFS */

  /* Space for bt_fetch() results */
  u8 *aBuffer;                    /* Space to store results */
  int nBuffer;                    /* Allocated size of aBuffer[] in bytes */



  /* Stuff used for crash test simulation */
  BtFile *apFile[2];              /* Database and log files used by pBt */
  bt_env env;                     /* Private VFS for this object */
  int nCrashSync;                 /* Number of syncs until crash (see above) */
  int bCrash;                     /* True once a crash has been simulated */
};
................................................................................
  BtDb *pBt = (BtDb*)pVfs->pVfsCtx;
  int rc;

  if( pBt->bCrash ) return SQLITE4_IOERR;

  p = (BtFile*)testMalloc(sizeof(BtFile));
  if( !p ) return SQLITE4_NOMEM;

  assert( pBt->apFile[0]==0 || pBt->apFile[1]==0 );

  pBt->apFile[pBt->apFile[0]!=0] = p;



  p->pBt = pBt; rc = pBt->pVfs->xOpen(pEnv, pVfs, zFile, flags, &p->pFile);
  if( rc!=SQLITE4_OK ){
    testFree(p);
    p = 0;


  }

  *ppFile = (bt_file*)p;
  return rc;
}

static int btVfsSize(bt_file *pFile, sqlite4_int64 *piRes){
................................................................................
  return rc;
}

static int btVfsSectorSize(bt_file *pFile){
  BtFile *p = (BtFile*)pFile;
  return p->pBt->pVfs->xSectorSize(p->pFile);
}







static int btVfsClose(bt_file *pFile){
  BtFile *p = (BtFile*)pFile;
  int rc;
  assert( p->pBt->apFile[0]==p || p->pBt->apFile[1]==p );
  btFlushSectors(p, 0);
  testFree(p->apSector);
  rc = p->pBt->pVfs->xClose(p->pFile);

  testFree(p);
  return rc;
}

static int btVfsUnlink(sqlite4_env *pEnv, bt_env *pVfs, const char *zFile){
  BtDb *pBt = (BtDb*)pVfs->pVfsCtx;
  if( pBt->bCrash ) return SQLITE4_IOERR;
................................................................................
  BtFile *p = (BtFile*)pFile;
  if( p->pBt->bCrash ) return SQLITE4_IOERR;
  return p->pBt->pVfs->xShmUnmap(p->pFile, bDelete);
}

static int bt_close(TestDb *pTestDb){
  BtDb *p = (BtDb*)pTestDb;

  free(p->aBuffer);
  return sqlite4BtClose(p->pBt);


}

static int btMinTransaction(BtDb *p, int iMin, int *piLevel){
  int iLevel;
  int rc = SQLITE4_OK;

  iLevel = sqlite4BtTransactionLevel(p->pBt);
................................................................................
  if( bClear && zFilename && zFilename[0] ){
    char *zLog = sqlite3_mprintf("%s-wal", zFilename);
    unlink(zFilename);
    unlink(zLog);
    sqlite3_free(zLog);
  }
  
  rc = sqlite4BtNew(pEnv, sizeof(BtDb), &pBt);
  if( rc==SQLITE4_OK ){
    p = (BtDb*)sqlite4BtExtra(pBt);
    p->base.pMethods = &SqlMethods;
    p->pBt = pBt;
    p->pEnv = pEnv;


    p->env.pVfsCtx = (void*)p;
    p->env.xFullpath = btVfsFullpath;
    p->env.xOpen = btVfsOpen;
    p->env.xSize = btVfsSize;
    p->env.xRead = btVfsRead;
    p->env.xWrite = btVfsWrite;







>
>







 







>
|
>
|
|
>
>
|



>
>







 







>
>
>
>
>
>




<



>







 







>

<
>
>







 







|

|



>







40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
..
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256

257
258
259
260
261
262
263
264
265
266
267
...
295
296
297
298
299
300
301
302
303

304
305
306
307
308
309
310
311
312
...
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
  bt_db *pBt;                     /* bt database handle */
  sqlite4_env *pEnv;              /* SQLite environment (for malloc/free) */
  bt_env *pVfs;                   /* Underlying VFS */

  /* Space for bt_fetch() results */
  u8 *aBuffer;                    /* Space to store results */
  int nBuffer;                    /* Allocated size of aBuffer[] in bytes */

  int nRef;

  /* Stuff used for crash test simulation */
  BtFile *apFile[2];              /* Database and log files used by pBt */
  bt_env env;                     /* Private VFS for this object */
  int nCrashSync;                 /* Number of syncs until crash (see above) */
  int bCrash;                     /* True once a crash has been simulated */
};
................................................................................
  BtDb *pBt = (BtDb*)pVfs->pVfsCtx;
  int rc;

  if( pBt->bCrash ) return SQLITE4_IOERR;

  p = (BtFile*)testMalloc(sizeof(BtFile));
  if( !p ) return SQLITE4_NOMEM;
  if( flags & BT_OPEN_DATABASE ){
    pBt->apFile[0] = p;
  }else if( flags & BT_OPEN_LOG ){
    pBt->apFile[1] = p;
  }
  p->pBt = pBt; 

  rc = pBt->pVfs->xOpen(pEnv, pVfs, zFile, flags, &p->pFile);
  if( rc!=SQLITE4_OK ){
    testFree(p);
    p = 0;
  }else{
    pBt->nRef++;
  }

  *ppFile = (bt_file*)p;
  return rc;
}

static int btVfsSize(bt_file *pFile, sqlite4_int64 *piRes){
................................................................................
  return rc;
}

static int btVfsSectorSize(bt_file *pFile){
  BtFile *p = (BtFile*)pFile;
  return p->pBt->pVfs->xSectorSize(p->pFile);
}

static void btDeref(BtDb *p){
  p->nRef--;
  assert( p->nRef>=0 );
  if( p->nRef<=0 ) testFree(p);
}

static int btVfsClose(bt_file *pFile){
  BtFile *p = (BtFile*)pFile;
  int rc;

  btFlushSectors(p, 0);
  testFree(p->apSector);
  rc = p->pBt->pVfs->xClose(p->pFile);
  btDeref(p->pBt);
  testFree(p);
  return rc;
}

static int btVfsUnlink(sqlite4_env *pEnv, bt_env *pVfs, const char *zFile){
  BtDb *pBt = (BtDb*)pVfs->pVfsCtx;
  if( pBt->bCrash ) return SQLITE4_IOERR;
................................................................................
  BtFile *p = (BtFile*)pFile;
  if( p->pBt->bCrash ) return SQLITE4_IOERR;
  return p->pBt->pVfs->xShmUnmap(p->pFile, bDelete);
}

static int bt_close(TestDb *pTestDb){
  BtDb *p = (BtDb*)pTestDb;
  int rc = sqlite4BtClose(p->pBt);
  free(p->aBuffer);

  btDeref(p);
  return rc;
}

static int btMinTransaction(BtDb *p, int iMin, int *piLevel){
  int iLevel;
  int rc = SQLITE4_OK;

  iLevel = sqlite4BtTransactionLevel(p->pBt);
................................................................................
  if( bClear && zFilename && zFilename[0] ){
    char *zLog = sqlite3_mprintf("%s-wal", zFilename);
    unlink(zFilename);
    unlink(zLog);
    sqlite3_free(zLog);
  }
  
  rc = sqlite4BtNew(pEnv, 0, &pBt);
  if( rc==SQLITE4_OK ){
    p = (BtDb*)testMalloc(sizeof(BtDb));
    p->base.pMethods = &SqlMethods;
    p->pBt = pBt;
    p->pEnv = pEnv;
    p->nRef = 1;

    p->env.pVfsCtx = (void*)p;
    p->env.xFullpath = btVfsFullpath;
    p->env.xOpen = btVfsOpen;
    p->env.xSize = btVfsSize;
    p->env.xRead = btVfsRead;
    p->env.xWrite = btVfsWrite;

Changes to src/bt.h.

148
149
150
151
152
153
154












155
156
157
158
159
160

161
162
163
164
165
166
167
...
195
196
197
198
199
200
201
202








** BT_CONTROL_SAFETY:
**   The third argument is interpreted as a pointer to type (int). If
**   the value stored in the (int) location is 0, 1 or 2, then the current
**   b-tree safety level is set to 0, 1 or 2, respectively. Otherwise, the
**   integer value is set to the current safety level.
**
** BT_CONTROL_AUTOCKPT:












*/
#define BT_CONTROL_INFO     7706389
#define BT_CONTROL_SETVFS   7706390
#define BT_CONTROL_GETVFS   7706391
#define BT_CONTROL_SAFETY   7706392
#define BT_CONTROL_AUTOCKPT 7706393


int sqlite4BtControl(bt_db*, int op, void *pArg);

#define BT_SAFETY_OFF    0
#define BT_SAFETY_NORMAL 1
#define BT_SAFETY_FULL   2

................................................................................
  int (*xUnlink)(sqlite4_env*,bt_env*, const char *);
  int (*xLock)(bt_file*, int, int);
  int (*xTestLock)(bt_file*, int, int, int);
  int (*xShmMap)(bt_file*, int, int, void **);
  void (*xShmBarrier)(bt_file*);
  int (*xShmUnmap)(bt_file*, int);
};
















>
>
>
>
>
>
>
>
>
>
>
>






>







 








>
>
>
>
>
>
>
>
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
...
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
** BT_CONTROL_SAFETY:
**   The third argument is interpreted as a pointer to type (int). If
**   the value stored in the (int) location is 0, 1 or 2, then the current
**   b-tree safety level is set to 0, 1 or 2, respectively. Otherwise, the
**   integer value is set to the current safety level.
**
** BT_CONTROL_AUTOCKPT:
**   The third argument is interpreted as a pointer to type (int). If
**   the indicated value is greater than or equal to zero, then the 
**   database connection auto-checkpoint value is set accordingly. If
**   the indicated value is less than zero, it is set to the current
**   auto-checkpoint value before returning.
**
** BT_CONTROL_LOGSIZE:
**   The third argument is interpreted as a pointer to type (int). The
**   value pointer to is set to the number of uncheckpointed frames
**   that stored in the log file according to the snapshot used by the
**   most recently completed transaction or checkpoint operation.
**   
*/
#define BT_CONTROL_INFO     7706389
#define BT_CONTROL_SETVFS   7706390
#define BT_CONTROL_GETVFS   7706391
#define BT_CONTROL_SAFETY   7706392
#define BT_CONTROL_AUTOCKPT 7706393
#define BT_CONTROL_LOGSIZE  7706394

int sqlite4BtControl(bt_db*, int op, void *pArg);

#define BT_SAFETY_OFF    0
#define BT_SAFETY_NORMAL 1
#define BT_SAFETY_FULL   2

................................................................................
  int (*xUnlink)(sqlite4_env*,bt_env*, const char *);
  int (*xLock)(bt_file*, int, int);
  int (*xTestLock)(bt_file*, int, int, int);
  int (*xShmMap)(bt_file*, int, int, void **);
  void (*xShmBarrier)(bt_file*);
  int (*xShmUnmap)(bt_file*, int);
};

/*
** Flags for xOpen
*/
#define BT_OPEN_DATABASE   0x0001
#define BT_OPEN_LOG        0x0002
#define BT_OPEN_READONLY   0x0004


Changes to src/btInt.h.

129
130
131
132
133
134
135


136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
...
206
207
208
209
210
211
212


213
214
215
216
217
218
219
...
231
232
233
234
235
236
237

238
239
240
241
242
243
244

bt_env *sqlite4BtPagerGetEnv(BtPager*);
void sqlite4BtPagerSetEnv(BtPager*, bt_env*);

void sqlite4BtPagerSetSafety(BtPager*, int*);
void sqlite4BtPagerSetAutockpt(BtPager*, int*);



/*
** End of bt_pager.c interface.
*************************************************************************/

/*************************************************************************
** File-system interface.
*/

/* Flags for the 3rd argument to xOpen */
#define BT_OPEN_READONLY 0x0001

/* Candidate values for the 3rd argument to bt_env.xLock() */
#define BT_LOCK_UNLOCK 0
#define BT_LOCK_SHARED 1
#define BT_LOCK_EXCL   2

/* Size of shared-memory chunks - 48KB. */
#define BT_SHM_CHUNK_SIZE (48*1024)
................................................................................

/*************************************************************************
** Interface to bt_lock.c functionality.
*/
typedef struct BtShared BtShared;
typedef struct BtLock BtLock;
typedef struct BtReadSlot BtReadSlot;


struct BtLock {
  /* These three are set by the bt_pager module and thereafter used by 
  ** the bt_lock, bt_pager and bt_log modules. */
  sqlite4_env *pEnv;              /* SQLite environment */
  bt_env *pVfs;                   /* Bt environment */
  bt_file *pFd;                   /* Database file descriptor */
  int iDebugId;                   /* Sometimes useful when debugging */
................................................................................
  int nAutoCkpt;                  /* Auto-checkpoint when log is this large */

  /* These are used only by the bt_lock module. */
  BtShared *pShared;              /* Shared by all handles on this file */
  BtLock *pNext;                  /* Next connection using pShared */
  u32 mExclLock;                  /* Mask of exclusive locks held */
  u32 mSharedLock;                /* Mask of shared locks held */

};

struct BtReadSlot {
  u32 iFirst;
  u32 iLast;
};








>
>








<
<
<







 







>
>







 







>







129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145



146
147
148
149
150
151
152
...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

bt_env *sqlite4BtPagerGetEnv(BtPager*);
void sqlite4BtPagerSetEnv(BtPager*, bt_env*);

void sqlite4BtPagerSetSafety(BtPager*, int*);
void sqlite4BtPagerSetAutockpt(BtPager*, int*);

void sqlite4BtPagerLogsize(BtPager*, int*);

/*
** End of bt_pager.c interface.
*************************************************************************/

/*************************************************************************
** File-system interface.
*/




/* Candidate values for the 3rd argument to bt_env.xLock() */
#define BT_LOCK_UNLOCK 0
#define BT_LOCK_SHARED 1
#define BT_LOCK_EXCL   2

/* Size of shared-memory chunks - 48KB. */
#define BT_SHM_CHUNK_SIZE (48*1024)
................................................................................

/*************************************************************************
** Interface to bt_lock.c functionality.
*/
typedef struct BtShared BtShared;
typedef struct BtLock BtLock;
typedef struct BtReadSlot BtReadSlot;
typedef struct BtFile BtFile;

struct BtLock {
  /* These three are set by the bt_pager module and thereafter used by 
  ** the bt_lock, bt_pager and bt_log modules. */
  sqlite4_env *pEnv;              /* SQLite environment */
  bt_env *pVfs;                   /* Bt environment */
  bt_file *pFd;                   /* Database file descriptor */
  int iDebugId;                   /* Sometimes useful when debugging */
................................................................................
  int nAutoCkpt;                  /* Auto-checkpoint when log is this large */

  /* These are used only by the bt_lock module. */
  BtShared *pShared;              /* Shared by all handles on this file */
  BtLock *pNext;                  /* Next connection using pShared */
  u32 mExclLock;                  /* Mask of exclusive locks held */
  u32 mSharedLock;                /* Mask of shared locks held */
  BtFile *pBtFile;                /* Used to defer close if necessary */
};

struct BtReadSlot {
  u32 iFirst;
  u32 iLast;
};

Changes to src/bt_lock.c.

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
..
87
88
89
90
91
92
93
















94
95
96
97
98
99
100
...
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
...
212
213
214
215
216
217
218

































219
220
221
222
223
224
225
226
227
228
229

230
231
232
233
234
235
236
...
252
253
254
255
256
257
258

259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275




























276
277
278
279





280
281
282
283
284
285
286
...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366

367
368
369
370
371
372
373
...
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
...
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
...
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
...
586
587
588
589
590
591
592




593
594

595
596


597





598
599
600
601



602
603
604
605
606
607
608
#define BT_LOCK_READER_DBONLY 5   /* Reading the db file only */
#define BT_LOCK_READER0       6   /* Array of BT_NREADER locks */

#define BT_LOCK_UNLOCK     0
#define BT_LOCK_SHARED     1
#define BT_LOCK_EXCL       2

typedef struct BtFile BtFile;

/*
** Global data. All global variables used by code in this file are grouped
** into the following structure instance.
**
** pDatabase:
**   Linked list of all Database objects allocated within this process.
**   This list may not be traversed without holding the global mutex (see
................................................................................

/*
** Relinquish the mutex obtained by calling btLockMutexEnter().
*/
static void btLockMutexLeave(sqlite4_env *pEnv){
  sqlite4_mutex_leave(sqlite4_mutex_alloc(pEnv, SQLITE4_MUTEX_STATIC_KV));
}

















static int btLockLockopNonblocking(
  BtLock *p,                      /* BtLock handle */
  int iLock,                      /* Slot to lock */
  int eOp                         /* One of BT_LOCK_UNLOCK, SHARED or EXCL */
){
  const u32 mask = ((u32)1 << iLock);
................................................................................
    }
    assert( nExcl==0 || nExcl==1 );
    assert( nExcl==0 || nShared==0 );

    switch( eOp ){
      case BT_LOCK_UNLOCK:
        if( nShared==0 ){
#if 0
          lockSharedFile(db->pEnv, p, iLock, LSM_LOCK_UNLOCK);
#endif
        }
        p->mExclLock &= ~mask;
        p->mSharedLock &= ~mask;
        break;

      case BT_LOCK_SHARED:
        if( nExcl ){
          rc = SQLITE4_BUSY;
        }else{
          if( nShared==0 ){
#if 0
            rc = lockSharedFile(db->pEnv, p, iLock, LSM_LOCK_SHARED);
#endif
          }
          if( rc==SQLITE4_OK ){
            p->mSharedLock |= mask;
            p->mExclLock &= ~mask;
          }
        }
        break;

      default:
        assert( eOp==BT_LOCK_EXCL );
        if( nExcl || nShared ){
          rc = SQLITE4_BUSY;
        }else{
#if 0
          rc = lockSharedFile(db->pEnv, p, iLock, LSM_LOCK_EXCL);
#endif
          if( rc==SQLITE4_OK ){
            p->mSharedLock &= ~mask;
            p->mExclLock |= mask;
          }
        }
        break;
    }
................................................................................
    if( rc!=SQLITE4_BUSY || bBlock==0 ) break;
    /* todo: Fix blocking locks */
    btLockDelay();
  }
  return rc;
}



































/*
** Connect to the database as a read/write connection. If recovery
** is required (i.e. if this is the first connection to the db), invoke 
** the xRecover() method.
**
** Return SQLITE4_OK if successful, or an SQLite4 error code if an
** error occurs.
*/
int sqlite4BtLockConnect(BtLock *p, int (*xRecover)(BtLock*)){
  sqlite4_env *pEnv = p->pEnv;

  int rc = SQLITE4_OK;
  const char *zName;
  int nName;
  BtShared *pShared;

  zName = sqlite4BtPagerFilename((BtPager*)p, BT_PAGERFILE_DATABASE);
  nName = strlen(zName);
................................................................................
      sqlite4_free(pEnv, pShared);
      sqlite4_mutex_free(pMutex);
      pShared = 0;
      pMutex = 0;
      rc = btErrorBkpt(SQLITE4_NOMEM);
    }else{
      memset(pShared, 0, sizeof(BtShared));

      pShared->nName = nName;
      pShared->zName = (char *)&pShared[1];
      memcpy(pShared->zName, zName, nName+1);
      pShared->pNext = gBtShared.pDatabase;
      pShared->pClientMutex = pMutex;
      gBtShared.pDatabase = pShared;
    }
  }

  if( rc==SQLITE4_OK ){
    pShared->nRef++;
  }
  btLockMutexLeave(p->pEnv);

  /* Add this connection to the linked list at BtShared.pLock */
  if( rc==SQLITE4_OK ){
    sqlite4_mutex_enter(pShared->pClientMutex);




























    p->pNext = pShared->pLock;
    pShared->pLock = p;
    sqlite4_mutex_leave(pShared->pClientMutex);
    p->pShared = pShared;





  }

  if( rc==SQLITE4_OK ){
    rc = btLockLockop(p, BT_LOCK_DMS1, BT_LOCK_EXCL, 1);
    if( rc==SQLITE4_OK ){
      rc = btLockLockop(p, BT_LOCK_DMS2_RW, BT_LOCK_EXCL, 0);
      if( rc==SQLITE4_OK ){
................................................................................
    }
    if( rc==SQLITE4_BUSY ) rc = SQLITE4_OK;
    btLockLockop(p, BT_LOCK_DMS2_RW, BT_LOCK_UNLOCK, 0);
    btLockLockop(p, BT_LOCK_DMS2_RO, BT_LOCK_UNLOCK, 0);
    btLockLockop(p, BT_LOCK_DMS1, BT_LOCK_UNLOCK, 0);
  }

  btLockMutexEnter(p->pEnv);
  pShared->nRef--;
  if( pShared->nRef==0 ){
    int i;
    BtShared **ppS;
    for(ppS=&gBtShared.pDatabase; *ppS!=pShared; ppS=&(*ppS)->pNext);
    *ppS = (*ppS)->pNext;

    sqlite4_mutex_free(pShared->pClientMutex);
    for(i=0; i<pShared->nShmChunk; i++){
      sqlite4_free(p->pEnv, pShared->apShmChunk[i]);
    }
    sqlite4_free(p->pEnv, pShared->apShmChunk);
    sqlite4_free(p->pEnv, pShared);
  }
  btLockMutexLeave(p->pEnv);

  return rc;
}

#ifndef NDEBUG
static void assertNoLockedSlots(BtLock *pLock){
  u32 mask = (1 << (BT_LOCK_READER0+BT_NREADER+1)) - (1 << BT_LOCK_READER0);
  assert( (pLock->mExclLock & mask)==0 );
................................................................................
  if( (iFirst<aLog[0] || iFirst>aLog[1])
   && (iFirst<aLog[2] || iFirst>aLog[3])
   && (iFirst<aLog[4] || iFirst>aLog[5])
  ){
    iLast = 0;
  }

BtReadSlot ss[BT_NREADER];
memcpy(ss, aSlot, BT_NREADER*sizeof(BtReadSlot));
int iOk = -1;

  if( iLast==0 ){
    rc = btLockLockop(pLock, BT_LOCK_READER_DBONLY, BT_LOCK_SHARED, 0);
  }else{
    const int nMaxRetry = 100;
    int nAttempt = 100;           /* Remaining lock attempts */

    for(nAttempt=0; rc==SQLITE4_BUSY && nAttempt<nMaxRetry; nAttempt++){
................................................................................
      int iIdxLast = sqlite4BtLogFrameToIdx(aLog, iLast);

      assert( iIdxFirst>=0 && iIdxLast>=0 );

      /* Try to find a slot populated with the values required. */
      for(i=0; i<BT_NREADER; i++){
        if( aSlot[i].iFirst==iFirst && aSlot[i].iLast==iLast ){
          iOk = i;
          break;
        }
      }

      /* Or, if there is no slot with the required values - try to create one */
      if( i==BT_NREADER ){
        for(i=0; i<BT_NREADER; i++){
................................................................................
}

int sqlite4BtLockShmMap(BtLock *pLock, int iChunk, int nByte, u8 **ppOut){
  int rc = SQLITE4_OK;
  BtShared *pShared = pLock->pShared;
  u8 *pOut = 0;

  assert( pShared->bMultiProc==0 && pShared->bReadonly==0 );

  sqlite4_mutex_enter(pShared->pClientMutex);
  if( pShared->nShmChunk<=iChunk ){
    u8 **apNew;
    int nNew = iChunk+1;
    int nByte = sizeof(u8*)*nNew;

................................................................................
      memset(&apNew[pShared->nShmChunk],0,nByte-sizeof(u8*)*pShared->nShmChunk);
      pShared->nShmChunk = nNew;
      pShared->apShmChunk = apNew;
    }
  }

  if( rc==SQLITE4_OK ){




    if( pShared->apShmChunk[iChunk]==0 ){
      u8 *p = (u8*)sqlite4_malloc(pLock->pEnv, nByte);

      if( p ) memset(p, 0, nByte);
      pShared->apShmChunk[iChunk] = p;


    }





    pOut = pShared->apShmChunk[iChunk];
    if( pOut==0 ){
      rc = btErrorBkpt(SQLITE4_NOMEM);
    }



  }
  sqlite4_mutex_leave(pShared->pClientMutex);
  
  *ppOut = pOut;
  return rc;
}








<
<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
<
<










|
<
<













|
<
<







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>











>







 







>








<








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
<
|
>
>
>
>
>







 







|
|
|
|
<
<
<
<
|
<
<
|
<
<
<
<
>







 







<
<
<
<







 







<







 







|







 







>
>
>
>
|
|
>
|
|
>
>
|
>
>
>
>
>
|
<
<

>
>
>







27
28
29
30
31
32
33


34
35
36
37
38
39
40
..
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
...
145
146
147
148
149
150
151
152


153
154
155
156
157
158
159
160
161
162
163


164
165
166
167
168
169
170
171
172
173
174
175
176
177


178
179
180
181
182
183
184
...
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
...
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309

310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347

348
349
350
351
352
353
354
355
356
357
358
359
360
...
418
419
420
421
422
423
424
425
426
427
428




429


430




431
432
433
434
435
436
437
438
...
469
470
471
472
473
474
475




476
477
478
479
480
481
482
...
485
486
487
488
489
490
491

492
493
494
495
496
497
498
...
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
...
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670


671
672
673
674
675
676
677
678
679
680
681
#define BT_LOCK_READER_DBONLY 5   /* Reading the db file only */
#define BT_LOCK_READER0       6   /* Array of BT_NREADER locks */

#define BT_LOCK_UNLOCK     0
#define BT_LOCK_SHARED     1
#define BT_LOCK_EXCL       2



/*
** Global data. All global variables used by code in this file are grouped
** into the following structure instance.
**
** pDatabase:
**   Linked list of all Database objects allocated within this process.
**   This list may not be traversed without holding the global mutex (see
................................................................................

/*
** Relinquish the mutex obtained by calling btLockMutexEnter().
*/
static void btLockMutexLeave(sqlite4_env *pEnv){
  sqlite4_mutex_leave(sqlite4_mutex_alloc(pEnv, SQLITE4_MUTEX_STATIC_KV));
}

/*
** Take the specified lock on the shared file-handle associated with
** the connection passed as the first argument.
*/
static int btLockSharedFile(BtLock *p, int iLock, int eOp){
  int rc = SQLITE4_OK;
  BtShared *pShared = p->pShared;

  /* This is a no-op in single process mode */
  assert( (pShared->bMultiProc==0)==(pShared->pFile==0) );
  if( pShared->pFile ){
    rc = p->pVfs->xLock(pShared->pFile, iLock, eOp);
  }
  return rc;
}

static int btLockLockopNonblocking(
  BtLock *p,                      /* BtLock handle */
  int iLock,                      /* Slot to lock */
  int eOp                         /* One of BT_LOCK_UNLOCK, SHARED or EXCL */
){
  const u32 mask = ((u32)1 << iLock);
................................................................................
    }
    assert( nExcl==0 || nExcl==1 );
    assert( nExcl==0 || nShared==0 );

    switch( eOp ){
      case BT_LOCK_UNLOCK:
        if( nShared==0 ){
          btLockSharedFile(p, iLock, BT_LOCK_UNLOCK);


        }
        p->mExclLock &= ~mask;
        p->mSharedLock &= ~mask;
        break;

      case BT_LOCK_SHARED:
        if( nExcl ){
          rc = SQLITE4_BUSY;
        }else{
          if( nShared==0 ){
            rc = btLockSharedFile(p, iLock, BT_LOCK_SHARED);


          }
          if( rc==SQLITE4_OK ){
            p->mSharedLock |= mask;
            p->mExclLock &= ~mask;
          }
        }
        break;

      default:
        assert( eOp==BT_LOCK_EXCL );
        if( nExcl || nShared ){
          rc = SQLITE4_BUSY;
        }else{
          rc = btLockSharedFile(p, iLock, BT_LOCK_EXCL);


          if( rc==SQLITE4_OK ){
            p->mSharedLock &= ~mask;
            p->mExclLock |= mask;
          }
        }
        break;
    }
................................................................................
    if( rc!=SQLITE4_BUSY || bBlock==0 ) break;
    /* todo: Fix blocking locks */
    btLockDelay();
  }
  return rc;
}

static void btLockSharedDeref(
  sqlite4_env *pEnv, 
  bt_env *pVfs, 
  BtShared *pShared
){
  btLockMutexEnter(pEnv);
  pShared->nRef--;
  if( pShared->nRef==0 ){
    BtShared **ppS;
    for(ppS=&gBtShared.pDatabase; *ppS!=pShared; ppS=&(*ppS)->pNext);
    *ppS = (*ppS)->pNext;
    while( pShared->pBtFile ){
      BtFile *p = pShared->pBtFile;
      pShared->pBtFile = p->pNext;
      pVfs->xClose(p->pFd);
      sqlite4_free(pEnv, p);
    }
    sqlite4_mutex_free(pShared->pClientMutex);

    /* If they were allocated in heap space, free all "shared" memory chunks */
    if( pShared->pFile==0 ){
      int i;
      for(i=0; i<pShared->nShmChunk; i++){
        sqlite4_free(pEnv, pShared->apShmChunk[i]);
      }
    }else{
      pVfs->xClose(pShared->pFile);
    }
    sqlite4_free(pEnv, pShared->apShmChunk);
    sqlite4_free(pEnv, pShared);
  }
  btLockMutexLeave(pEnv);
}

/*
** Connect to the database as a read/write connection. If recovery
** is required (i.e. if this is the first connection to the db), invoke 
** the xRecover() method.
**
** Return SQLITE4_OK if successful, or an SQLite4 error code if an
** error occurs.
*/
int sqlite4BtLockConnect(BtLock *p, int (*xRecover)(BtLock*)){
  sqlite4_env *pEnv = p->pEnv;
  bt_env *pVfs = p->pVfs;
  int rc = SQLITE4_OK;
  const char *zName;
  int nName;
  BtShared *pShared;

  zName = sqlite4BtPagerFilename((BtPager*)p, BT_PAGERFILE_DATABASE);
  nName = strlen(zName);
................................................................................
      sqlite4_free(pEnv, pShared);
      sqlite4_mutex_free(pMutex);
      pShared = 0;
      pMutex = 0;
      rc = btErrorBkpt(SQLITE4_NOMEM);
    }else{
      memset(pShared, 0, sizeof(BtShared));
      pShared->bMultiProc = 1;
      pShared->nName = nName;
      pShared->zName = (char *)&pShared[1];
      memcpy(pShared->zName, zName, nName+1);
      pShared->pNext = gBtShared.pDatabase;
      pShared->pClientMutex = pMutex;
      gBtShared.pDatabase = pShared;
    }
  }

  if( rc==SQLITE4_OK ){
    pShared->nRef++;
  }
  btLockMutexLeave(p->pEnv);

  /* Add this connection to the linked list at BtShared.pLock */
  if( rc==SQLITE4_OK ){
    sqlite4_mutex_enter(pShared->pClientMutex);

    /* If this is a multi-process connection and the shared file-handle
    ** has not yet been opened, open it now. Under the cover of the
    ** client-mutex.  */
    if( pShared->pFile==0 && pShared->bMultiProc ){
      rc = pVfs->xOpen(pEnv, pVfs, pShared->zName, 0, &pShared->pFile);
    }

    if( rc==SQLITE4_OK ){
      if( pShared->pBtFile ){
        p->pBtFile = pShared->pBtFile;
        pShared->pBtFile = p->pBtFile->pNext;
        p->pBtFile->pNext = 0;
        p->pFd = p->pBtFile->pFd;
      }else{
        p->pBtFile = (BtFile*)sqlite4_malloc(pEnv, sizeof(BtFile));
        if( p->pBtFile ){
          int flags = BT_OPEN_DATABASE;
          p->pBtFile->pNext = 0;
          rc = pVfs->xOpen(pEnv, pVfs, pShared->zName, flags, &p->pFd);
          p->pBtFile->pFd = p->pFd;
        }else{
          rc = btErrorBkpt(SQLITE4_NOMEM);
        }
      }
    }

    if( rc==SQLITE4_OK ){
      p->pNext = pShared->pLock;
      pShared->pLock = p;

      p->pShared = pShared;
    }
    sqlite4_mutex_leave(pShared->pClientMutex);
    if( rc!=SQLITE4_OK ){
      btLockSharedDeref(pEnv, pVfs, pShared);
    }
  }

  if( rc==SQLITE4_OK ){
    rc = btLockLockop(p, BT_LOCK_DMS1, BT_LOCK_EXCL, 1);
    if( rc==SQLITE4_OK ){
      rc = btLockLockop(p, BT_LOCK_DMS2_RW, BT_LOCK_EXCL, 0);
      if( rc==SQLITE4_OK ){
................................................................................
    }
    if( rc==SQLITE4_BUSY ) rc = SQLITE4_OK;
    btLockLockop(p, BT_LOCK_DMS2_RW, BT_LOCK_UNLOCK, 0);
    btLockLockop(p, BT_LOCK_DMS2_RO, BT_LOCK_UNLOCK, 0);
    btLockLockop(p, BT_LOCK_DMS1, BT_LOCK_UNLOCK, 0);
  }

  sqlite4_mutex_enter(pShared->pClientMutex);
  assert( p->pBtFile->pNext==0 );
  p->pBtFile->pNext = pShared->pBtFile;
  pShared->pBtFile = p->pBtFile;




  sqlite4_mutex_leave(pShared->pClientMutex);







  btLockSharedDeref(p->pEnv, p->pVfs, pShared);
  return rc;
}

#ifndef NDEBUG
static void assertNoLockedSlots(BtLock *pLock){
  u32 mask = (1 << (BT_LOCK_READER0+BT_NREADER+1)) - (1 << BT_LOCK_READER0);
  assert( (pLock->mExclLock & mask)==0 );
................................................................................
  if( (iFirst<aLog[0] || iFirst>aLog[1])
   && (iFirst<aLog[2] || iFirst>aLog[3])
   && (iFirst<aLog[4] || iFirst>aLog[5])
  ){
    iLast = 0;
  }





  if( iLast==0 ){
    rc = btLockLockop(pLock, BT_LOCK_READER_DBONLY, BT_LOCK_SHARED, 0);
  }else{
    const int nMaxRetry = 100;
    int nAttempt = 100;           /* Remaining lock attempts */

    for(nAttempt=0; rc==SQLITE4_BUSY && nAttempt<nMaxRetry; nAttempt++){
................................................................................
      int iIdxLast = sqlite4BtLogFrameToIdx(aLog, iLast);

      assert( iIdxFirst>=0 && iIdxLast>=0 );

      /* Try to find a slot populated with the values required. */
      for(i=0; i<BT_NREADER; i++){
        if( aSlot[i].iFirst==iFirst && aSlot[i].iLast==iLast ){

          break;
        }
      }

      /* Or, if there is no slot with the required values - try to create one */
      if( i==BT_NREADER ){
        for(i=0; i<BT_NREADER; i++){
................................................................................
}

int sqlite4BtLockShmMap(BtLock *pLock, int iChunk, int nByte, u8 **ppOut){
  int rc = SQLITE4_OK;
  BtShared *pShared = pLock->pShared;
  u8 *pOut = 0;

  assert( pShared->bReadonly==0 );

  sqlite4_mutex_enter(pShared->pClientMutex);
  if( pShared->nShmChunk<=iChunk ){
    u8 **apNew;
    int nNew = iChunk+1;
    int nByte = sizeof(u8*)*nNew;

................................................................................
      memset(&apNew[pShared->nShmChunk],0,nByte-sizeof(u8*)*pShared->nShmChunk);
      pShared->nShmChunk = nNew;
      pShared->apShmChunk = apNew;
    }
  }

  if( rc==SQLITE4_OK ){

    assert( (pShared->bMultiProc==0)==(pShared->pFile==0) );
    if( pShared->pFile==0 ){
      /* Single process mode. Allocate memory from the heap. */
      if( pShared->apShmChunk[iChunk]==0 ){
        u8 *p = (u8*)sqlite4_malloc(pLock->pEnv, nByte);
        if( p ){
          memset(p, 0, nByte);
          pShared->apShmChunk[iChunk] = p;
        }else{
          rc = btErrorBkpt(SQLITE4_NOMEM);
        }
      }
    }else{
      /* Multi-process mode. Request shared memory from VFS */
      void *pShm = 0;
      rc = pLock->pVfs->xShmMap(pShared->pFile, iChunk, nByte, &pShm);
      pShared->apShmChunk[iChunk] = (u8*)pShm;


    }

    pOut = pShared->apShmChunk[iChunk];
    assert( pOut || rc!=SQLITE4_OK );
  }
  sqlite4_mutex_leave(pShared->pClientMutex);
  
  *ppOut = pOut;
  return rc;
}

Changes to src/bt_log.c.

921
922
923
924
925
926
927

928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
int sqlite4BtLogOpen(BtPager *pPager, int bRecover, BtLog **ppLog){
  BtLock *pLock = (BtLock*)pPager;
  bt_env *pVfs = pLock->pVfs;
  sqlite4_env *pEnv = pLock->pEnv;
  int rc;                         /* Return code */
  const char *zWal;               /* Name of log file to open */
  BtLog *pLog;                    /* Log handle to return */


  pLog = sqlite4_malloc(pEnv, sizeof(BtLog));
  if( pLog==0 ){
    rc = SQLITE4_NOMEM;
    goto open_out;
  }
  memset(pLog, 0, sizeof(BtLog));
  pLog->pLock = (BtLock*)pPager;
  pLog->nWrapLog = BT_NWRAPLOG;

  zWal = sqlite4BtPagerFilename(pPager, BT_PAGERFILE_LOG);
  rc = pVfs->xOpen(pEnv, pVfs, zWal, 0, &pLog->pFd);

  if( rc==SQLITE4_OK && bRecover ){
    rc = btLogMapShm(pLog, 0);
    if( rc==SQLITE4_OK ){
      BtShm *pShm = btLogShm(pLog);
      memset(pShm, 0, sizeof(BtShm));
      pShm->ckpt.iFirstRead = 1;







>











|







921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
int sqlite4BtLogOpen(BtPager *pPager, int bRecover, BtLog **ppLog){
  BtLock *pLock = (BtLock*)pPager;
  bt_env *pVfs = pLock->pVfs;
  sqlite4_env *pEnv = pLock->pEnv;
  int rc;                         /* Return code */
  const char *zWal;               /* Name of log file to open */
  BtLog *pLog;                    /* Log handle to return */
  int flags = BT_OPEN_LOG;

  pLog = sqlite4_malloc(pEnv, sizeof(BtLog));
  if( pLog==0 ){
    rc = SQLITE4_NOMEM;
    goto open_out;
  }
  memset(pLog, 0, sizeof(BtLog));
  pLog->pLock = (BtLock*)pPager;
  pLog->nWrapLog = BT_NWRAPLOG;

  zWal = sqlite4BtPagerFilename(pPager, BT_PAGERFILE_LOG);
  rc = pVfs->xOpen(pEnv, pVfs, zWal, flags, &pLog->pFd);

  if( rc==SQLITE4_OK && bRecover ){
    rc = btLogMapShm(pLog, 0);
    if( rc==SQLITE4_OK ){
      BtShm *pShm = btLogShm(pLog);
      memset(pShm, 0, sizeof(BtShm));
      pShm->ckpt.iFirstRead = 1;

Changes to src/bt_main.c.

2398
2399
2400
2401
2402
2403
2404






2405
2406
2407
2408
2409
    }

    case BT_CONTROL_AUTOCKPT: {
      int *pInt = (int*)pArg;
      sqlite4BtPagerSetAutockpt(db->pPager, pInt);
      break;
    }






  }

  return rc;
}








>
>
>
>
>
>





2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
    }

    case BT_CONTROL_AUTOCKPT: {
      int *pInt = (int*)pArg;
      sqlite4BtPagerSetAutockpt(db->pPager, pInt);
      break;
    }

    case BT_CONTROL_LOGSIZE: {
      int *pInt = (int*)pArg;
      sqlite4BtPagerLogsize(db->pPager, pInt);
      break;
    }
  }

  return rc;
}

Changes to src/bt_pager.c.

364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
...
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
...
928
929
930
931
932
933
934




935
936
937
938
939
940
941
** Close a pager database handle.
*/
int sqlite4BtPagerClose(BtPager *p){
  int rc;

  rc = sqlite4BtLockDisconnect((BtLock*)p, btCheckpoint, btCleanup);

  if( p->btl.pFd ){
    p->btl.pVfs->xClose(p->btl.pFd);
  }

  p->iTransactionLevel = 0;
  btCloseSavepoints(p, 0);
  btPurgeCache(p);
  sqlite4BtLogClose(p->pLog, 0);
  sqlite4_free(p->btl.pEnv, p->zFile);
  sqlite4_free(p->btl.pEnv, p->aSavepoint);
  sqlite4_free(p->btl.pEnv, p);
................................................................................
** fails, the BtPager is rendered unusable (and must be closed by the
** caller using BtPagerClose()).
**
** If successful, SQLITE4_OK is returned. Otherwise, an SQLite error code.
*/
int sqlite4BtPagerOpen(BtPager *p, const char *zFilename){
  int rc;                         /* Return code */
  int flags = 0;                  /* Flags to pass to xOpen() */
  sqlite4_env *pEnv = p->btl.pEnv;
  bt_env *pVfs = p->btl.pVfs;

  assert( p->btl.pFd==0 && p->zFile==0 );

  rc = pVfs->xFullpath(pEnv, pVfs, zFilename, &p->zFile);
  if( rc==SQLITE4_OK ){
    p->nFile = strlen(p->zFile);
    rc = pVfs->xOpen(pEnv, pVfs, zFilename, flags, &p->btl.pFd);
  }

  /* If the database file was successfully opened, connect to the
  ** database and open the database logger. */
  if( rc==SQLITE4_OK ){
    rc = sqlite4BtLockConnect((BtLock*)p, btRecover);
    if( rc==SQLITE4_OK && p->pLog==0 ){
      rc = sqlite4BtLogOpen(p, 0, &p->pLog);
    }
  }

  assert( (rc==SQLITE4_OK)==(p->btl.pFd!=0 && p->pLog!=0) );
................................................................................
void sqlite4BtPagerSetAutockpt(BtPager *pPager, int *piVal){
  int iVal = *piVal;
  if( iVal>=0 ){
    pPager->btl.nAutoCkpt = iVal;
  }
  *piVal = pPager->btl.nAutoCkpt;
}





#ifndef NDEBUG
int sqlite4BtPagerRefcount(BtPager *p){
  return p->nTotalRef;
}
#endif








<
<
<
<







 







<








<
<
<
<
<
<







 







>
>
>
>







364
365
366
367
368
369
370




371
372
373
374
375
376
377
...
403
404
405
406
407
408
409

410
411
412
413
414
415
416
417






418
419
420
421
422
423
424
...
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
** Close a pager database handle.
*/
int sqlite4BtPagerClose(BtPager *p){
  int rc;

  rc = sqlite4BtLockDisconnect((BtLock*)p, btCheckpoint, btCleanup);





  p->iTransactionLevel = 0;
  btCloseSavepoints(p, 0);
  btPurgeCache(p);
  sqlite4BtLogClose(p->pLog, 0);
  sqlite4_free(p->btl.pEnv, p->zFile);
  sqlite4_free(p->btl.pEnv, p->aSavepoint);
  sqlite4_free(p->btl.pEnv, p);
................................................................................
** fails, the BtPager is rendered unusable (and must be closed by the
** caller using BtPagerClose()).
**
** If successful, SQLITE4_OK is returned. Otherwise, an SQLite error code.
*/
int sqlite4BtPagerOpen(BtPager *p, const char *zFilename){
  int rc;                         /* Return code */

  sqlite4_env *pEnv = p->btl.pEnv;
  bt_env *pVfs = p->btl.pVfs;

  assert( p->btl.pFd==0 && p->zFile==0 );

  rc = pVfs->xFullpath(pEnv, pVfs, zFilename, &p->zFile);
  if( rc==SQLITE4_OK ){
    p->nFile = strlen(p->zFile);






    rc = sqlite4BtLockConnect((BtLock*)p, btRecover);
    if( rc==SQLITE4_OK && p->pLog==0 ){
      rc = sqlite4BtLogOpen(p, 0, &p->pLog);
    }
  }

  assert( (rc==SQLITE4_OK)==(p->btl.pFd!=0 && p->pLog!=0) );
................................................................................
void sqlite4BtPagerSetAutockpt(BtPager *pPager, int *piVal){
  int iVal = *piVal;
  if( iVal>=0 ){
    pPager->btl.nAutoCkpt = iVal;
  }
  *piVal = pPager->btl.nAutoCkpt;
}

void sqlite4BtPagerLogsize(BtPager *pPager, int *pnFrame){
  *pnFrame = sqlite4BtLogSize(pPager->pLog);
}

#ifndef NDEBUG
int sqlite4BtPagerRefcount(BtPager *p){
  return p->nTotalRef;
}
#endif

Changes to src/bt_unix.c.

255
256
257
258
259
260
261


262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
...
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  return rc;
}

static int btPosixOsUnlink(sqlite4_env *pEnv, bt_env *pVfs, const char *zFile){
  int prc = unlink(zFile);
  return prc ? btErrorBkpt(SQLITE4_IOERR) : SQLITE4_OK;
}



int btPosixOsLock(bt_file *pFile, int iLock, int eType){
  int rc = SQLITE4_OK;
  PosixFile *p = (PosixFile *)pFile;
  static const short aType[3] = { F_UNLCK, F_RDLCK, F_WRLCK };
  struct flock lock;

  assert( aType[BT_LOCK_UNLOCK]==F_UNLCK );
  assert( aType[BT_LOCK_SHARED]==F_RDLCK );
  assert( aType[BT_LOCK_EXCL]==F_WRLCK );
  assert( eType>=0 && eType<(sizeof(aType)/sizeof(aType[0])) );
  assert( iLock>0 && iLock<=32 );

  memset(&lock, 0, sizeof(lock));
  lock.l_whence = SEEK_SET;
  lock.l_len = 1;
  lock.l_type = aType[eType];
  lock.l_start = (4096-iLock);

  if( fcntl(p->fd, F_SETLK, &lock) ){
    int e = errno;
    if( e==EACCES || e==EAGAIN ){
      rc = SQLITE4_BUSY;
    }else{
      rc = btErrorBkpt(SQLITE4_IOERR);
................................................................................
  static const short aType[3] = { 0, F_RDLCK, F_WRLCK };
  struct flock lock;

  assert( eType==BT_LOCK_SHARED || eType==BT_LOCK_EXCL );
  assert( aType[BT_LOCK_SHARED]==F_RDLCK );
  assert( aType[BT_LOCK_EXCL]==F_WRLCK );
  assert( eType>=0 && eType<(sizeof(aType)/sizeof(aType[0])) );
  assert( iLock>0 && iLock<=32 );

  memset(&lock, 0, sizeof(lock));
  lock.l_whence = SEEK_SET;
  lock.l_len = nLock;
  lock.l_type = aType[eType];
  lock.l_start = (4096-iLock);

  if( fcntl(p->fd, F_GETLK, &lock) ){
    rc = btErrorBkpt(SQLITE4_IOERR);
  }else if( lock.l_type!=F_UNLCK ){
    rc = SQLITE4_BUSY;
  }








>
>











|





|







 







|





|







255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
...
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
  return rc;
}

static int btPosixOsUnlink(sqlite4_env *pEnv, bt_env *pVfs, const char *zFile){
  int prc = unlink(zFile);
  return prc ? btErrorBkpt(SQLITE4_IOERR) : SQLITE4_OK;
}

#define btPosixLockToByte(iLock) (100 + (iLock))

int btPosixOsLock(bt_file *pFile, int iLock, int eType){
  int rc = SQLITE4_OK;
  PosixFile *p = (PosixFile *)pFile;
  static const short aType[3] = { F_UNLCK, F_RDLCK, F_WRLCK };
  struct flock lock;

  assert( aType[BT_LOCK_UNLOCK]==F_UNLCK );
  assert( aType[BT_LOCK_SHARED]==F_RDLCK );
  assert( aType[BT_LOCK_EXCL]==F_WRLCK );
  assert( eType>=0 && eType<(sizeof(aType)/sizeof(aType[0])) );
  assert( iLock>=0 && iLock<=32 );

  memset(&lock, 0, sizeof(lock));
  lock.l_whence = SEEK_SET;
  lock.l_len = 1;
  lock.l_type = aType[eType];
  lock.l_start = btPosixLockToByte(iLock);

  if( fcntl(p->fd, F_SETLK, &lock) ){
    int e = errno;
    if( e==EACCES || e==EAGAIN ){
      rc = SQLITE4_BUSY;
    }else{
      rc = btErrorBkpt(SQLITE4_IOERR);
................................................................................
  static const short aType[3] = { 0, F_RDLCK, F_WRLCK };
  struct flock lock;

  assert( eType==BT_LOCK_SHARED || eType==BT_LOCK_EXCL );
  assert( aType[BT_LOCK_SHARED]==F_RDLCK );
  assert( aType[BT_LOCK_EXCL]==F_WRLCK );
  assert( eType>=0 && eType<(sizeof(aType)/sizeof(aType[0])) );
  assert( iLock>=0 && iLock<=32 );

  memset(&lock, 0, sizeof(lock));
  lock.l_whence = SEEK_SET;
  lock.l_len = nLock;
  lock.l_type = aType[eType];
  lock.l_start = btPosixLockToByte(iLock);

  if( fcntl(p->fd, F_GETLK, &lock) ){
    rc = btErrorBkpt(SQLITE4_IOERR);
  }else if( lock.l_type!=F_UNLCK ){
    rc = SQLITE4_BUSY;
  }