From 26247e435212eae2e8082e4fa3533c28a8fd4fd8 Mon Sep 17 00:00:00 2001 From: larrybr Date: Wed, 10 May 2023 09:51:51 +0000 Subject: [PATCH] Begin use of a resource manager for CLI. FossilOrigin-Name: 1527d429d66505d4f4bb8635c4d9d7ab926037b136554605d67835ee5d19d2de --- manifest | 12 ++- manifest.uuid | 2 +- src/resmanage.c | 251 ++++++++++++++++++++++++++++++++++++++++++++++++ src/resmanage.h | 115 ++++++++++++++++++++++ 4 files changed, 374 insertions(+), 6 deletions(-) create mode 100644 src/resmanage.c create mode 100644 src/resmanage.h diff --git a/manifest b/manifest index 7933776880..75d0dd1b33 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Get\sTCL\sextension\sto\sbuild\sand\sextensible\sshell\stests\sto\spass. -D 2023-05-10T00:45:17.703 +C Begin\suse\sof\sa\sresource\smanager\sfor\sCLI. +D 2023-05-10T09:51:51.040 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -637,6 +637,8 @@ F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7 F src/prepare.c 6350675966bd0e7ac3a464af9dbfe26db6f0d4237f4e1f1acdb17b12ad371e6e F src/printf.c b9320cdbeca0b336c3f139fd36dd121e4167dd62b35fbe9ccaa9bab44c0af38d F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c +F src/resmanage.c 1af14f7ee7a1ae77f1cd21214661912d6cbd786e33e9c303e2aa272d6fd86de0 +F src/resmanage.h 63778a96198495727b40579291341fbe2a631bf8082e5be906f3a6b1e6b89f2f F src/resolve.c 3e53e02ce87c9582bd7e7d22f13f4094a271678d9dc72820fa257a2abb5e4032 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c f9333ef8181192c22662f5cb8d257efc4a2880f9ee4853c6c4616f783d27e1b5 @@ -2077,8 +2079,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 76734a4b6c61ab004703df40e2197e4f6e5c0b3d5937aed7ea41193ebe2721a1 -R e17d2c7e426cf79f5b6c9ff2d31408ec +P 05ecd8a59ec576d4afe7f8785bdcc052c0887b7a5933783a832895b8c40cdeb9 +R 87fb9edd1be1a9bd6d2d5e412cb26bac U larrybr -Z c2f2477a954fb9c881959c41cc321353 +Z 1047f910d7d5ef2b2fd5d09f9bd5a949 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 938c02edf9..fa3275e3f6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -05ecd8a59ec576d4afe7f8785bdcc052c0887b7a5933783a832895b8c40cdeb9 \ No newline at end of file +1527d429d66505d4f4bb8635c4d9d7ab926037b136554605d67835ee5d19d2de \ No newline at end of file diff --git a/src/resmanage.c b/src/resmanage.c new file mode 100644 index 0000000000..f9aeda3327 --- /dev/null +++ b/src/resmanage.c @@ -0,0 +1,251 @@ +/* +** 2023 May 8 +** +** 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. +** +************************************************************************* +** This file implements a simple resource management package, interface +** and function of which is described in resmanage.h . +*/ + +#include +#include + +#include +#include "resmanage.h" + +/* Track how to free various held resources. */ +typedef enum FreeableResourceKind { + FRK_Malloc = 0, FRK_DbConn = 1, FRK_DbStmt = 2, + FRK_DbMem = 3, FRK_File = 4, +#if (!defined(_WIN32) && !defined(WIN32)) || !SQLITE_OS_WINRT + FRK_Pipe, +#endif + FRK_CustomBase /* series of values for custom freers */ +} FreeableResourceKind; + +#if defined(_WIN32) || defined(WIN32) +# if !SQLITE_OS_WINRT +# include +# include +# undef pclose +# define pclose _pclose +# endif +#else +extern int pclose(FILE*); +#endif + +typedef struct ResourceHeld { + union { + void *p_any; + void *p_malloced; + sqlite3 *p_conn; + sqlite3_stmt *p_stmt; + void *p_s3mem; + FILE *p_stream; + } held; + FreeableResourceKind frk; +} ResourceHeld; + +/* The held-resource stack. This is for single-threaded use only. */ +static ResourceHeld *pResHold = 0; +static unsigned short numResHold = 0; +static unsigned short numResAlloc = 0; + +/* A small set of custom freers. It is linearly searched, used for +** layered heap-allocated (and other-allocated) data structures, so +** tends to have use limited to where slow things are happening. +*/ +static unsigned short numCustom = 0; /* number of the set */ +static unsigned short numCustomAlloc = 0; /* allocated space */ +typedef void (*FreerFunction)(void *); +static FreerFunction *aCustomFreers = 0; /* content of set */ + +/* Info recorded in support of quit_moan(...) and stack-ripping */ +static ResourceMark exit_mark = 0; +#ifndef SHELL_OMIT_LONGJMP +static jmp_buf *p_exit_ripper = 0; +#endif + +/* Implementation per header comment */ +ResourceMark holder_mark(){ + return numResHold; +} + +/* Implementation per header comment */ +void quit_moan(const char *zMoan, int errCode){ + if( zMoan ){ + fprintf(stderr, "Quitting due to %s, freeing %d resources.\n", + zMoan, numResHold); + } + holder_free(exit_mark); +#ifndef SHELL_OMIT_LONGJMP + if( p_exit_ripper!=0 ){ + longjmp(*p_exit_ripper, errCode); + } else +#endif + exit(errCode); +} + +/* Free a single resource item. (ignorant of stack) */ +static void free_rk( ResourceHeld *pRH ){ + if( pRH->held.p_any == 0 ) return; + switch( pRH->frk ){ + case FRK_Malloc: + free(pRH->held.p_malloced); + break; + case FRK_DbConn: + sqlite3_close_v2(pRH->held.p_conn); + break; + case FRK_DbStmt: + sqlite3_clear_bindings(pRH->held.p_stmt); + sqlite3_finalize(pRH->held.p_stmt); + break; + case FRK_DbMem: + sqlite3_free(pRH->held.p_s3mem); + break; + case FRK_File: + fclose(pRH->held.p_stream); + break; +#if (!defined(_WIN32) && !defined(WIN32)) || !SQLITE_OS_WINRT + case FRK_Pipe: + pclose(pRH->held.p_stream); + break; +#endif + default: + { + int ck = pRH->frk - FRK_CustomBase; + assert(ck>=0); + if( ck < numCustom ){ + aCustomFreers[ck]( pRH->held.p_any ); + } + } + } + pRH->held.p_any = 0; +} + +void* take_held(ResourceMark mark, unsigned short offset){ + unsigned short rix = mark + offset; + if( rix < numResHold && numResHold > 0 ){ + void *rv = pResHold[rix].held.p_any; + pResHold[rix].held.p_any = 0; + return rv; + }else return 0; +} + +/* Shared resource-stack pushing code */ +static void res_hold(void *pv, FreeableResourceKind frk){ + ResourceHeld rh = { pv, frk }; + if( numResHold == numResAlloc ){ + size_t nrn = numResAlloc + (numResAlloc>>2) + 5; + ResourceHeld *prh; + prh = (ResourceHeld*)realloc(pResHold, nrn*sizeof(ResourceHeld)); + if( prh!=0 ){ + pResHold = prh; + numResAlloc = nrn; + }else{ + quit_moan("Out of memory",1); + } + } + pResHold[numResHold++] = rh; +} + +/* Implementation per header comment */ +void* mmem_holder(void *pm){ + res_hold(pm, FRK_Malloc); + return pm; +} +/* Implementation per header comment */ +char* mstr_holder(char *z){ + if( z!=0 ) res_hold(z, FRK_Malloc); + return z; +} +/* Implementation per header comment */ +char* sstr_holder(char *z){ + if( z!=0 ) res_hold(z, FRK_DbMem); + return z; +} +/* Implementation per header comment */ +void file_holder(FILE *pf){ + if( pf!=0 ) res_hold(pf, FRK_File); +} +#if (!defined(_WIN32) && !defined(WIN32)) || !SQLITE_OS_WINRT +/* Implementation per header comment */ +void pipe_holder(FILE *pp){ + if( pp!=0 ) res_hold(pp, FRK_Pipe); +} +#endif +/* Implementation per header comment */ +void* any_holder(void *pm, void (*its_freer)(void*)){ + int i = 0; + while( i < numCustom ){ + if( its_freer == aCustomFreers[i] ) break; + ++i; + } + if( i == numCustom ){ + size_t ncf = numCustom + 2; + FreerFunction *pcf; + pcf = (FreerFunction *)realloc(aCustomFreers, ncf*sizeof(FreerFunction)); + if( pcf!=0 ){ + numCustomAlloc = ncf; + aCustomFreers = pcf; + aCustomFreers[numCustom++] = its_freer; + }else{ + quit_moan("Out of memory",1); + } + } + res_hold(pm, i + FRK_CustomBase); + return pm; +} +/* Implementation per header comment */ +void* smem_holder(void *pm){ + res_hold(pm, FRK_DbMem); + return pm; +} +/* Implementation per header comment */ +void conn_holder(sqlite3 *pdb){ + res_hold(pdb, FRK_DbConn); +} +/* Implementation per header comment */ +void stmt_holder(sqlite3_stmt *pstmt){ + res_hold(pstmt, FRK_DbStmt); +} + +/* Implementation per header comment */ +void holder_free(ResourceMark mark){ + while( numResHold > mark ){ + free_rk(&pResHold[--numResHold]); + } + if( mark==0 ){ + if( numResAlloc>0 ){ + free(pResHold); + pResHold = 0; + numResAlloc = 0; + } + if( numCustomAlloc>0 ){ + free(aCustomFreers); + aCustomFreers = 0; + numCustom = 0; + numCustomAlloc = 0; + } + } +} + +#ifndef SHELL_OMIT_LONGJMP +/* Implementation per header comment */ +void register_exit_ripper(jmp_buf *pjb, ResourceMark rip_mark){ + exit_mark = rip_mark; + p_exit_ripper = pjb; +} +/* Implementation per header comment */ +void forget_exit_ripper(jmp_buf *pjb){ + exit_mark = 0; + assert(p_exit_ripper == pjb); + p_exit_ripper = 0; +} +#endif diff --git a/src/resmanage.h b/src/resmanage.h new file mode 100644 index 0000000000..a43bee0b97 --- /dev/null +++ b/src/resmanage.h @@ -0,0 +1,115 @@ +/* +** 2023 May 8 +** +** 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. +** +************************************************************************* +** This file declares the interface of a simple resource management package +** which supports freeing of resources upon an abrupt, program-initiated +** termination of a function from somewhere in a call tree. The package is +** designed to be used with setjmp()/longjmp() to effect a pattern similar +** to the try/throw/catch available in some programming languages. (But see +** below regarding usage when the setmp()/longjmp() pair is unavailable.) +** +** The general scheme is that routines whose pre-return code might be +** bypassed, thereby leaking resources, do not rely on pre-return code +** to release locally held resources. Instead, they give ownership of +** such resources to this package, via xxxx_holder(...) calls, and use +** its holder_mark() and holder_free(...) functions to release locally +** acquired resources. +** +** For environments where setmp()/longjmp() are unavailable, (indicated by +** SHELL_OMIT_LONGJMP defined), the package substitutes a process exit for +** resumption of execution at a chosen code location. The resources in the +** package's held resource stack are still released. And the ability to +** free locally acquired resources as functions return is retained. This +** can simplify early function exit logic, but a body of code relying on +** process exit to ease error handling is unsuitable for use as a called +** routine within a larger application. That use is most of the reason for +** this package's existence. +*/ + +#ifndef RES_MANAGE_H +# define RES_MANAGE_H + +#ifndef SHELL_OMIT_LONGJMP +# include +# define RIP_STATE(jb) jmp_buf jb +# define RIP_TO_HERE(jb) setjmp(jb) +#else +# define RIP_STATE(jb) +# define RIP_TO_HERE(jb) 0 +#endif +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sqlite3.h" + +/* Type used for marking positions within a held-resource stack */ +typedef unsigned short ResourceMark; + +/* Current position of the held-resource stack */ +extern ResourceMark holder_mark(); + +/* Routines for holding resources on held-resource stack together +** with enough information for them to be freed by this package. +*/ +/* anything together with arbitrary freeing function */ +extern void* any_holder(void *pm, void (*its_freer)(void*)); +/* anything in the malloc() heap */ +extern void* mmem_holder(void *pm); +/* a C string in the malloc() heap */ +extern char* mstr_holder(char *z); +/* some SQLite-allocated memory */ +extern void* smem_holder(void *pm); +/* a C string in the SQLite heap */ +extern char* sstr_holder(char *z); +/* a SQLite database "connection" */ +extern void conn_holder(sqlite3 *pdb); +/* a SQLite prepared statement */ +extern void stmt_holder(sqlite3_stmt *pstmt); +/* an open C runtime FILE */ +extern void file_holder(FILE *); +#if (!defined(_WIN32) && !defined(WIN32)) || !SQLITE_OS_WINRT +/* an open C runtime pipe */ +extern void pipe_holder(FILE *); +#endif + +/* Take back a held resource pointer, leaving held as NULL. (no-op) */ +extern void* take_held(ResourceMark mark, unsigned short offset); + +/* Free all held resources in excess of given resource stack mark. */ +extern void holder_free(ResourceMark mark); + +#ifndef SHELL_OMIT_LONGJMP +/* Remember a longjmp() destination together with a resource stack +** mark which determines how far the resource stack may be stripped. +** This info determines how far the execution and resource stacks +** will be stripped back should quit_moan(...) be called. +*/ +extern void register_exit_ripper(jmp_buf *pjb, ResourceMark rip_mark); +/* Forget whatever register_exit_ripper() has been recorded. */ +extern void forget_exit_ripper(jmp_buf *pjb); +#else +#define register_exit_ripper(jb, rm) +#define forget_exit_ripper() +#endif + +/* 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); + +#ifdef __cplusplus +} /* End of the 'extern "C"' block */ +#endif +#endif /* RES_MANAGE_H */ -- 2.47.3