Index: src/fault.c ================================================================== --- src/fault.c +++ src/fault.c @@ -27,61 +27,44 @@ #include "sqliteInt.h" #ifndef SQLITE_OMIT_BUILTIN_TEST /* -** Global variables. -*/ -typedef struct BenignMallocHooks BenignMallocHooks; -static SQLITE_WSD struct BenignMallocHooks { - void (*xBenignBegin)(void); - void (*xBenignEnd)(void); -} sqlite3Hooks = { 0, 0 }; - -/* The "wsdHooks" macro will resolve to the appropriate BenignMallocHooks -** structure. If writable static data is unsupported on the target, -** we have to locate the state vector at run-time. In the more common -** case where writable static data is supported, wsdHooks can refer directly -** to the "sqlite3Hooks" state vector declared above. -*/ -#ifdef SQLITE_OMIT_WSD -# define wsdHooksInit \ - BenignMallocHooks *x = &GLOBAL(BenignMallocHooks,sqlite3Hooks) -# define wsdHooks x[0] -#else -# define wsdHooksInit -# define wsdHooks sqlite3Hooks -#endif - - -/* -** Register hooks to call when sqlite3BeginBenignMalloc() and -** sqlite3EndBenignMalloc() are called, respectively. -*/ -void sqlite3BenignMallocHooks( - void (*xBenignBegin)(void), - void (*xBenignEnd)(void) -){ - wsdHooksInit; - wsdHooks.xBenignBegin = xBenignBegin; - wsdHooks.xBenignEnd = xBenignEnd; -} - -/* -** This (sqlite3EndBenignMalloc()) is called by SQLite code to indicate that -** subsequent malloc failures are benign. A call to sqlite3EndBenignMalloc() -** indicates that subsequent malloc failures are non-benign. -*/ -void sqlite3BeginBenignMalloc(void){ - wsdHooksInit; - if( wsdHooks.xBenignBegin ){ - wsdHooks.xBenignBegin(); - } -} -void sqlite3EndBenignMalloc(void){ - wsdHooksInit; - if( wsdHooks.xBenignEnd ){ - wsdHooks.xBenignEnd(); - } +** The default xBenignCtrl function is a no-op +*/ +static void sqlite3BenignCtrlNoop(int eOp){ + (void)eOp; +} + +/* +** Global variable: Pointer to the benign malloc control interface. +*/ +static void (*sqlite3xBenignCtrl)(int) = sqlite3BenignCtrlNoop; + +/* +** Register a pointer to the benign-malloc control interface function. +** If the argument is a NULL pointer, register the default no-op controller. +*/ +void sqlite3BenignMallocHooks(void (*xBenignCtrl)(int)){ + sqlite3xBenignCtrl = xBenignCtrl ? xBenignCtrl : sqlite3BenignCtrlNoop; +} + +/* +** The sqlite3BeginBenignMalloc() and sqlite3EndBenignMalloc() calls bracket +** sections of code for which malloc failures are non-fatal. +*/ +void sqlite3BeginBenignMalloc(void){ + sqlite3xBenignCtrl(1); +} +void sqlite3EndBenignMalloc(void){ + sqlite3xBenignCtrl(0); +} + +/* +** The sqlite3PreviousBenignMalloc() call indicates that the previous +** malloc call (which must have failed) was a benign failure. +*/ +void sqlite3PreviousBenignMalloc(void){ + sqlite3xBenignCtrl(2); } #endif /* #ifndef SQLITE_OMIT_BUILTIN_TEST */ Index: src/hash.c ================================================================== --- src/hash.c +++ src/hash.c @@ -116,15 +116,15 @@ ** sqlite3MallocZero() to make the allocation, as sqlite3MallocZero() ** only zeroes the requested number of bytes whereas this module will ** use the actual amount of space allocated for the hash table (which ** may be larger than the requested amount). */ - sqlite3BeginBenignMalloc(); new_ht = (struct _ht *)sqlite3Malloc( new_size*sizeof(struct _ht) ); - sqlite3EndBenignMalloc(); - - if( new_ht==0 ) return 0; + if( new_ht==0 ){ + sqlite3PreviousBenignMalloc(); + return 0; + } sqlite3_free(pH->ht); pH->ht = new_ht; pH->htsize = new_size = sqlite3MallocSize(new_ht)/sizeof(struct _ht); memset(new_ht, 0, new_size*sizeof(struct _ht)); for(elem=pH->first, pH->first=0; elem; elem = next_elem){ Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -662,14 +662,16 @@ if( cnt<0 ) cnt = 0; if( sz==0 || cnt==0 ){ sz = 0; pStart = 0; }else if( pBuf==0 ){ - sqlite3BeginBenignMalloc(); pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */ - sqlite3EndBenignMalloc(); - if( pStart ) cnt = sqlite3MallocSize(pStart)/sz; + if( pStart==0 ){ + sqlite3PreviousBenignMalloc(); + }else{ + cnt = sqlite3MallocSize(pStart)/sz; + } }else{ pStart = pBuf; } db->lookaside.pStart = pStart; db->lookaside.pFree = 0; @@ -3438,22 +3440,22 @@ rc = sqlite3FaultSim(0); break; } /* - ** sqlite3_test_control(BENIGN_MALLOC_HOOKS, xBegin, xEnd) + ** sqlite3_test_control(BENIGN_MALLOC_CTRL, xCtrl) ** - ** Register hooks to call to indicate which malloc() failures - ** are benign. + ** Register a callback function that will handle calls to + ** sqlite3BeginBenignMalloc(), sqlite3EndBenignMalloc(), and + ** sqlite3PreviousBenignMalloc(). If the callback function pointer + ** is NULL, then a built-in default (no-op) handler is used. */ - case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: { - typedef void (*void_function)(void); - void_function xBenignBegin; - void_function xBenignEnd; - xBenignBegin = va_arg(ap, void_function); - xBenignEnd = va_arg(ap, void_function); - sqlite3BenignMallocHooks(xBenignBegin, xBenignEnd); + case SQLITE_TESTCTRL_BENIGN_MALLOC_CTRL: { + typedef void (*void_function)(int); + void_function xBenignCtrl; + xBenignCtrl = va_arg(ap, void_function); + sqlite3BenignMallocHooks(xBenignCtrl); break; } /* ** sqlite3_test_control(SQLITE_TESTCTRL_PENDING_BYTE, unsigned int X) Index: src/malloc.c ================================================================== --- src/malloc.c +++ src/malloc.c @@ -524,10 +524,11 @@ mem0.alarmThreshold-nDiff ){ sqlite3MallocAlarm(nDiff); } pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); if( pNew==0 && mem0.alarmThreshold>0 ){ + sqlite3PreviousBenignMalloc(); sqlite3MallocAlarm((int)nBytes); pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); } if( pNew ){ nNew = sqlite3MallocSize(pNew); Index: src/pcache1.c ================================================================== --- src/pcache1.c +++ src/pcache1.c @@ -270,22 +270,22 @@ i64 szBulk; char *zBulk; if( pcache1.nInitPage==0 ) return 0; /* Do not bother with a bulk allocation if the cache size very small */ if( pCache->nMax<3 ) return 0; - sqlite3BeginBenignMalloc(); if( pcache1.nInitPage>0 ){ szBulk = pCache->szAlloc * (i64)pcache1.nInitPage; }else{ szBulk = -1024 * (i64)pcache1.nInitPage; } if( szBulk > pCache->szAlloc*(i64)pCache->nMax ){ szBulk = pCache->szAlloc*pCache->nMax; } zBulk = pCache->pBulk = sqlite3Malloc( szBulk ); - sqlite3EndBenignMalloc(); - if( zBulk ){ + if( zBulk==0 ){ + sqlite3PreviousBenignMalloc(); + }else{ int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc; int i; for(i=0; iszPage]; pX->page.pBuf = zBulk; Index: src/shell.c ================================================================== --- src/shell.c +++ src/shell.c @@ -3797,11 +3797,10 @@ { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE }, { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE }, { "prng_reset", SQLITE_TESTCTRL_PRNG_RESET }, { "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST }, { "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL }, - { "benign_malloc_hooks", SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS }, { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE }, { "assert", SQLITE_TESTCTRL_ASSERT }, { "always", SQLITE_TESTCTRL_ALWAYS }, { "reserve", SQLITE_TESTCTRL_RESERVE }, { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS }, @@ -3912,14 +3911,10 @@ }else{ fprintf(stderr,"Usage: .testctrl imposter dbName onoff tnum\n"); } break; - case SQLITE_TESTCTRL_BITVEC_TEST: - case SQLITE_TESTCTRL_FAULT_INSTALL: - case SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS: - case SQLITE_TESTCTRL_SCRATCHMALLOC: default: fprintf(stderr,"Error: CLI support for testctrl %s not implemented\n", azArg[1]); break; } Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -6416,11 +6416,11 @@ #define SQLITE_TESTCTRL_PRNG_SAVE 5 #define SQLITE_TESTCTRL_PRNG_RESTORE 6 #define SQLITE_TESTCTRL_PRNG_RESET 7 #define SQLITE_TESTCTRL_BITVEC_TEST 8 #define SQLITE_TESTCTRL_FAULT_INSTALL 9 -#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 +#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 /* NOT USED */ #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 @@ -6432,11 +6432,12 @@ #define SQLITE_TESTCTRL_VDBE_COVERAGE 21 #define SQLITE_TESTCTRL_BYTEORDER 22 #define SQLITE_TESTCTRL_ISINIT 23 #define SQLITE_TESTCTRL_SORTER_MMAP 24 #define SQLITE_TESTCTRL_IMPOSTER 25 -#define SQLITE_TESTCTRL_LAST 25 +#define SQLITE_TESTCTRL_BENIGN_MALLOC_CTRL 26 +#define SQLITE_TESTCTRL_LAST 26 /* ** CAPI3REF: SQLite Runtime Status ** ** ^These interfaces are used to retrieve runtime status information Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -3154,11 +3154,11 @@ void sqlite3ScratchFree(void*); void *sqlite3PageMalloc(int); void sqlite3PageFree(void*); void sqlite3MemSetDefault(void); #ifndef SQLITE_OMIT_BUILTIN_TEST -void sqlite3BenignMallocHooks(void (*)(void), void (*)(void)); +void sqlite3BenignMallocHooks(void (*)(int)); #endif int sqlite3HeapNearlyFull(void); /* ** On systems with ample stack space and that support alloca(), make @@ -3834,13 +3834,15 @@ ** is not defined. */ #ifndef SQLITE_OMIT_BUILTIN_TEST void sqlite3BeginBenignMalloc(void); void sqlite3EndBenignMalloc(void); + void sqlite3PreviousBenignMalloc(void); #else #define sqlite3BeginBenignMalloc() #define sqlite3EndBenignMalloc() + #define sqlite3PreviousBenignMalloc() #endif /* ** Allowed return values from sqlite3FindInIndex() */ Index: src/test_malloc.c ================================================================== --- src/test_malloc.c +++ src/test_malloc.c @@ -167,16 +167,31 @@ }else{ return -1; } } - -static void faultsimBeginBenign(void){ - memfault.isBenignMode++; -} -static void faultsimEndBenign(void){ - memfault.isBenignMode--; +/* +** The SQLITE_TESTCTRL_BENIGN_MALLOC_CTRL callback. The eOp argument means: +** +** 0: Leave benign malloc mode +** 1: Enter benign malloc mode (may be nested) +** 2: Make the previous malloc failure benign if it was not already so +*/ +static void faultsimBenignCtrl(int eOp){ + switch( eOp ){ + case 0: /* Leave benign malloc mode */ + assert( memfault.isBenignMode>0 ); + memfault.isBenignMode--; + break; + case 1: /* Enter benign malloc mode */ + memfault.isBenignMode++; + break; + case 2: /* Previous failure is benign */ + assert( memfault.nFail>0 || memfault.enable==0 ); + if( memfault.isBenignMode==0 ) memfault.nBenign++; + break; + } } /* ** Add or remove the fault-simulation layer using sqlite3_config(). If ** the argument is non-zero, the @@ -205,13 +220,12 @@ rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memfault.m); assert(memfault.m.xMalloc); if( rc==SQLITE_OK ){ rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &m); } - sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, - faultsimBeginBenign, faultsimEndBenign - ); + sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_CTRL, + faultsimBenignCtrl); }else{ sqlite3_mem_methods m2; assert(memfault.m.xMalloc); /* One should be able to reset the default memory allocator by storing @@ -220,11 +234,11 @@ sqlite3_config(SQLITE_CONFIG_MALLOC, &m2); sqlite3_config(SQLITE_CONFIG_GETMALLOC, &m2); assert( memcmp(&m2, &memfault.m, sizeof(m2))==0 ); rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memfault.m); - sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, 0, 0); + sqlite3_test_control(SQLITE_TESTCTRL_BENIGN_MALLOC_CTRL, 0); } if( rc==SQLITE_OK ){ memfault.isInstalled = 1; }