Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix error handling (malloc and io errors) in the asynchronous backend. (CVS 4404) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
80a44382d149b9d53212c15368565ede |
User & Date: | danielk1977 2007-09-05 16:54:41.000 |
Context
2007-09-05
| ||
17:06 | Fix a harmless typo in the PRNG initialization on win32. Ticket #2617. (CVS 4405) (check-in: ea1d76e3fa user: drh tags: trunk) | |
16:54 | Fix error handling (malloc and io errors) in the asynchronous backend. (CVS 4404) (check-in: 80a44382d1 user: danielk1977 tags: trunk) | |
14:32 | Test that the asynchronous backend works with components like "." or ".." in the path to the database file. (CVS 4403) (check-in: 0a87a85422 user: danielk1977 tags: trunk) | |
Changes
Changes to src/test_async.c.
︙ | ︙ | |||
356 357 358 359 360 361 362 363 364 365 366 367 368 369 | }; 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. | > | 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 | }; 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; AsyncWrite close; }; /* ** 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. |
︙ | ︙ | |||
386 387 388 389 390 391 392 | } 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--; | < < < | 387 388 389 390 391 392 393 394 395 396 397 398 399 400 | } 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. */ |
︙ | ︙ | |||
429 430 431 432 433 434 435 | ){ AsyncWrite *p; if( op!=ASYNC_CLOSE && async.ioError ){ return async.ioError; } p = sqlite3_malloc(sizeof(AsyncWrite) + (zByte?nByte:0)); if( !p ){ | > > > > > > | | 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 | ){ AsyncWrite *p; if( op!=ASYNC_CLOSE && async.ioError ){ return async.ioError; } p = sqlite3_malloc(sizeof(AsyncWrite) + (zByte?nByte:0)); if( !p ){ /* The upper layer does not expect operations like OsWrite() to ** return SQLITE_NOMEM. This is partly because under normal conditions ** SQLite is required to do rollback without calling malloc(). So ** if malloc() fails here, treat it as an I/O error. The above ** layer knows how to handle that. */ return SQLITE_IOERR; } p->op = op; p->iOffset = iOffset; p->nByte = nByte; p->pFileData = pFileData; p->pNext = 0; if( zByte ){ |
︙ | ︙ | |||
458 459 460 461 462 463 464 | AsyncFileData *p = ((AsyncFile *)pFile)->pData; /* Unlock the file, if it is locked */ pthread_mutex_lock(&async.lockMutex); p->lock.eLock = 0; pthread_mutex_unlock(&async.lockMutex); | | > | 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 | AsyncFileData *p = ((AsyncFile *)pFile)->pData; /* Unlock the file, if it is locked */ pthread_mutex_lock(&async.lockMutex); p->lock.eLock = 0; pthread_mutex_unlock(&async.lockMutex); addAsyncWrite(&p->close); return SQLITE_OK; } /* ** Implementation of sqlite3OsWrite() for asynchronous files. Instead of ** writing to the underlying file, this function adds an entry to the end of ** the global AsyncWrite list. Either SQLITE_OK or SQLITE_NOMEM may be ** returned. |
︙ | ︙ | |||
732 733 734 735 736 737 738 739 740 741 742 743 744 745 | */ static int asyncSectorSize(sqlite3_file *pFile){ return 512; } static int asyncDeviceCharacteristics(sqlite3_file *pFile){ return 0; } /* ** Open a file. */ static int asyncOpen( sqlite3_vfs *pAsyncVfs, const char *zName, | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 773 774 775 776 777 778 | */ static int asyncSectorSize(sqlite3_file *pFile){ return 512; } static int asyncDeviceCharacteristics(sqlite3_file *pFile){ return 0; } static int unlinkAsyncFile(AsyncFileData *pData){ AsyncLock *pLock; AsyncFileLock **ppIter; int rc = SQLITE_OK; pLock = sqlite3HashFind(&async.aLock, pData->zName, pData->nName); for(ppIter=&pLock->pList; *ppIter; ppIter=&((*ppIter)->pNext)){ if( (*ppIter)==&pData->lock ){ *ppIter = pData->lock.pNext; break; } } if( !pLock->pList ){ if( pLock->pFile ){ sqlite3OsClose(pLock->pFile); } sqlite3_free(pLock); sqlite3HashInsert(&async.aLock, pData->zName, pData->nName, 0); if( !sqliteHashFirst(&async.aLock) ){ sqlite3HashClear(&async.aLock); } }else{ rc = getFileLock(pLock); } return rc; } /* ** Open a file. */ static int asyncOpen( sqlite3_vfs *pAsyncVfs, const char *zName, |
︙ | ︙ | |||
762 763 764 765 766 767 768 | asyncSectorSize, /* xSectorSize */ asyncDeviceCharacteristics /* xDeviceCharacteristics */ }; sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData; AsyncFile *p = (AsyncFile *)pFile; int nName = strlen(zName)+1; | | < > > > | < < < > | > > > > | 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 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 | asyncSectorSize, /* xSectorSize */ asyncDeviceCharacteristics /* xDeviceCharacteristics */ }; sqlite3_vfs *pVfs = (sqlite3_vfs *)pAsyncVfs->pAppData; AsyncFile *p = (AsyncFile *)pFile; int nName = strlen(zName)+1; int rc = SQLITE_OK; int nByte; AsyncFileData *pData; AsyncLock *pLock = 0; int isExclusive = (flags&SQLITE_OPEN_EXCLUSIVE); nByte = ( sizeof(AsyncFileData) + /* AsyncFileData structure */ 2 * pVfs->szOsFile + /* AsyncFileData.pBaseRead and pBaseWrite */ nName /* AsyncFileData.zName */ ); pData = sqlite3_malloc(nByte); if( !pData ){ return SQLITE_NOMEM; } memset(pData, 0, nByte); pData->zName = (char *)&pData[1]; pData->nName = nName; pData->pBaseRead = (sqlite3_file *)&pData->zName[nName]; pData->pBaseWrite = (sqlite3_file *)&pData->zName[nName+pVfs->szOsFile]; pData->close.pFileData = pData; pData->close.op = ASYNC_CLOSE; memcpy(pData->zName, zName, nName); if( !isExclusive ){ 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 ){ AsyncLock *pDelete; #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 pDelete = sqlite3HashInsert( &async.aLock, pData->zName, pData->nName, (void *)pLock ); if( pDelete ){ rc = SQLITE_NOMEM; sqlite3_free(pLock); } }else{ rc = SQLITE_NOMEM; } } } if( rc==SQLITE_OK ){ |
︙ | ︙ | |||
841 842 843 844 845 846 847 848 849 850 851 852 853 854 | }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. */ | > > > > > > > > > > > > | 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 | }else{ sqlite3OsClose(pData->pBaseRead); sqlite3OsClose(pData->pBaseWrite); sqlite3_free(pData); } pthread_mutex_unlock(&async.lockMutex); if( rc==SQLITE_OK && isExclusive ){ rc = addNewAsyncWrite(pData, ASYNC_OPENEXCLUSIVE, (i64)flags, 0, 0); if( rc==SQLITE_OK ){ if( pOutFlags ) *pOutFlags = flags; }else{ pthread_mutex_lock(&async.lockMutex); unlinkAsyncFile(pData); pthread_mutex_unlock(&async.lockMutex); sqlite3_free(pData); } } return rc; } /* ** Implementation of sqlite3OsDelete. Add an entry to the end of the ** write-op queue to perform the delete. */ |
︙ | ︙ | |||
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 | int rc = SQLITE_OK; int holdingMutex = 0; if( pthread_mutex_trylock(&async.writerMutex) ){ return 0; } while( async.writerHaltNow==0 ){ sqlite3_file *pBase = 0; if( !holdingMutex ){ pthread_mutex_lock(&async.queueMutex); } while( (p = async.pQueueFirst)==0 ){ pthread_cond_broadcast(&async.emptySignal); | > | 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 | int rc = SQLITE_OK; int holdingMutex = 0; if( pthread_mutex_trylock(&async.writerMutex) ){ return 0; } while( async.writerHaltNow==0 ){ int doNotFree = 0; sqlite3_file *pBase = 0; if( !holdingMutex ){ pthread_mutex_lock(&async.queueMutex); } while( (p = async.pQueueFirst)==0 ){ pthread_cond_broadcast(&async.emptySignal); |
︙ | ︙ | |||
1139 1140 1141 1142 1143 1144 1145 | assert( pBase ); ASYNC_TRACE(("TRUNCATE %s to %d bytes\n", p->pFileData->zName, p->iOffset)); rc = sqlite3OsTruncate(pBase, p->iOffset); break; case ASYNC_CLOSE: { | < < < < < < < < < < < < < < < < < < < | < > > | 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 | assert( pBase ); ASYNC_TRACE(("TRUNCATE %s to %d bytes\n", p->pFileData->zName, p->iOffset)); rc = sqlite3OsTruncate(pBase, p->iOffset); break; case ASYNC_CLOSE: { 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); rc = unlinkAsyncFile(pData); pthread_mutex_unlock(&async.lockMutex); async.pQueueFirst = p->pNext; sqlite3_free(pData); doNotFree = 1; break; } case ASYNC_UNLOCK: { AsyncLock *pLock; AsyncFileData *pData = p->pFileData; int eLock = p->nByte; |
︙ | ︙ | |||
1223 1224 1225 1226 1227 1228 1229 | pthread_mutex_lock(&async.queueMutex); holdingMutex = 1; } /* ASYNC_TRACE(("UNLINK %p\n", p)); */ if( p==async.pQueueLast ){ async.pQueueLast = 0; } | > | | > | 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 | pthread_mutex_lock(&async.queueMutex); holdingMutex = 1; } /* ASYNC_TRACE(("UNLINK %p\n", p)); */ if( p==async.pQueueLast ){ async.pQueueLast = 0; } if( !doNotFree ){ async.pQueueFirst = p->pNext; sqlite3_free(p); } assert( holdingMutex ); /* An IO error has occured. We cannot report the error back to the ** connection that requested the I/O since the error happened ** asynchronously. The connection has already moved on. There ** really is nobody to report the error to. ** |
︙ | ︙ | |||
1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 | ** multi-file transaction that included the database associated with ** the IO error (i.e. a database ATTACHed to the same handle at some ** point in time). */ if( rc!=SQLITE_OK ){ async.ioError = rc; } /* Drop the queue mutex before continuing to the next write operation ** in order to give other threads a chance to work with the write queue. */ if( !async.pQueueFirst || !async.ioError ){ | > > > > > > > > < | 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 | ** multi-file transaction that included the database associated with ** the IO error (i.e. a database ATTACHed to the same handle at some ** point in time). */ if( rc!=SQLITE_OK ){ async.ioError = rc; } if( async.ioError && !async.pQueueFirst ){ pthread_mutex_lock(&async.lockMutex); if( 0==sqliteHashFirst(&async.aLock) ){ async.ioError = SQLITE_OK; } pthread_mutex_unlock(&async.lockMutex); } /* Drop the queue mutex before continuing to the next write operation ** in order to give other threads a chance to work with the write queue. */ if( !async.pQueueFirst || !async.ioError ){ pthread_mutex_unlock(&async.queueMutex); holdingMutex = 0; if( async.ioDelay>0 ){ sqlite3OsSleep(pVfs, async.ioDelay); }else{ sched_yield(); } |
︙ | ︙ |
Changes to test/async2.test.
1 2 3 4 5 6 7 | # # 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. # #*********************************************************************** # | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # # 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: async2.test,v 1.8 2007/09/05 16:54:41 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl if { [info commands sqlite3async_enable]=="" || |
︙ | ︙ | |||
42 43 44 45 46 47 48 | COMMIT; UPDATE counter SET c = 'FIN'; } db close | < < < > > > > | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | COMMIT; UPDATE counter SET c = 'FIN'; } db close foreach err [list ioerr malloc-transient malloc-persistent] { set ::go 1 for {set n 1} {$::go} {incr n} { set ::sqlite_io_error_pending 0 sqlite3_memdebug_fail -1 file delete -force test.db test.db-journal sqlite3 db test.db execsql $::setup_script db close sqlite3async_enable 1 sqlite3 db test.db switch -- $err { ioerr { set ::sqlite_io_error_pending $n } malloc-persistent { sqlite3_memdebug_fail $n -repeat 1 } malloc-transient { sqlite3_memdebug_fail $n -repeat 0 } } catchsql $::sql_script db close sqlite3async_halt idle sqlite3async_start sqlite3async_wait sqlite3async_enable 0 set ::sqlite_io_error_pending 0 sqlite3_memdebug_fail -1 |
︙ | ︙ |
Changes to test/async3.test.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # #*********************************************************************** # # The focus of this file is testing the code in test_async.c. # Specifically, it tests that the xFullPathname() method of # of the asynchronous vfs works correctly. # | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # #*********************************************************************** # # The focus of this file is testing the code in test_async.c. # Specifically, it tests that the xFullPathname() method of # of the asynchronous vfs works correctly. # # $Id: async3.test,v 1.2 2007/09/05 16:54:41 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl if { [info commands sqlite3async_enable]=="" } { # The async logic is not built into this system puts "Skipping async3 tests: not compiled with required features" |
︙ | ︙ | |||
39 40 41 42 43 44 45 46 47 48 49 50 51 52 | } do_test async3-1.0 { file mkdir [file join chocolate banana vanilla] file delete -force chocolate/banana/vanilla/file.db file delete -force chocolate/banana/vanilla/file.db-journal } {} do_test async3-1.1 { sqlite3 db chocolate/banana/vanilla/file.db execsql { CREATE TABLE abc(a, b, c); BEGIN; INSERT INTO abc VALUES(1, 2, 3); } | > | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | } do_test async3-1.0 { file mkdir [file join chocolate banana vanilla] file delete -force chocolate/banana/vanilla/file.db file delete -force chocolate/banana/vanilla/file.db-journal } {} do_test async3-1.1 { sqlite3 db chocolate/banana/vanilla/file.db execsql { CREATE TABLE abc(a, b, c); BEGIN; INSERT INTO abc VALUES(1, 2, 3); } |
︙ | ︙ |