SQLite

Check-in [45df27a22d]
Login

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

Overview
Comment:Fix thread related problems in test modules test_async.c and test_journal.c. (CVS 6399)
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 45df27a22d283871ed1de334fe3b74b0121d57a6
User & Date: danielk1977 2009-03-28 17:21:52.000
Context
2009-03-28
18:56
The test_async.c module must pass an unchanging filename to the underlying VFS. (CVS 6400) (check-in: d1eeee2167 user: drh tags: trunk)
17:21
Fix thread related problems in test modules test_async.c and test_journal.c. (CVS 6399) (check-in: 45df27a22d user: danielk1977 tags: trunk)
15:04
Back out check-in (6380). Replace it with a proper fix to the xFullPathname method in the async VFS. (CVS 6398) (check-in: 767a7f7b55 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/test_async.c.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
** 2005 December 14
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
**
** $Id: test_async.c,v 1.54 2009/03/28 15:04:24 drh Exp $
**
** This file contains an example implementation of an asynchronous IO 
** backend for SQLite.
**
** WHAT IS ASYNCHRONOUS I/O?
**
** With asynchronous I/O, write requests are handled by a separate thread












|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
** 2005 December 14
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
**
** $Id: test_async.c,v 1.55 2009/03/28 17:21:52 danielk1977 Exp $
**
** This file contains an example implementation of an asynchronous IO 
** backend for SQLite.
**
** WHAT IS ASYNCHRONOUS I/O?
**
** With asynchronous I/O, write requests are handled by a separate thread
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
  assert(&(aMutex[1])==&async.queueMutex);
  assert(&(aMutex[2])==&async.writerMutex);
  assert(&(aHolder[0])==&asyncdebug.lockMutexHolder);
  assert(&(aHolder[1])==&asyncdebug.queueMutexHolder);
  assert(&(aHolder[2])==&asyncdebug.writerMutexHolder);

  assert( pthread_self()!=0 );

  for(iIdx=0; iIdx<3; iIdx++){
    if( pMutex==&aMutex[iIdx] ) break;

    /* This is the key assert(). Here we are checking that if the caller
     * is trying to block on async.writerMutex, neither of the other two
     * mutex are held. If the caller is trying to block on async.queueMutex,
     * lockMutex is not held.







<







479
480
481
482
483
484
485

486
487
488
489
490
491
492
  assert(&(aMutex[1])==&async.queueMutex);
  assert(&(aMutex[2])==&async.writerMutex);
  assert(&(aHolder[0])==&asyncdebug.lockMutexHolder);
  assert(&(aHolder[1])==&asyncdebug.queueMutexHolder);
  assert(&(aHolder[2])==&asyncdebug.writerMutexHolder);

  assert( pthread_self()!=0 );

  for(iIdx=0; iIdx<3; iIdx++){
    if( pMutex==&aMutex[iIdx] ) break;

    /* This is the key assert(). Here we are checking that if the caller
     * is trying to block on async.writerMutex, neither of the other two
     * mutex are held. If the caller is trying to block on async.queueMutex,
     * lockMutex is not held.
606
607
608
609
610
611
612

613

614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631

632

633
634
635
636
637
638
639
**
** Once an AsyncWrite structure has been added to the list, it becomes the
** property of the writer thread and must not be read or modified by the
** caller.  
*/
static void addAsyncWrite(AsyncWrite *pWrite){
  /* We must hold the queue mutex in order to modify the queue pointers */

  pthread_mutex_lock(&async.queueMutex);


  /* Add the record to the end of the write-op queue */
  assert( !pWrite->pNext );
  if( async.pQueueLast ){
    assert( async.pQueueFirst );
    async.pQueueLast->pNext = pWrite;
  }else{
    async.pQueueFirst = pWrite;
  }
  async.pQueueLast = pWrite;
  ASYNC_TRACE(("PUSH %p (%s %s %d)\n", pWrite, azOpcodeName[pWrite->op],
         pWrite->pFileData ? pWrite->pFileData->zName : "-", pWrite->iOffset));

  if( pWrite->op==ASYNC_CLOSE ){
    async.nFile--;
  }

  /* Drop the queue mutex */

  pthread_mutex_unlock(&async.queueMutex);


  /* The writer thread might have been idle because there was nothing
  ** on the write-op queue for it to do.  So wake it up. */
  pthread_cond_signal(&async.queueSignal);
}

/*







>
|
>


















>
|
>







605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
**
** Once an AsyncWrite structure has been added to the list, it becomes the
** property of the writer thread and must not be read or modified by the
** caller.  
*/
static void addAsyncWrite(AsyncWrite *pWrite){
  /* We must hold the queue mutex in order to modify the queue pointers */
  if( pWrite->op!=ASYNC_UNLOCK ){
    pthread_mutex_lock(&async.queueMutex);
  }

  /* Add the record to the end of the write-op queue */
  assert( !pWrite->pNext );
  if( async.pQueueLast ){
    assert( async.pQueueFirst );
    async.pQueueLast->pNext = pWrite;
  }else{
    async.pQueueFirst = pWrite;
  }
  async.pQueueLast = pWrite;
  ASYNC_TRACE(("PUSH %p (%s %s %d)\n", pWrite, azOpcodeName[pWrite->op],
         pWrite->pFileData ? pWrite->pFileData->zName : "-", pWrite->iOffset));

  if( pWrite->op==ASYNC_CLOSE ){
    async.nFile--;
  }

  /* Drop the queue mutex */
  if( pWrite->op!=ASYNC_UNLOCK ){
    pthread_mutex_unlock(&async.queueMutex);
  }

  /* The writer thread might have been idle because there was nothing
  ** on the write-op queue for it to do.  So wake it up. */
  pthread_cond_signal(&async.queueSignal);
}

/*
952
953
954
955
956
957
958

959
960
961
962


963
964
965
966
967
968
969
  return rc;
}
static int asyncUnlock(sqlite3_file *pFile, int eLock){
  int rc = SQLITE_OK;
  AsyncFileData *p = ((AsyncFile *)pFile)->pData;
  if( p->zName ){
    AsyncFileLock *pLock = &p->lock;

    pthread_mutex_lock(&async.lockMutex);
    pLock->eLock = MIN(pLock->eLock, eLock);
    pthread_mutex_unlock(&async.lockMutex);
    rc = addNewAsyncWrite(p, ASYNC_UNLOCK, 0, eLock, 0);


  }
  return rc;
}

/*
** This function is called when the pager layer first opens a database file
** and is checking for a hot-journal.







>


<

>
>







955
956
957
958
959
960
961
962
963
964

965
966
967
968
969
970
971
972
973
974
  return rc;
}
static int asyncUnlock(sqlite3_file *pFile, int eLock){
  int rc = SQLITE_OK;
  AsyncFileData *p = ((AsyncFile *)pFile)->pData;
  if( p->zName ){
    AsyncFileLock *pLock = &p->lock;
    pthread_mutex_lock(&async.queueMutex);
    pthread_mutex_lock(&async.lockMutex);
    pLock->eLock = MIN(pLock->eLock, eLock);

    rc = addNewAsyncWrite(p, ASYNC_UNLOCK, 0, eLock, 0);
    pthread_mutex_unlock(&async.lockMutex);
    pthread_mutex_unlock(&async.queueMutex);
  }
  return rc;
}

/*
** This function is called when the pager layer first opens a database file
** and is checking for a hot-journal.
1522
1523
1524
1525
1526
1527
1528

1529
1530





































1531
1532
1533
1534
1535
1536
1537

1538
1539
1540
1541
1542
1543
1544
        async.pQueueFirst = p->pNext;
        sqlite3_free(pData);
        doNotFree = 1;
        break;
      }

      case ASYNC_UNLOCK: {

        AsyncFileData *pData = p->pFileData;
        int eLock = p->nByte;





































        pthread_mutex_lock(&async.lockMutex);
        pData->lock.eAsyncLock = MIN(
            pData->lock.eAsyncLock, MAX(pData->lock.eLock, eLock)
        );
        assert(pData->lock.eAsyncLock>=pData->lock.eLock);
        rc = getFileLock(pData->pLock);
        pthread_mutex_unlock(&async.lockMutex);

        break;
      }

      case ASYNC_DELETE:
        ASYNC_TRACE(("DELETE %s\n", p->zBuf));
        rc = pVfs->xDelete(pVfs, p->zBuf, (int)p->iOffset);
        break;







>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
|
|
>







1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
        async.pQueueFirst = p->pNext;
        sqlite3_free(pData);
        doNotFree = 1;
        break;
      }

      case ASYNC_UNLOCK: {
        AsyncWrite *pIter;
        AsyncFileData *pData = p->pFileData;
        int eLock = p->nByte;

        /* When a file is locked by SQLite using the async backend, it is 
        ** locked within the 'real' file-system synchronously. When it is
        ** unlocked, an ASYNC_UNLOCK event is added to the write-queue to
        ** unlock the file asynchronously. The design of the async backend
        ** requires that the 'real' file-system file be locked from the
        ** time that SQLite first locks it (and probably reads from it)
        ** until all asynchronous write events that were scheduled before
        ** SQLite unlocked the file have been processed.
        **
        ** This is more complex if SQLite locks and unlocks the file multiple
        ** times in quick succession. For example, if SQLite does: 
        ** 
        **   lock, write, unlock, lock, write, unlock
        **
        ** Each "lock" operation locks the file immediately. Each "write" 
        ** and "unlock" operation adds an event to the event queue. If the
        ** second "lock" operation is performed before the first "unlock"
        ** operation has been processed asynchronously, then the first
        ** "unlock" cannot be safely processed as is, since this would mean
        ** the file was unlocked when the second "write" operation is
        ** processed. To work around this, when processing an ASYNC_UNLOCK
        ** operation, SQLite:
        **
        **   1) Unlocks the file to the minimum of the argument passed to
        **      the xUnlock() call and the current lock from SQLite's point
        **      of view, and
        **
        **   2) Only unlocks the file at all if this event is the last
        **      ASYNC_UNLOCK event on this file in the write-queue.
        */ 
        assert( holdingMutex==1 );
        assert( async.pQueueFirst==p );
        for(pIter=async.pQueueFirst->pNext; pIter; pIter=pIter->pNext){
          if( pIter->pFileData==pData && pIter->op==ASYNC_UNLOCK ) break;
        }
        if( !pIter ){
          pthread_mutex_lock(&async.lockMutex);
          pData->lock.eAsyncLock = MIN(
              pData->lock.eAsyncLock, MAX(pData->lock.eLock, eLock)
          );
          assert(pData->lock.eAsyncLock>=pData->lock.eLock);
          rc = getFileLock(pData->pLock);
          pthread_mutex_unlock(&async.lockMutex);
        }
        break;
      }

      case ASYNC_DELETE:
        ASYNC_TRACE(("DELETE %s\n", p->zBuf));
        rc = pVfs->xDelete(pVfs, p->zBuf, (int)p->iOffset);
        break;
Changes to src/test_journal.c.
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
******************************************************************************
**
** This file contains code for a VFS layer that acts as a wrapper around
** an existing VFS. The code in this file attempts to verify that SQLite
** correctly populates and syncs a journal file before writing to a
** corresponding database file.
**
** $Id: test_journal.c,v 1.13 2009/03/26 11:49:11 danielk1977 Exp $
*/
#if SQLITE_TEST          /* This file is used for testing only */

#include "sqlite3.h"
#include "sqliteInt.h"

/*







|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
******************************************************************************
**
** This file contains code for a VFS layer that acts as a wrapper around
** an existing VFS. The code in this file attempts to verify that SQLite
** correctly populates and syncs a journal file before writing to a
** corresponding database file.
**
** $Id: test_journal.c,v 1.14 2009/03/28 17:21:52 danielk1977 Exp $
*/
#if SQLITE_TEST          /* This file is used for testing only */

#include "sqlite3.h"
#include "sqliteInt.h"

/*
201
202
203
204
205
206
207











208
209
210
211
212
213
214
};

struct JtGlobal {
  sqlite3_vfs *pVfs;             /* Parent VFS */
  jt_file *pList;                /* List of all open files */
};
static struct JtGlobal g = {0, 0};












extern int sqlite3_io_error_pending;
static void stop_ioerr_simulation(int *piSave){
  *piSave = sqlite3_io_error_pending;
  sqlite3_io_error_pending = -1;
}
static void start_ioerr_simulation(int iSave){







>
>
>
>
>
>
>
>
>
>
>







201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
};

struct JtGlobal {
  sqlite3_vfs *pVfs;             /* Parent VFS */
  jt_file *pList;                /* List of all open files */
};
static struct JtGlobal g = {0, 0};

/*
** Functions to obtain and relinquish a mutex to protect g.pList. The
** STATIC_PRNG mutex is reused, purely for the sake of convenience.
*/
static void enterJtMutex(void){
  sqlite3_mutex_enter(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PRNG));
}
static void leaveJtMutex(void){
  sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PRNG));
}

extern int sqlite3_io_error_pending;
static void stop_ioerr_simulation(int *piSave){
  *piSave = sqlite3_io_error_pending;
  sqlite3_io_error_pending = -1;
}
static void start_ioerr_simulation(int iSave){
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
279
280
281
282
283
284
285
286

287
288
289
290
291
292
293
** Close an jt-file.
*/
static int jtClose(sqlite3_file *pFile){
  jt_file **pp;
  jt_file *p = (jt_file *)pFile;

  closeTransaction(p);

  if( p->zName ){
    for(pp=&g.pList; *pp!=p; pp=&(*pp)->pNext);
    *pp = p->pNext;
  }

  return sqlite3OsClose(p->pReal);
}

/*
** Read data from an jt-file.
*/
static int jtRead(
  sqlite3_file *pFile, 
  void *zBuf, 
  int iAmt, 
  sqlite_int64 iOfst
){
  jt_file *p = (jt_file *)pFile;
  return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
}


/*
** Parameter zJournal is the name of a journal file that is currently 
** open. This function locates and returns the handle opened on the
** corresponding database file by the pager that currently has the
** journal file opened. This file-handle is identified by the 
** following properties:
**
**   a) SQLITE_OPEN_MAIN_DB was specified when the file was opened.
**
**   b) The file-name specified when the file was opened matches
**      all but the final 8 characters of the journal file name.
**
**   c) There is currently a reserved lock on the file.
**/
static jt_file *locateDatabaseHandle(const char *zJournal){
  jt_file *pMain = 0;

  for(pMain=g.pList; pMain; pMain=pMain->pNext){
    int nName = strlen(zJournal) - strlen("-journal");
    if( (pMain->flags&SQLITE_OPEN_MAIN_DB)
     && (strlen(pMain->zName)==nName)
     && 0==memcmp(pMain->zName, zJournal, nName)
     && (pMain->eLock>=SQLITE_LOCK_RESERVED)
    ){
      break;
    }
  }

  return pMain;
}

/*
** Parameter z points to a buffer of 4 bytes in size containing a 
** unsigned 32-bit integer stored in big-endian format. Decode the 
** integer and return its value.







>




|















<

















>










>







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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
** Close an jt-file.
*/
static int jtClose(sqlite3_file *pFile){
  jt_file **pp;
  jt_file *p = (jt_file *)pFile;

  closeTransaction(p);
  enterJtMutex();
  if( p->zName ){
    for(pp=&g.pList; *pp!=p; pp=&(*pp)->pNext);
    *pp = p->pNext;
  }
  leaveJtMutex();
  return sqlite3OsClose(p->pReal);
}

/*
** Read data from an jt-file.
*/
static int jtRead(
  sqlite3_file *pFile, 
  void *zBuf, 
  int iAmt, 
  sqlite_int64 iOfst
){
  jt_file *p = (jt_file *)pFile;
  return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
}


/*
** Parameter zJournal is the name of a journal file that is currently 
** open. This function locates and returns the handle opened on the
** corresponding database file by the pager that currently has the
** journal file opened. This file-handle is identified by the 
** following properties:
**
**   a) SQLITE_OPEN_MAIN_DB was specified when the file was opened.
**
**   b) The file-name specified when the file was opened matches
**      all but the final 8 characters of the journal file name.
**
**   c) There is currently a reserved lock on the file.
**/
static jt_file *locateDatabaseHandle(const char *zJournal){
  jt_file *pMain = 0;
  enterJtMutex();
  for(pMain=g.pList; pMain; pMain=pMain->pNext){
    int nName = strlen(zJournal) - strlen("-journal");
    if( (pMain->flags&SQLITE_OPEN_MAIN_DB)
     && (strlen(pMain->zName)==nName)
     && 0==memcmp(pMain->zName, zJournal, nName)
     && (pMain->eLock>=SQLITE_LOCK_RESERVED)
    ){
      break;
    }
  }
  leaveJtMutex();
  return pMain;
}

/*
** Parameter z points to a buffer of 4 bytes in size containing a 
** unsigned 32-bit integer stored in big-endian format. Decode the 
** integer and return its value.
665
666
667
668
669
670
671

672
673
674
675

676
677
678
679
680
681
682
    pFile->pMethods = &jt_io_methods;
    p->eLock = 0;
    p->zName = zName;
    p->flags = flags;
    p->pNext = 0;
    p->pWritable = 0;
    p->aCksum = 0;

    if( zName ){
      p->pNext = g.pList;
      g.pList = p;
    }

  }
  return rc;
}

/*
** Delete the file located at zPath. If the dirSync argument is true,
** ensure the file-system modifications are synced to disk before







>




>







678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
    pFile->pMethods = &jt_io_methods;
    p->eLock = 0;
    p->zName = zName;
    p->flags = flags;
    p->pNext = 0;
    p->pWritable = 0;
    p->aCksum = 0;
    enterJtMutex();
    if( zName ){
      p->pNext = g.pList;
      g.pList = p;
    }
    leaveJtMutex();
  }
  return rc;
}

/*
** Delete the file located at zPath. If the dirSync argument is true,
** ensure the file-system modifications are synced to disk before