Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Extend CLI resource manager usage. Improve interface to it for clarity and simplicity. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | cli_extension |
Files: | files | file ages | folders |
SHA3-256: |
fd379f22926d55d52176b34b20e6dda2 |
User & Date: | larrybr 2023-05-13 16:54:12.397 |
Context
2023-05-15
| ||
21:33 | Fix CLI memory leak upon loading any shell extension. (check-in: b91cec479d user: larrybr tags: cli_extension) | |
2023-05-13
| ||
16:54 | Extend CLI resource manager usage. Improve interface to it for clarity and simplicity. (check-in: fd379f2292 user: larrybr tags: cli_extension) | |
15:34 | Mention that sqlite3_mutex_free() accepts NULL as a no-op. (no code change) (check-in: 603d9ad501 user: larrybr tags: trunk) | |
07:23 | CLI closer to doing full cleanup on error exits. Needs testing with OOM simulation. (check-in: 8751f93fa5 user: larrybr tags: cli_extension) | |
Changes
Changes to ext/misc/basexx.c.
︙ | ︙ | |||
65 66 67 68 69 70 71 72 | #include "base85.c" #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_basexx_init(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){ init_api_ptr(pApi); | > > > | | | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | #include "base85.c" #ifdef _WIN32 __declspec(dllexport) #endif int sqlite3_basexx_init(sqlite3 *db, char **pzErr, const sqlite3_api_routines *pApi){ int rc1; int rc2; init_api_ptr(pApi); rc1 = BASE64_INIT(db); rc2 = BASE85_INIT(db); if( rc1==SQLITE_OK && rc2==SQLITE_OK ){ BASE64_EXPOSE(db, pzErr); BASE64_EXPOSE(db, pzErr); return SQLITE_OK; }else{ return SQLITE_ERROR; |
︙ | ︙ |
Changes to src/resmanage.c.
︙ | ︙ | |||
74 75 76 77 78 79 80 | static ResourceCount numCustomAlloc = 0; /* allocated space */ typedef void (*FreerFunction)(void *); static FreerFunction *aCustomFreers = 0; /* content of set */ const char *resmanage_oom_message = "out of memory, aborting"; /* Info recorded in support of quit_moan(...) and stack-ripping */ | | < < < > | > > | | | 74 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 102 103 104 105 106 107 | static ResourceCount numCustomAlloc = 0; /* allocated space */ typedef void (*FreerFunction)(void *); static FreerFunction *aCustomFreers = 0; /* content of set */ const char *resmanage_oom_message = "out of memory, aborting"; /* Info recorded in support of quit_moan(...) and stack-ripping */ static RipStackDest *pRipStack = 0; /* Current position of the held-resource stack */ ResourceMark holder_mark(){ return numResHold; } /* Strip resource stack then strip call stack (or exit.) */ void quit_moan(const char *zMoan, int errCode){ RipStackDest *pRSD = pRipStack; int nFreed; if( zMoan ){ fprintf(stderr, "Error: Terminating due to %s.\n", zMoan); } fprintf(stderr, "Auto-freed %d resources.\n", holder_free( (pRSD)? pRSD->resDest : 0 )); pRipStack = (pRSD)? pRSD->pPrev : 0; #ifndef SHELL_OMIT_LONGJMP if( pRSD!=0 ){ longjmp(pRSD->exeDest, errCode); } else #endif exit(errCode); } /* Free a single resource item. (ignorant of stack) */ static int free_rk( ResourceHeld *pRH ){ |
︙ | ︙ | |||
320 321 322 323 324 325 326 | numCustom = 0; numCustomAlloc = 0; } } return rv; } | < | > > | | | | | | > | | < > | 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | numCustom = 0; numCustomAlloc = 0; } } return rv; } /* Record a resource stack and call stack rip-to position */ void register_exit_ripper(RipStackDest *pRSD){ assert(pRSD!=0); pRSD->pPrev = pRipStack; pRSD->resDest = holder_mark(); pRipStack = pRSD; } /* Undo register_exit_ripper effect, back to previous state. */ void forget_exit_ripper(RipStackDest *pRSD){ if( pRSD==0 ) pRipStack = 0; else{ pRipStack = pRSD->pPrev; pRSD->pPrev = 0; } } |
Changes to src/resmanage.h.
︙ | ︙ | |||
35 36 37 38 39 40 41 | */ #ifndef RES_MANAGE_H # define RES_MANAGE_H #ifndef SHELL_OMIT_LONGJMP # include <setjmp.h> | < < < < < > > > > > > > > > > > > > > > > > > | 35 36 37 38 39 40 41 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 78 79 | */ #ifndef RES_MANAGE_H # define RES_MANAGE_H #ifndef SHELL_OMIT_LONGJMP # include <setjmp.h> #endif #include <stdio.h> #include <assert.h> #ifdef __cplusplus extern "C" { #endif #include "sqlite3.h" /* Type used for marking/conveying positions within a held-resource stack */ typedef unsigned short ResourceMark; typedef unsigned short ResourceCount; /* Type used to record a possible succession of recovery destinations */ typedef struct RipStackDest { struct RipStackDest *pPrev; ResourceMark resDest; #ifndef SHELL_OMIT_LONGJMP jmp_buf exeDest; #endif } RipStackDest; #define RIP_STACK_DEST_INIT {0} /* This macro effects stack mark and potential rip-back, keeping needed ** details in a RipStackDest object (independent of SHELL_OMIT_LONGJMP.) */ #ifndef SHELL_OMIT_LONGJMP # define RIP_TO_HERE(RSD) (RSD.resDest=holder_mark(), setjmp(RSD.exeDest)) #else # define RIP_TO_HERE(RSD) (RSD.resDest=holder_mark(), 0) #endif /* Type used for generic free functions (to fib about their signature.) */ typedef void (*GenericFreer)(void*); /* Current position of the held-resource stack */ extern ResourceMark holder_mark(); |
︙ | ︙ | |||
132 133 134 135 136 137 138 | /* Swap a held resource pointer for a new one. */ extern void* swap_held(ResourceMark mark, ResourceCount offset, void *pNew); /* Free all held resources in excess of given resource stack mark. ** Return count of number actually freed (rather than being 0.) */ extern int holder_free(ResourceMark mark); | < < < | | | | < < < < | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | /* Swap a held resource pointer for a new one. */ extern void* swap_held(ResourceMark mark, ResourceCount offset, void *pNew); /* Free all held resources in excess of given resource stack mark. ** Return count of number actually freed (rather than being 0.) */ extern int holder_free(ResourceMark mark); /* Remember execution and resource stack postion/state. This determines ** how far these stacks may be stripped should quit_moan(...) be called. */ extern void register_exit_ripper(RipStackDest *); /* Forget whatever register_exit_ripper() has been recorded. */ extern void forget_exit_ripper(RipStackDest *); /* Strip resource stack and execute previously registered longjmp() as ** previously prepared by register_exit_ripper() call. Or, if no such ** prep done (or possible), strip the whole stack and exit the process. */ extern void quit_moan(const char *zMoan, int errCode); |
︙ | ︙ |
Changes to src/shell.c.in.
︙ | ︙ | |||
16149 16150 16151 16152 16153 16154 16155 | pGlobalDbLock = 0; } } static void sayAbnormalExit(void){ if( seenInterrupt ) fprintf(stderr, "Program interrupted.\n"); } | | < > > > > > > > > > > > > > > | 16149 16150 16151 16152 16153 16154 16155 16156 16157 16158 16159 16160 16161 16162 16163 16164 16165 16166 16167 16168 16169 16170 16171 16172 16173 16174 16175 16176 16177 16178 16179 16180 16181 16182 16183 16184 16185 16186 16187 16188 16189 | pGlobalDbLock = 0; } } static void sayAbnormalExit(void){ if( seenInterrupt ) fprintf(stderr, "Program interrupted.\n"); } /* A vector of command strings collected from the command line. */ typedef struct CmdArgs { char **azCmd; /* the strings */ int nCmd; /* how many collected */ u8 bArgsHeld; /* whether "the strings" are owned by this object */ } CmdArgs; /* Data collected during args scanning. */ typedef struct ArgsData { int readStdin; /* whether stdin will be read */ int nOptsEnd; /* where -- seen, else argc */ const char *zInitFile; /* specified init file */ const char *zVfs; /* -vfs command-line option */ short bQuiet; /* -quiet option */ } ArgsData; static void freeCmdArgs(CmdArgs *pca){ int i; if( !pca ) return; if( pca->bArgsHeld ){ for( i=0; i<pca->nCmd; ++i ){ free(pca->azCmd[i]); } } free(pca->azCmd); pca->azCmd = 0; pca->nCmd = 0; } /* ** Perform CLI invocation argument processing. ** This code is collected here for convenience, to declutter main() ** and to make this processing a little simpler to understand. */ static int scanInvokeArgs(int argc, char **argv, int pass, ShellInState *psi, CmdArgs *pca, ArgsData *pad){ |
︙ | ︙ | |||
16545 16546 16547 16548 16549 16550 16551 | ShellInState datai; ShellExState datax; #endif #if SHELL_DATAIO_EXT BuiltInFFExporter ffExporter = BI_FF_EXPORTER_INIT( &datai ); BuiltInCMExporter cmExporter = BI_CM_EXPORTER_INIT( &datai ); #endif | | | | < < | > < | 16558 16559 16560 16561 16562 16563 16564 16565 16566 16567 16568 16569 16570 16571 16572 16573 16574 16575 16576 16577 16578 16579 16580 16581 16582 16583 16584 16585 16586 | ShellInState datai; ShellExState datax; #endif #if SHELL_DATAIO_EXT BuiltInFFExporter ffExporter = BI_FF_EXPORTER_INIT( &datai ); BuiltInCMExporter cmExporter = BI_CM_EXPORTER_INIT( &datai ); #endif RipStackDest mainRipDest = RIP_STACK_DEST_INIT; const char *zInitFile = 0; int bQuiet = 0; /* for testing, to suppress banner and history actions */ int i, aec; int rc = 0; DotCmdRC drc = DCR_Ok; int warnInmemoryDb = 0; /* azCmd, nCmd, bArgsHeld */ CmdArgs cmdArgs = {0,0,0}; /* readStdin, nOptsEnd, zInitFile, zVfs, bQuiet */ ArgsData argsData = { 1, argc, 0,0,0 }; #if !SQLITE_SHELL_IS_UTF8 CmdArgs argsUtf8 = {0,0,1}; AnyResourceHolder caRH = {&argsUtf8, freeCmdArgs}; #endif setvbuf(STD_ERR, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ #ifdef SQLITE_SHELL_FIDDLE stdin_is_interactive = 0; stdout_is_console = 1; datai.wasm.zDefaultDbName = "/fiddle.sqlite3"; #else |
︙ | ︙ | |||
16631 16632 16633 16634 16635 16636 16637 | /* From here on, within the true clause of this next test, various ** heap allocations are made which may fail, resulting in an abrupt ** shell exit. Such an exit happens in 1 of 2 ways: A held resource ** stack and the call stack are ripped back to this point; or just ** the held resource stack is ripped back and a process exit occurs. */ | | | > | | | | > > > | > | < < | > > > | 16642 16643 16644 16645 16646 16647 16648 16649 16650 16651 16652 16653 16654 16655 16656 16657 16658 16659 16660 16661 16662 16663 16664 16665 16666 16667 16668 16669 16670 16671 16672 16673 16674 16675 16676 16677 16678 16679 16680 16681 16682 16683 16684 16685 16686 16687 16688 | /* From here on, within the true clause of this next test, various ** heap allocations are made which may fail, resulting in an abrupt ** shell exit. Such an exit happens in 1 of 2 ways: A held resource ** stack and the call stack are ripped back to this point; or just ** the held resource stack is ripped back and a process exit occurs. */ register_exit_ripper(&mainRipDest); if( 0==RIP_TO_HERE(mainRipDest) ){ main_resource_mark = mainRipDest.resDest; /* On Windows, we must translate command-line arguments into UTF-8. ** The SQLite memory allocator subsystem has to be enabled in order to ** do this. But we want to run an sqlite3_shutdown() afterwards so that ** subsequent sqlite3_config() calls will work. So copy all results into ** memory that does not come from the SQLite memory allocator. */ #if !SQLITE_SHELL_IS_UTF8 sqlite3_initialize(); argsUtf8.azCmd = malloc(sizeof(argv[0])*argc); shell_check_oom(argsUtf8.azCmd); argsUtf8.nCmd = 0; any_ref_holder(&caRH); ++main_resource_mark; for(i=0; i<argc; i++){ char *z = sqlite3_win32_unicode_to_utf8(wargv[i]); i64 n; shell_check_oom(z); ++main_resource_mark; sstr_holder(z); n = strlen(z); argsUtf8.azCmd[i] = malloc( n+1 ); if( argsUtf8.azCmd[i] ) ++argsUtf8.nCmd; else shell_out_of_memory(); sqlite3_free(pop_holder()); --main_resource_mark; memcpy(argsUtf8.azCmd[i], z, n+1); } sqlite3_shutdown(); argv = argsUtf8.azCmd; #endif assert( argc>=1 && argv && argv[0] ); Argv0 = argv[0]; #if SHELL_DYNAMIC_EXTENSION initStartupDir(); if( isExtendedBasename(Argv0) ){ |
︙ | ︙ | |||
16910 16911 16912 16913 16914 16915 16916 | /* Unload extensions and free the DB used for dealing with them. */ sqlite3_close(datax.dbShell); /* This notification can only reach statically linked extensions. */ notify_subscribers(&datai, NK_ShutdownImminent, 0); /* Forcefull unsubscribe static extension event listeners. */ subscribe_events(&datax, 0, &datax, NK_Unsubscribe, 0); # endif | < < < < | 16927 16928 16929 16930 16931 16932 16933 16934 16935 16936 16937 16938 16939 16940 | /* Unload extensions and free the DB used for dealing with them. */ sqlite3_close(datax.dbShell); /* This notification can only reach statically linked extensions. */ notify_subscribers(&datai, NK_ShutdownImminent, 0); /* Forcefull unsubscribe static extension event listeners. */ subscribe_events(&datax, 0, &datax, NK_Unsubscribe, 0); # endif free(datax.pSpecWidths); free(datai.zNonce); for(i=0; i<datai.nSavedModes; ++i) sqlite3_free(datai.pModeStack[i]); # if SHELL_DATAIO_EXT cmExporter.pMethods->destruct((ExportHandler*)&cmExporter); ffExporter.pMethods->destruct((ExportHandler*)&ffExporter); # endif |
︙ | ︙ |
Changes to src/sqlite.h.in.
︙ | ︙ | |||
7877 7878 7879 7880 7881 7882 7883 | ** mutex must be exited an equal number of times before another thread ** can enter.)^ If the same thread tries to enter any mutex other ** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined. ** ** ^(Some systems (for example, Windows 95) do not support the operation ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() ** will always return SQLITE_BUSY. The SQLite core only ever uses | | | | | | 7877 7878 7879 7880 7881 7882 7883 7884 7885 7886 7887 7888 7889 7890 7891 7892 7893 7894 7895 7896 7897 7898 7899 7900 7901 | ** mutex must be exited an equal number of times before another thread ** can enter.)^ If the same thread tries to enter any mutex other ** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined. ** ** ^(Some systems (for example, Windows 95) do not support the operation ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() ** will always return SQLITE_BUSY. The SQLite core only ever uses ** sqlite3_mutex_try() as an optimization so this is acceptable ** behavior.)^ ** ** ^The sqlite3_mutex_leave() routine exits a mutex that was ** previously entered by the same thread. The behavior ** is undefined if the mutex is not currently entered by the ** calling thread or is not currently allocated. ** ** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), ** sqlite3_mutex_leave(), or sqlite3_mutex_free() is a NULL pointer, ** then any of the four routines behaves as a no-op. ** ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. */ sqlite3_mutex *sqlite3_mutex_alloc(int); void sqlite3_mutex_free(sqlite3_mutex*); void sqlite3_mutex_enter(sqlite3_mutex*); int sqlite3_mutex_try(sqlite3_mutex*); |
︙ | ︙ |