SQLite

Check-in [fd379f2292]
Login

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: fd379f22926d55d52176b34b20e6dda2cd1218adaaed446e4945c38a5efe0fb1
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
Unified Diff Ignore Whitespace Patch
Changes to ext/misc/basexx.c.
65
66
67
68
69
70
71



72
73
74
75
76
77
78
79
80
81
#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);
  int rc1 = BASE64_INIT(db);
  int 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;







>
>
>

|
|







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
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 ResourceMark exit_mark = 0;
#ifndef SHELL_OMIT_LONGJMP
static jmp_buf *p_exit_ripper = 0;
#endif

/* 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){

  int nFreed;
  if( zMoan ){
    fprintf(stderr, "Error: Terminating due to %s.\n", zMoan);
  }
  fprintf(stderr, "Auto-freed %d resources.\n", 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 int free_rk( ResourceHeld *pRH ){







|
<
<
<








>




|
>
>

|
|







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
327
328
329


330
331
332
333
334
335
336

337
338
339

      numCustom = 0;
      numCustomAlloc = 0;
    }
  }
  return rv;
}

#ifndef SHELL_OMIT_LONGJMP
/* Record a resource stack and call stack rip-to position */
void register_exit_ripper(jmp_buf *pjb, ResourceMark rip_mark){


  exit_mark = rip_mark;
  p_exit_ripper = pjb;
}
/* Undo register_exit_ripper effect, back to default state. */
void forget_exit_ripper(jmp_buf *pjb){
  exit_mark = 0;
  assert(p_exit_ripper == pjb);

  p_exit_ripper = 0;
}
#endif








<

|
>
>
|
|

|
|
|
|
>
|
|
<
>
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
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
*/

#ifndef RES_MANAGE_H
# define RES_MANAGE_H

#ifndef SHELL_OMIT_LONGJMP
# include <setjmp.h>
# 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 <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 for generic free functions (to fib about their signature.) */
typedef void (*GenericFreer)(void*);

/* Current position of the held-resource stack */
extern ResourceMark holder_mark();








<
<
<
<
<













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







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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/* 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);

#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);








<
<
<
|
|

|

|
<
<
<
<







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
16156
16157
16158
16159
16160

16161
16162
16163
16164
16165
16166
16167
16168
16169













16170
16171
16172
16173
16174
16175
16176
    pGlobalDbLock = 0;
  }
}
static void sayAbnormalExit(void){
  if( seenInterrupt ) fprintf(stderr, "Program interrupted.\n");
}

/* A vector of command strings collected from . */
typedef struct CmdArgs {
  /* Array is malloc'ed, but not its elements except by wmain()'s futzing. */
  char **azCmd;  /* the strings */
  int nCmd;      /* how many collected */

} 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;













/*
** 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){







|

<


>









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







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
16552
16553
16554
16555
16556
16557
16558
16559
16560
16561
16562
16563
16564
16565
16566

16567
16568
16569
16570
16571
16572
16573
16574
16575
  ShellInState datai;
  ShellExState datax;
#endif
#if SHELL_DATAIO_EXT
  BuiltInFFExporter ffExporter = BI_FF_EXPORTER_INIT( &datai );
  BuiltInCMExporter cmExporter = BI_CM_EXPORTER_INIT( &datai );
#endif
  RIP_STATE(exit_jb);
  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 */
  CmdArgs cmdArgs = {0,0};
  /* readStdin, nOptsEnd, zInitFile, zVfs, bQuiet */
  ArgsData argsData = { 1, argc, 0,0,0 };
  int nOptsEnd = argc;
#if !SQLITE_SHELL_IS_UTF8
  char **argvToFree = 0;
  int argcToFree = 0;

#endif
  main_resource_mark = holder_mark();

  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







|






|
|


<

<
|
>

<







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
16638
16639

16640
16641
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

  /* 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(&exit_jb, main_resource_mark);
  if( 0==RIP_TO_HERE(exit_jb) ){


    /* 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();
    argvToFree = malloc(sizeof(argv[0])*argc*2);
    shell_check_oom(argvToFree);
    argcToFree = argc;
    argv = argvToFree + argc;

    for(i=0; i<argc; i++){
      char *z = sqlite3_win32_unicode_to_utf8(wargv[i]);
      i64 n;
      shell_check_oom(z);


      n = strlen(z);
      argv[i] = malloc( n+1 );

      shell_check_oom(argv[i]);
      memcpy(argv[i], z, n+1);
      argvToFree[i] = argv[i];
      sqlite3_free(z);


    }
    sqlite3_shutdown();

#endif

    assert( argc>=1 && argv && argv[0] );
    Argv0 = argv[0];
#if SHELL_DYNAMIC_EXTENSION
    initStartupDir();
    if( isExtendedBasename(Argv0) ){







|
|
>









|
|
|
|
>




>
>

|
>
|
<
<
|
>
>


>







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
16917
16918
16919
16920
16921
16922
16923
16924
16925
16926
16927
  /* 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
# if !SQLITE_SHELL_IS_UTF8
  for(i=0; i<argcToFree; i++) free(argvToFree[i]);
  free(argvToFree);
# 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







<
<
<
<







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
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(), or
** sqlite3_mutex_leave() is a NULL pointer, then all three routines
** behave as no-ops.
**
** 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*);







|







|
|
|







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*);