/ Check-in [3794dcd3]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Add external locking to test_async.c. There are still some tests to come. (CVS 4398)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 3794dcd31a74e90b181b336bf6a4c917bda526b8
User & Date: danielk1977 2007-09-04 18:28:44
Context
2007-09-04
22:31
Do not use the TryEnterCriticalSection API on windows since it is unavailable on some platforms. (CVS 4399) check-in: bf3d67d1 user: drh tags: trunk
18:28
Add external locking to test_async.c. There are still some tests to come. (CVS 4398) check-in: 3794dcd3 user: danielk1977 tags: trunk
15:38
Fix a problem whereby the *ppVtab output buffer passed to sqlite3_module.xConstruct() could be invalidated (freed) if a malloc() failure occured within a call to sqlite3_declare_vtab(). (CVS 4397) check-in: efd61df1 user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/test_async.c.

66
67
68
69
70
71
72







73
74
75
76
77
78
79
80

81
82
83
84
85
86
87
..
91
92
93
94
95
96
97


98
99
100
101
102
103
104
...
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
...
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
185
...
218
219
220
221
222
223
224
225


226
227
228
229
230
231
232
...
250
251
252
253
254
255
256

257
258
259
260
261
262
263
264
265
266
267
268
269
...
291
292
293
294
295
296
297



298
299
300
301
302
303
304
...
312
313
314
315
316
317
318
319
320
321
322

323
324





325

326
327
328
329
330
331
332
333
334
...
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
...
473
474
475
476
477
478
479



480
481
482
483
484
485

486
487
488
489
490
491
492
493
494
495
496
497
...
587
588
589
590
591
592
593




























594
595
596
597
598
599
600
...
603
604
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
643
644
645
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
...
709
710
711
712
713
714
715


716
717
718
719
720
721
722
...
734
735
736
737
738
739
740


























741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756


757
758
759
760
761
762
763
764
765

766
767
768
769
770
771
772
....
1055
1056
1057
1058
1059
1060
1061

1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081



1082
1083
1084
1085
1086
1087



















1088
1089
1090
1091
1092
1093
1094
** stream that exceeds the I/O capability of the background writer thread,
** the queue of pending write operations will grow without bound until we
** run out of memory.  Users of this technique may want to keep track of
** the quantity of pending writes and stop accepting new write requests
** when the buffer gets to be too big.
*/








#include "sqliteInt.h"
#include <tcl.h>

/*
** This test uses pthreads and hence only works on unix and with
** a threadsafe build of SQLite.
*/
#if OS_UNIX && SQLITE_THREADSAFE


/*
** This demo uses pthreads.  If you do not have a pthreads implementation
** for your operating system, you will need to recode the threading 
** logic.
*/
#include <pthread.h>
................................................................................
#define MIN(x,y) ((x)<(y)?(x):(y))
#define MAX(x,y) ((x)>(y)?(x):(y))

/* Forward references */
typedef struct AsyncWrite AsyncWrite;
typedef struct AsyncFile AsyncFile;
typedef struct AsyncFileData AsyncFileData;



/* Enable for debugging */
static int sqlite3async_trace = 0;
# define ASYNC_TRACE(X) if( sqlite3async_trace ) asyncTrace X
static void asyncTrace(const char *zFormat, ...){
  char *z;
  va_list ap;
................................................................................
**     xOpenXXX (three versions)
**     xDelete
**     xFileExists
**     xSyncDirectory
**
** File handle operations (invoked by SQLite thread):
**
**         asyncWrite, asyncClose, asyncTruncate, asyncSync, 
**         asyncSetFullSync, asyncOpenDirectory.
**    
**     The operations above add an entry to the global write-op list. They
**     prepare the entry, acquire the async.queueMutex momentarily while
**     list pointers are  manipulated to insert the new entry, then release
**     the mutex and signal the writer thread to wake up in case it happens
**     to be asleep.
**
................................................................................
**    
**     These primitives implement in-process locking using a hash table
**     on the file name.  Files are locked correctly for connections coming
**     from the same process.  But other processes cannot see these locks
**     and will therefore not honor them.
**
**
**         asyncFileHandle.
**    
**     The sqlite3OsFileHandle() function is currently only used when 
**     debugging the pager module. Unless sqlite3OsClose() is called on the
**     file (shouldn't be possible for other reasons), the underlying 
**     implementations are safe to call without grabbing any mutex. So we just
**     go ahead and call it no matter what any other threads are doing.
**
**    
**         asyncSeek.
**
**     Calling this method just manipulates the AsyncFile.iOffset variable. 
**     Since this variable is never accessed by writer thread, this
**     function does not require the mutex.  Actual calls to OsSeek() take 
**     place just before OsWrite() or OsRead(), which are always protected by 
**     the mutex.
**
** The writer thread:
**
**     The async.writerMutex is used to make sure only there is only
**     a single writer thread running at a time.
**
**     Inside the writer thread is a loop that works like this:
**
................................................................................
#ifndef SQLITE_ASYNC_TWO_FILEHANDLES
/* #define SQLITE_ASYNC_TWO_FILEHANDLES 0 */
#define SQLITE_ASYNC_TWO_FILEHANDLES 1
#endif

/*
** State information is held in the static variable "async" defined
** as follows:


*/
static struct TestAsyncStaticData {
  pthread_mutex_t queueMutex;  /* Mutex for access to write operation queue */
  pthread_mutex_t writerMutex; /* Prevents multiple writer threads */
  pthread_mutex_t lockMutex;   /* For access to aLock hash table */
  pthread_cond_t queueSignal;  /* For waking up sleeping writer thread */
  pthread_cond_t emptySignal;  /* Notify when the write queue is empty */
................................................................................
#define ASYNC_NOOP          0
#define ASYNC_WRITE         1
#define ASYNC_SYNC          2
#define ASYNC_TRUNCATE      3
#define ASYNC_CLOSE         4
#define ASYNC_DELETE        5
#define ASYNC_OPENEXCLUSIVE 6


/* Names of opcodes.  Used for debugging only.
** Make sure these stay in sync with the macros above!
*/
static const char *azOpcodeName[] = {
  "NOOP", "WRITE", "SYNC", "TRUNCATE", "CLOSE", "DELETE", "OPENEX"
};

/*
** Entries on the write-op queue are instances of the AsyncWrite
** structure, defined here.
**
** The interpretation of the iOffset and nByte variables varies depending 
................................................................................
**     iOffset -> Contains the "syncDir" flag.
**     nByte   -> Number of bytes of zBuf points to (file name).
**
** ASYNC_OPENEXCLUSIVE:
**     iOffset -> Value of "delflag".
**     nByte   -> Number of bytes of zBuf points to (file name).
**



**
** For an ASYNC_WRITE operation, zBuf points to the data to write to the file. 
** This space is sqlite3_malloc()d along with the AsyncWrite structure in a
** single blob, so is deleted when sqlite3_free() is called on the parent 
** structure.
*/
struct AsyncWrite {
................................................................................

/*
** An instance of the following structure is allocated along with each
** AsyncFileData structure (see AsyncFileData.lock), but is only used if the
** file was opened with the SQLITE_OPEN_MAIN_DB.
**
** The global async.aLock[] hash table maps from database file-name to a
** linked-list of AsyncLock structures corresponding to handles opened on the
** file. The AsyncLock structures are linked into the list when the file is
** opened and removed when it is closed. Mutex async.lockMutex must be held
** before accessing any AsyncLock structure or the async.aLock[] table.

*/
typedef struct AsyncLock AsyncLock;





struct AsyncLock {

  int eLock;
  AsyncLock *pNext;
};

/* 
** The AsyncFile structure is a subclass of sqlite3_file used for 
** asynchronous IO. 
**
** All of the actual data for the structure is stored in the structure
................................................................................
  AsyncFileData *pData;
};
struct AsyncFileData {
  char *zName;               /* Underlying OS filename - used for debugging */
  int nName;                 /* Number of characters in zName */
  sqlite3_file *pBaseRead;   /* Read handle to the underlying Os file */
  sqlite3_file *pBaseWrite;  /* Write handle to the underlying Os file */
  AsyncLock lock;
};

/*
** Add an entry to the end of the global write-op list. pWrite should point 
** to an AsyncWrite structure allocated using sqlite3_malloc().  The writer
** thread will call sqlite3_free() to free the structure after the specified
** operation has been completed.
................................................................................
*/
static int asyncRead(sqlite3_file *pFile, void *zOut, int iAmt, i64 iOffset){
  AsyncFileData *p = ((AsyncFile *)pFile)->pData;
  int rc = SQLITE_OK;
  i64 filesize;
  int nRead;
  sqlite3_file *pBase = p->pBaseRead;




  /* If an I/O error has previously occurred in this virtual file 
  ** system, then all subsequent operations fail.
  */
  if( async.ioError!=SQLITE_OK ){
    return async.ioError;

  }

  /* Grab the write queue mutex for the duration of the call */
  pthread_mutex_lock(&async.queueMutex);

  if( pBase->pMethods ){
    rc = sqlite3OsFileSize(pBase, &filesize);
    if( rc!=SQLITE_OK ){
      goto asyncread_out;
    }
    nRead = MIN(filesize - iOffset, iAmt);
    if( nRead>0 ){
................................................................................
      }
    }
    *piSize = s;
  }
  pthread_mutex_unlock(&async.queueMutex);
  return rc;
}





























/*
** No disk locking is performed.  We keep track of locks locally in
** the async.aLock hash table.  Locking should appear to work the same
** as with standard (unmodified) SQLite as long as all connections 
** come from this one process.  Connections from external processes
** cannot see our internal hash table (obviously) and will thus not
................................................................................
static int asyncLock(sqlite3_file *pFile, int eLock){
  int rc = SQLITE_OK;
  AsyncFileData *p = ((AsyncFile *)pFile)->pData;

  pthread_mutex_lock(&async.lockMutex);
  if( p->lock.eLock<eLock ){
    AsyncLock *pLock;

    pLock = (AsyncLock *)sqlite3HashFind(&async.aLock, p->zName, p->nName);
    assert(pLock);
    for(/*no-op*/; pLock; pLock=pLock->pNext){

      if( pLock!=&p->lock && (
        (eLock==SQLITE_LOCK_EXCLUSIVE && pLock->eLock>=SQLITE_LOCK_SHARED) ||
        (eLock==SQLITE_LOCK_PENDING && pLock->eLock>=SQLITE_LOCK_RESERVED) ||
        (eLock==SQLITE_LOCK_RESERVED && pLock->eLock>=SQLITE_LOCK_RESERVED) ||
        (eLock==SQLITE_LOCK_SHARED && pLock->eLock>=SQLITE_LOCK_PENDING)
      )){
        rc = SQLITE_BUSY;
      }
    }
    if( rc==SQLITE_OK ){
      p->lock.eLock = eLock;







    }
  }
  pthread_mutex_unlock(&async.lockMutex);

  ASYNC_TRACE(("LOCK %d (%s) rc=%d\n", eLock, p->zName, rc));
  return rc;
}
static int asyncUnlock(sqlite3_file *pFile, int eLock){
  AsyncFileData *p = ((AsyncFile *)pFile)->pData;
  AsyncLock *pLock = &p->lock;
  pthread_mutex_lock(&async.lockMutex);
  if( pLock->eLock>eLock ){
    pLock->eLock = eLock;
  }
  pthread_mutex_unlock(&async.lockMutex);
  return SQLITE_OK;
}

/*
** This function is called when the pager layer first opens a database file
** and is checking for a hot-journal.
*/
static int asyncCheckReservedLock(sqlite3_file *pFile){
  int ret = 0;

  AsyncLock *pLock;
  AsyncFileData *p = ((AsyncFile *)pFile)->pData;

  pthread_mutex_lock(&async.lockMutex);
  pLock = (AsyncLock *)sqlite3HashFind(&async.aLock, p->zName, p->nName);
  for(/*no-op*/; pLock; pLock=pLock->pNext){
    if( pLock->eLock>=SQLITE_LOCK_RESERVED ){
      ret = 1;
    }
  }
  pthread_mutex_unlock(&async.lockMutex);

  ASYNC_TRACE(("CHECK-LOCK %d (%s)\n", ret, p->zName));
  return ret;
}

/* 
** This is a no-op, as the asynchronous backend does not support locking.
*/
static int asyncFileControl(sqlite3_file *id, int op, void *pArg){








  return SQLITE_ERROR;
}

/* 
** Return the device characteristics and sector-size of the device. It
** is not tricky to implement these correctly, as this backend might 
** not have an open file handle at this point.
................................................................................
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
  AsyncFile *p = (AsyncFile *)pFile;
  int nName = strlen(zName)+1;
  int rc;
  int nByte;
  AsyncFileData *pData;



  nByte = (
    sizeof(AsyncFileData) +        /* AsyncFileData structure */
    2 * pVfs->szOsFile +           /* AsyncFileData.pBaseRead and pBaseWrite */
    nName                          /* AsyncFileData.zName */
  ); 
  pData = sqlite3_malloc(nByte);
  if( !pData ){
................................................................................
    if( pOutFlags ) *pOutFlags = flags;
  }else{
    rc = sqlite3OsOpen(pVfs, zName, pData->pBaseRead, flags, pOutFlags);
    if( rc==SQLITE_OK && ((*pOutFlags)&SQLITE_OPEN_READWRITE) ){
      rc = sqlite3OsOpen(pVfs, zName, pData->pBaseWrite, flags, 0);
    }
  }



























  if( rc==SQLITE_OK ){
    HashElem *pElem;
    p->pMethod = &async_methods;
    p->pData = pData;
    incrOpenFileCount();

    /* Link AsyncFileData.lock into the linked list of AsyncLock structures
    ** for this file. Obtain the async.lockMutex mutex before doing so.
    */
    AsyncLock *pNext;
    pthread_mutex_lock(&async.lockMutex);
    pNext = sqlite3HashInsert(
        &async.aLock, pData->zName, pData->nName, (void *)&pData->lock
    );
    pData->lock.pNext = pNext;


    pElem = sqlite3HashFindElem(&async.aLock, pData->zName, pData->nName);
    pData->zName = (char *)sqliteHashKey(pElem);
    pthread_mutex_unlock(&async.lockMutex);
  }else{
    sqlite3OsClose(pData->pBaseRead);
    sqlite3OsClose(pData->pBaseWrite);
    sqlite3_free(pData);
  }


  return rc;
}

/*
** Implementation of sqlite3OsDelete. Add an entry to the end of the 
** write-op queue to perform the delete.
*/
................................................................................
        ASYNC_TRACE(("TRUNCATE %s to %d bytes\n", 
                p->pFileData->zName, p->iOffset));
        rc = sqlite3OsTruncate(pBase, p->iOffset);
        break;

      case ASYNC_CLOSE: {
        AsyncLock *pLock;

        AsyncFileData *pData = p->pFileData;
        ASYNC_TRACE(("CLOSE %s\n", p->pFileData->zName));
        sqlite3OsClose(pData->pBaseWrite);
        sqlite3OsClose(pData->pBaseRead);

        /* Unlink AsyncFileData.lock from the linked list of AsyncLock 
        ** structures for this file. Obtain the async.lockMutex mutex 
        ** before doing so.
        */
        pthread_mutex_lock(&async.lockMutex);
        pLock = sqlite3HashFind(&async.aLock, pData->zName, pData->nName);
        if( pLock==&pData->lock ){
          sqlite3HashInsert(
              &async.aLock, pData->zName, pData->nName, (void *)pLock->pNext 
          );
        }else{
          for( ; pLock && pLock->pNext!=&pData->lock; pLock=pLock->pNext);
          if( pLock ){
            pLock->pNext = pData->lock.pNext;
          }



        }
        pthread_mutex_unlock(&async.lockMutex);

        sqlite3_free(pData);
        break;
      }




















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

      case ASYNC_OPENEXCLUSIVE: {







>
>
>
>
>
>
>








>







 







>
>







 







|
<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







 







|
>
>







 







>





|







 







>
>
>







 







|
|
|
|
>

|
>
>
>
>
>

>

|







 







|







 







>
>
>





|
>


<
<
<







 







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







 







>

|
<
>
|
|
|
|
|






>
>
>
>
>
>
>









|





|








>





|
|













>
>
>
>
>
>
>
>







 







>
>







 







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







|
|

<
<
<
<
<
|
>
>


<






>







 







>





|





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






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







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
..
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
...
138
139
140
141
142
143
144
145

146
147
148
149
150
151
152
...
164
165
166
167
168
169
170

















171
172
173
174
175
176
177
...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
...
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
...
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
...
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
...
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
...
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496



497
498
499
500
501
502
503
...
593
594
595
596
597
598
599
600
601
602
603
604
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
...
637
638
639
640
641
642
643
644
645
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
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
...
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
...
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829





830
831
832
833
834

835
836
837
838
839
840
841
842
843
844
845
846
847
848
....
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
** stream that exceeds the I/O capability of the background writer thread,
** the queue of pending write operations will grow without bound until we
** run out of memory.  Users of this technique may want to keep track of
** the quantity of pending writes and stop accepting new write requests
** when the buffer gets to be too big.
*/

/* 
** If this symbol is defined, then file-system locks are obtained as
** required. This slows things down, but allows multiple processes
** to access the database concurrently.
*/
#define ENABLE_FILE_LOCKING

#include "sqliteInt.h"
#include <tcl.h>

/*
** This test uses pthreads and hence only works on unix and with
** a threadsafe build of SQLite.
*/
#if OS_UNIX && SQLITE_THREADSAFE


/*
** This demo uses pthreads.  If you do not have a pthreads implementation
** for your operating system, you will need to recode the threading 
** logic.
*/
#include <pthread.h>
................................................................................
#define MIN(x,y) ((x)<(y)?(x):(y))
#define MAX(x,y) ((x)>(y)?(x):(y))

/* Forward references */
typedef struct AsyncWrite AsyncWrite;
typedef struct AsyncFile AsyncFile;
typedef struct AsyncFileData AsyncFileData;
typedef struct AsyncFileLock AsyncFileLock;
typedef struct AsyncLock AsyncLock;

/* Enable for debugging */
static int sqlite3async_trace = 0;
# define ASYNC_TRACE(X) if( sqlite3async_trace ) asyncTrace X
static void asyncTrace(const char *zFormat, ...){
  char *z;
  va_list ap;
................................................................................
**     xOpenXXX (three versions)
**     xDelete
**     xFileExists
**     xSyncDirectory
**
** File handle operations (invoked by SQLite thread):
**
**         asyncWrite, asyncClose, asyncTruncate, asyncSync 

**    
**     The operations above add an entry to the global write-op list. They
**     prepare the entry, acquire the async.queueMutex momentarily while
**     list pointers are  manipulated to insert the new entry, then release
**     the mutex and signal the writer thread to wake up in case it happens
**     to be asleep.
**
................................................................................
**    
**     These primitives implement in-process locking using a hash table
**     on the file name.  Files are locked correctly for connections coming
**     from the same process.  But other processes cannot see these locks
**     and will therefore not honor them.
**
**

















** The writer thread:
**
**     The async.writerMutex is used to make sure only there is only
**     a single writer thread running at a time.
**
**     Inside the writer thread is a loop that works like this:
**
................................................................................
#ifndef SQLITE_ASYNC_TWO_FILEHANDLES
/* #define SQLITE_ASYNC_TWO_FILEHANDLES 0 */
#define SQLITE_ASYNC_TWO_FILEHANDLES 1
#endif

/*
** State information is held in the static variable "async" defined
** as the following structure.
**
** Both async.ioError and async.nFile are protected by async.queueMutex.
*/
static struct TestAsyncStaticData {
  pthread_mutex_t queueMutex;  /* Mutex for access to write operation queue */
  pthread_mutex_t writerMutex; /* Prevents multiple writer threads */
  pthread_mutex_t lockMutex;   /* For access to aLock hash table */
  pthread_cond_t queueSignal;  /* For waking up sleeping writer thread */
  pthread_cond_t emptySignal;  /* Notify when the write queue is empty */
................................................................................
#define ASYNC_NOOP          0
#define ASYNC_WRITE         1
#define ASYNC_SYNC          2
#define ASYNC_TRUNCATE      3
#define ASYNC_CLOSE         4
#define ASYNC_DELETE        5
#define ASYNC_OPENEXCLUSIVE 6
#define ASYNC_UNLOCK        7

/* Names of opcodes.  Used for debugging only.
** Make sure these stay in sync with the macros above!
*/
static const char *azOpcodeName[] = {
  "NOOP", "WRITE", "SYNC", "TRUNCATE", "CLOSE", "DELETE", "OPENEX", "UNLOCK"
};

/*
** Entries on the write-op queue are instances of the AsyncWrite
** structure, defined here.
**
** The interpretation of the iOffset and nByte variables varies depending 
................................................................................
**     iOffset -> Contains the "syncDir" flag.
**     nByte   -> Number of bytes of zBuf points to (file name).
**
** ASYNC_OPENEXCLUSIVE:
**     iOffset -> Value of "delflag".
**     nByte   -> Number of bytes of zBuf points to (file name).
**
** ASYNC_UNLOCK:
**     nByte   -> Argument to sqlite3OsUnlock().
**
**
** For an ASYNC_WRITE operation, zBuf points to the data to write to the file. 
** This space is sqlite3_malloc()d along with the AsyncWrite structure in a
** single blob, so is deleted when sqlite3_free() is called on the parent 
** structure.
*/
struct AsyncWrite {
................................................................................

/*
** An instance of the following structure is allocated along with each
** AsyncFileData structure (see AsyncFileData.lock), but is only used if the
** file was opened with the SQLITE_OPEN_MAIN_DB.
**
** The global async.aLock[] hash table maps from database file-name to a
** linked-list of AsyncFileLock structures corresponding to handles opened on
** the file. The AsyncFileLock structures are linked into the list when the
** file is opened and removed when it is closed. Mutex async.lockMutex must be
** held before accessing any AsyncFileLock structure or the async.aLock[]
** table.
*/
struct AsyncFileLock {
  int eLock;                /* Internally visible lock state (sqlite pov) */
  int eAsyncLock;           /* Lock-state with write-queue unlock */
  AsyncFileLock *pNext;
};

struct AsyncLock {
  sqlite3_file *pFile;
  int eLock;
  AsyncFileLock *pList;
};

/* 
** The AsyncFile structure is a subclass of sqlite3_file used for 
** asynchronous IO. 
**
** All of the actual data for the structure is stored in the structure
................................................................................
  AsyncFileData *pData;
};
struct AsyncFileData {
  char *zName;               /* Underlying OS filename - used for debugging */
  int nName;                 /* Number of characters in zName */
  sqlite3_file *pBaseRead;   /* Read handle to the underlying Os file */
  sqlite3_file *pBaseWrite;  /* Write handle to the underlying Os file */
  AsyncFileLock lock;
};

/*
** Add an entry to the end of the global write-op list. pWrite should point 
** to an AsyncWrite structure allocated using sqlite3_malloc().  The writer
** thread will call sqlite3_free() to free the structure after the specified
** operation has been completed.
................................................................................
*/
static int asyncRead(sqlite3_file *pFile, void *zOut, int iAmt, i64 iOffset){
  AsyncFileData *p = ((AsyncFile *)pFile)->pData;
  int rc = SQLITE_OK;
  i64 filesize;
  int nRead;
  sqlite3_file *pBase = p->pBaseRead;

  /* Grab the write queue mutex for the duration of the call */
  pthread_mutex_lock(&async.queueMutex);

  /* If an I/O error has previously occurred in this virtual file 
  ** system, then all subsequent operations fail.
  */
  if( async.ioError!=SQLITE_OK ){
    rc = async.ioError;
    goto asyncread_out;
  }




  if( pBase->pMethods ){
    rc = sqlite3OsFileSize(pBase, &filesize);
    if( rc!=SQLITE_OK ){
      goto asyncread_out;
    }
    nRead = MIN(filesize - iOffset, iAmt);
    if( nRead>0 ){
................................................................................
      }
    }
    *piSize = s;
  }
  pthread_mutex_unlock(&async.queueMutex);
  return rc;
}

/*
** Lock or unlock the actual file-system entry.
*/
static int getFileLock(AsyncLock *pLock){
  int rc = SQLITE_OK;
  AsyncFileLock *pIter;
  int eRequired = 0;

  if( pLock->pFile ){
    for(pIter=pLock->pList; pIter; pIter=pIter->pNext){
      assert(pIter->eAsyncLock>=pIter->eLock);
      if( pIter->eAsyncLock>eRequired ){
        eRequired = pIter->eAsyncLock;
      }
    }
    if( eRequired>pLock->eLock ){
      rc = sqlite3OsLock(pLock->pFile, eRequired);
    }else if(eRequired<pLock->eLock){
      rc = sqlite3OsUnlock(pLock->pFile, eRequired);
    }
    if( rc==SQLITE_OK ){
      pLock->eLock = eRequired;
    }
  }

  return rc;
}

/*
** No disk locking is performed.  We keep track of locks locally in
** the async.aLock hash table.  Locking should appear to work the same
** as with standard (unmodified) SQLite as long as all connections 
** come from this one process.  Connections from external processes
** cannot see our internal hash table (obviously) and will thus not
................................................................................
static int asyncLock(sqlite3_file *pFile, int eLock){
  int rc = SQLITE_OK;
  AsyncFileData *p = ((AsyncFile *)pFile)->pData;

  pthread_mutex_lock(&async.lockMutex);
  if( p->lock.eLock<eLock ){
    AsyncLock *pLock;
    AsyncFileLock *pIter;
    pLock = (AsyncLock *)sqlite3HashFind(&async.aLock, p->zName, p->nName);
    assert(pLock && pLock->pList);

    for(pIter=pLock->pList; pIter; pIter=pIter->pNext){
      if( pIter!=&p->lock && (
        (eLock==SQLITE_LOCK_EXCLUSIVE && pIter->eLock>=SQLITE_LOCK_SHARED) ||
        (eLock==SQLITE_LOCK_PENDING && pIter->eLock>=SQLITE_LOCK_RESERVED) ||
        (eLock==SQLITE_LOCK_RESERVED && pIter->eLock>=SQLITE_LOCK_RESERVED) ||
        (eLock==SQLITE_LOCK_SHARED && pIter->eLock>=SQLITE_LOCK_PENDING)
      )){
        rc = SQLITE_BUSY;
      }
    }
    if( rc==SQLITE_OK ){
      p->lock.eLock = eLock;
      if( eLock>p->lock.eAsyncLock ){
        p->lock.eAsyncLock = eLock;
      }
    }
    assert(p->lock.eAsyncLock>=p->lock.eLock);
    if( rc==SQLITE_OK ){
      rc = getFileLock(pLock);
    }
  }
  pthread_mutex_unlock(&async.lockMutex);

  ASYNC_TRACE(("LOCK %d (%s) rc=%d\n", eLock, p->zName, rc));
  return rc;
}
static int asyncUnlock(sqlite3_file *pFile, int eLock){
  AsyncFileData *p = ((AsyncFile *)pFile)->pData;
  AsyncFileLock *pLock = &p->lock;
  pthread_mutex_lock(&async.lockMutex);
  if( pLock->eLock>eLock ){
    pLock->eLock = eLock;
  }
  pthread_mutex_unlock(&async.lockMutex);
  return addNewAsyncWrite(p, ASYNC_UNLOCK, 0, eLock, 0);
}

/*
** This function is called when the pager layer first opens a database file
** and is checking for a hot-journal.
*/
static int asyncCheckReservedLock(sqlite3_file *pFile){
  int ret = 0;
  AsyncFileLock *pIter;
  AsyncLock *pLock;
  AsyncFileData *p = ((AsyncFile *)pFile)->pData;

  pthread_mutex_lock(&async.lockMutex);
  pLock = (AsyncLock *)sqlite3HashFind(&async.aLock, p->zName, p->nName);
  for(pIter=pLock->pList; pIter; pIter=pIter->pNext){
    if( pIter->eLock>=SQLITE_LOCK_RESERVED ){
      ret = 1;
    }
  }
  pthread_mutex_unlock(&async.lockMutex);

  ASYNC_TRACE(("CHECK-LOCK %d (%s)\n", ret, p->zName));
  return ret;
}

/* 
** This is a no-op, as the asynchronous backend does not support locking.
*/
static int asyncFileControl(sqlite3_file *id, int op, void *pArg){
  switch( op ){
    case SQLITE_FCNTL_LOCKSTATE: {
      pthread_mutex_lock(&async.lockMutex);
      *(int*)pArg = ((AsyncFile*)id)->pData->lock.eLock;
      pthread_mutex_unlock(&async.lockMutex);
      return SQLITE_OK;
    }
  }
  return SQLITE_ERROR;
}

/* 
** Return the device characteristics and sector-size of the device. It
** is not tricky to implement these correctly, as this backend might 
** not have an open file handle at this point.
................................................................................
  sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData;
  AsyncFile *p = (AsyncFile *)pFile;
  int nName = strlen(zName)+1;
  int rc;
  int nByte;
  AsyncFileData *pData;

  AsyncLock *pLock = 0;

  nByte = (
    sizeof(AsyncFileData) +        /* AsyncFileData structure */
    2 * pVfs->szOsFile +           /* AsyncFileData.pBaseRead and pBaseWrite */
    nName                          /* AsyncFileData.zName */
  ); 
  pData = sqlite3_malloc(nByte);
  if( !pData ){
................................................................................
    if( pOutFlags ) *pOutFlags = flags;
  }else{
    rc = sqlite3OsOpen(pVfs, zName, pData->pBaseRead, flags, pOutFlags);
    if( rc==SQLITE_OK && ((*pOutFlags)&SQLITE_OPEN_READWRITE) ){
      rc = sqlite3OsOpen(pVfs, zName, pData->pBaseWrite, flags, 0);
    }
  }

  pthread_mutex_lock(&async.lockMutex);

  if( rc==SQLITE_OK ){
    pLock = sqlite3HashFind(&async.aLock, pData->zName, pData->nName);
    if( !pLock ){
      pLock = sqlite3MallocZero(pVfs->szOsFile + sizeof(AsyncLock));
      if( pLock ){
#ifdef ENABLE_FILE_LOCKING
        if( flags&SQLITE_OPEN_MAIN_DB ){
          pLock->pFile = (sqlite3_file *)&pLock[1];
          rc = sqlite3OsOpen(pVfs, zName, pLock->pFile, flags, 0);
          if( rc!=SQLITE_OK ){
            sqlite3_free(pLock);
            pLock = 0;
          }
        }
#endif
        sqlite3HashInsert(
          &async.aLock, pData->zName, pData->nName, (void *)pLock
        );
      }else{
        rc = SQLITE_NOMEM;
      }
    }
  }

  if( rc==SQLITE_OK ){
    HashElem *pElem;
    p->pMethod = &async_methods;
    p->pData = pData;
    incrOpenFileCount();

    /* Link AsyncFileData.lock into the linked list of 
    ** AsyncFileLock structures for this file.
    */





    pData->lock.pNext = pLock->pList;
    pLock->pList = &pData->lock;

    pElem = sqlite3HashFindElem(&async.aLock, pData->zName, pData->nName);
    pData->zName = (char *)sqliteHashKey(pElem);

  }else{
    sqlite3OsClose(pData->pBaseRead);
    sqlite3OsClose(pData->pBaseWrite);
    sqlite3_free(pData);
  }

  pthread_mutex_unlock(&async.lockMutex);
  return rc;
}

/*
** Implementation of sqlite3OsDelete. Add an entry to the end of the 
** write-op queue to perform the delete.
*/
................................................................................
        ASYNC_TRACE(("TRUNCATE %s to %d bytes\n", 
                p->pFileData->zName, p->iOffset));
        rc = sqlite3OsTruncate(pBase, p->iOffset);
        break;

      case ASYNC_CLOSE: {
        AsyncLock *pLock;
        AsyncFileLock **ppIter;
        AsyncFileData *pData = p->pFileData;
        ASYNC_TRACE(("CLOSE %s\n", p->pFileData->zName));
        sqlite3OsClose(pData->pBaseWrite);
        sqlite3OsClose(pData->pBaseRead);

        /* Unlink AsyncFileData.lock from the linked list of AsyncFileLock 
        ** structures for this file. Obtain the async.lockMutex mutex 
        ** before doing so.
        */
        pthread_mutex_lock(&async.lockMutex);
        pLock = sqlite3HashFind(&async.aLock, pData->zName, pData->nName);
        for(ppIter=&pLock->pList; *ppIter; ppIter=&((*ppIter)->pNext)){
          if( (*ppIter)==&pData->lock ){
            *ppIter = (*ppIter)->pNext;
            break;
          }
        }
        if( !pLock->pList ){
          if( pLock->pFile ) sqlite3OsClose(pLock->pFile);
          sqlite3_free(pLock);
          sqlite3HashInsert(&async.aLock, pData->zName, pData->nName, 0);
        }else{
          rc = getFileLock(pLock);
        }
        pthread_mutex_unlock(&async.lockMutex);

        sqlite3_free(pData);
        break;
      }

      case ASYNC_UNLOCK: {
        AsyncLock *pLock;
        AsyncFileData *pData = p->pFileData;
        int eLock = p->nByte;
        pthread_mutex_lock(&async.lockMutex);
        if( pData->lock.eAsyncLock>eLock ){
          if( pData->lock.eLock>eLock ){
            pData->lock.eAsyncLock = pData->lock.eLock;
          }else{
            pData->lock.eAsyncLock = eLock;
          }
        }
        assert(pData->lock.eAsyncLock>=pData->lock.eLock);
        pLock = sqlite3HashFind(&async.aLock, pData->zName, pData->nName);
        rc = getFileLock(pLock);
        pthread_mutex_unlock(&async.lockMutex);
        break;
      }

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

      case ASYNC_OPENEXCLUSIVE: {

Changes to test/async.test.

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
28
29
30
31
32
33
34

35

36
37
38
39
40
41
42
#    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.
#
#***********************************************************************
# This file runs all tests.
#
# $Id: async.test,v 1.8 2007/09/04 14:31:47 danielk1977 Exp $


if {[catch {sqlite3async_enable}]} {
  # The async logic is not built into this system
  return
}

................................................................................
  select4.test
  insert.test
  insert2.test
  insert3.test
  trans.test
  lock.test
  lock3.test

}


# Enable asynchronous IO.
sqlite3async_enable 1

rename do_test really_do_test
proc do_test {name args} {
  uplevel really_do_test async_io-$name $args







|







 







>

>







2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
..
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#    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.
#
#***********************************************************************
# This file runs all tests.
#
# $Id: async.test,v 1.9 2007/09/04 18:28:44 danielk1977 Exp $


if {[catch {sqlite3async_enable}]} {
  # The async logic is not built into this system
  return
}

................................................................................
  select4.test
  insert.test
  insert2.test
  insert3.test
  trans.test
  lock.test
  lock3.test
  lock2.test
}
# set INCLUDE lock4.test

# Enable asynchronous IO.
sqlite3async_enable 1

rename do_test really_do_test
proc do_test {name args} {
  uplevel really_do_test async_io-$name $args