xref: /sqlite-3.40.0/src/test_mutex.c (revision 7aa3ebee)
1 /*
2 ** 2008 June 18
3 **
4 ** The author disclaims copyright to this source code.  In place of
5 ** a legal notice, here is a blessing:
6 **
7 **    May you do good and not evil.
8 **    May you find forgiveness for yourself and forgive others.
9 **    May you share freely, never taking more than you give.
10 **
11 *************************************************************************
12 ** This file contains test logic for the sqlite3_mutex interfaces.
13 */
14 
15 #include "tcl.h"
16 #include "sqlite3.h"
17 #include "sqliteInt.h"
18 #include <stdlib.h>
19 #include <assert.h>
20 #include <string.h>
21 
22 #define MAX_MUTEXES        (SQLITE_MUTEX_STATIC_VFS3+1)
23 #define STATIC_MUTEXES     (MAX_MUTEXES-(SQLITE_MUTEX_RECURSIVE+1))
24 
25 /* defined in main.c */
26 extern const char *sqlite3ErrName(int);
27 
28 static const char *aName[MAX_MUTEXES+1] = {
29   "fast",        "recursive",   "static_master", "static_mem",
30   "static_open", "static_prng", "static_lru",    "static_pmem",
31   "static_app1", "static_app2", "static_app3",   "static_vfs1",
32   "static_vfs2", "static_vfs3", 0
33 };
34 
35 /* A countable mutex */
36 struct sqlite3_mutex {
37   sqlite3_mutex *pReal;
38   int eType;
39 };
40 
41 /* State variables */
42 static struct test_mutex_globals {
43   int isInstalled;           /* True if installed */
44   int disableInit;           /* True to cause sqlite3_initalize() to fail */
45   int disableTry;            /* True to force sqlite3_mutex_try() to fail */
46   int isInit;                /* True if initialized */
47   sqlite3_mutex_methods m;   /* Interface to "real" mutex system */
48   int aCounter[MAX_MUTEXES]; /* Number of grabs of each type of mutex */
49   sqlite3_mutex aStatic[STATIC_MUTEXES]; /* The static mutexes */
50 } g = {0};
51 
52 /* Return true if the countable mutex is currently held */
53 static int counterMutexHeld(sqlite3_mutex *p){
54   return g.m.xMutexHeld(p->pReal);
55 }
56 
57 /* Return true if the countable mutex is not currently held */
58 static int counterMutexNotheld(sqlite3_mutex *p){
59   return g.m.xMutexNotheld(p->pReal);
60 }
61 
62 /* Initialize the countable mutex interface
63 ** Or, if g.disableInit is non-zero, then do not initialize but instead
64 ** return the value of g.disableInit as the result code.  This can be used
65 ** to simulate an initialization failure.
66 */
67 static int counterMutexInit(void){
68   int rc;
69   if( g.disableInit ) return g.disableInit;
70   rc = g.m.xMutexInit();
71   g.isInit = 1;
72   return rc;
73 }
74 
75 /*
76 ** Uninitialize the mutex subsystem
77 */
78 static int counterMutexEnd(void){
79   g.isInit = 0;
80   return g.m.xMutexEnd();
81 }
82 
83 /*
84 ** Allocate a countable mutex
85 */
86 static sqlite3_mutex *counterMutexAlloc(int eType){
87   sqlite3_mutex *pReal;
88   sqlite3_mutex *pRet = 0;
89 
90   assert( g.isInit );
91   assert( eType>=SQLITE_MUTEX_FAST );
92   assert( eType<=SQLITE_MUTEX_STATIC_VFS3 );
93 
94   pReal = g.m.xMutexAlloc(eType);
95   if( !pReal ) return 0;
96 
97   if( eType==SQLITE_MUTEX_FAST || eType==SQLITE_MUTEX_RECURSIVE ){
98     pRet = (sqlite3_mutex *)malloc(sizeof(sqlite3_mutex));
99   }else{
100     int eStaticType = eType - (MAX_MUTEXES - STATIC_MUTEXES);
101     assert( eStaticType>=0 );
102     assert( eStaticType<STATIC_MUTEXES );
103     pRet = &g.aStatic[eStaticType];
104   }
105 
106   pRet->eType = eType;
107   pRet->pReal = pReal;
108   return pRet;
109 }
110 
111 /*
112 ** Free a countable mutex
113 */
114 static void counterMutexFree(sqlite3_mutex *p){
115   assert( g.isInit );
116   g.m.xMutexFree(p->pReal);
117   if( p->eType==SQLITE_MUTEX_FAST || p->eType==SQLITE_MUTEX_RECURSIVE ){
118     free(p);
119   }
120 }
121 
122 /*
123 ** Enter a countable mutex.  Block until entry is safe.
124 */
125 static void counterMutexEnter(sqlite3_mutex *p){
126   assert( g.isInit );
127   assert( p->eType>=0 );
128   assert( p->eType<MAX_MUTEXES );
129   g.aCounter[p->eType]++;
130   g.m.xMutexEnter(p->pReal);
131 }
132 
133 /*
134 ** Try to enter a mutex.  Return true on success.
135 */
136 static int counterMutexTry(sqlite3_mutex *p){
137   assert( g.isInit );
138   assert( p->eType>=0 );
139   assert( p->eType<MAX_MUTEXES );
140   g.aCounter[p->eType]++;
141   if( g.disableTry ) return SQLITE_BUSY;
142   return g.m.xMutexTry(p->pReal);
143 }
144 
145 /* Leave a mutex
146 */
147 static void counterMutexLeave(sqlite3_mutex *p){
148   assert( g.isInit );
149   g.m.xMutexLeave(p->pReal);
150 }
151 
152 /*
153 ** sqlite3_shutdown
154 */
155 static int test_shutdown(
156   void * clientData,
157   Tcl_Interp *interp,
158   int objc,
159   Tcl_Obj *CONST objv[]
160 ){
161   int rc;
162 
163   if( objc!=1 ){
164     Tcl_WrongNumArgs(interp, 1, objv, "");
165     return TCL_ERROR;
166   }
167 
168   rc = sqlite3_shutdown();
169   Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
170   return TCL_OK;
171 }
172 
173 /*
174 ** sqlite3_initialize
175 */
176 static int test_initialize(
177   void * clientData,
178   Tcl_Interp *interp,
179   int objc,
180   Tcl_Obj *CONST objv[]
181 ){
182   int rc;
183 
184   if( objc!=1 ){
185     Tcl_WrongNumArgs(interp, 1, objv, "");
186     return TCL_ERROR;
187   }
188 
189   rc = sqlite3_initialize();
190   Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
191   return TCL_OK;
192 }
193 
194 /*
195 ** install_mutex_counters BOOLEAN
196 */
197 static int test_install_mutex_counters(
198   void * clientData,
199   Tcl_Interp *interp,
200   int objc,
201   Tcl_Obj *CONST objv[]
202 ){
203   int rc = SQLITE_OK;
204   int isInstall;
205 
206   sqlite3_mutex_methods counter_methods = {
207     counterMutexInit,
208     counterMutexEnd,
209     counterMutexAlloc,
210     counterMutexFree,
211     counterMutexEnter,
212     counterMutexTry,
213     counterMutexLeave,
214     counterMutexHeld,
215     counterMutexNotheld
216   };
217 
218   if( objc!=2 ){
219     Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
220     return TCL_ERROR;
221   }
222   if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){
223     return TCL_ERROR;
224   }
225 
226   assert(isInstall==0 || isInstall==1);
227   assert(g.isInstalled==0 || g.isInstalled==1);
228   if( isInstall==g.isInstalled ){
229     Tcl_AppendResult(interp, "mutex counters are ", 0);
230     Tcl_AppendResult(interp, isInstall?"already installed":"not installed", 0);
231     return TCL_ERROR;
232   }
233 
234   if( isInstall ){
235     assert( g.m.xMutexAlloc==0 );
236     rc = sqlite3_config(SQLITE_CONFIG_GETMUTEX, &g.m);
237     if( rc==SQLITE_OK ){
238       sqlite3_config(SQLITE_CONFIG_MUTEX, &counter_methods);
239     }
240     g.disableTry = 0;
241   }else{
242     assert( g.m.xMutexAlloc );
243     rc = sqlite3_config(SQLITE_CONFIG_MUTEX, &g.m);
244     memset(&g.m, 0, sizeof(sqlite3_mutex_methods));
245   }
246 
247   if( rc==SQLITE_OK ){
248     g.isInstalled = isInstall;
249   }
250 
251   Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
252   return TCL_OK;
253 }
254 
255 /*
256 ** read_mutex_counters
257 */
258 static int test_read_mutex_counters(
259   void * clientData,
260   Tcl_Interp *interp,
261   int objc,
262   Tcl_Obj *CONST objv[]
263 ){
264   Tcl_Obj *pRet;
265   int ii;
266 
267   if( objc!=1 ){
268     Tcl_WrongNumArgs(interp, 1, objv, "");
269     return TCL_ERROR;
270   }
271 
272   pRet = Tcl_NewObj();
273   Tcl_IncrRefCount(pRet);
274   for(ii=0; ii<MAX_MUTEXES; ii++){
275     Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(aName[ii], -1));
276     Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(g.aCounter[ii]));
277   }
278   Tcl_SetObjResult(interp, pRet);
279   Tcl_DecrRefCount(pRet);
280 
281   return TCL_OK;
282 }
283 
284 /*
285 ** clear_mutex_counters
286 */
287 static int test_clear_mutex_counters(
288   void * clientData,
289   Tcl_Interp *interp,
290   int objc,
291   Tcl_Obj *CONST objv[]
292 ){
293   int ii;
294 
295   if( objc!=1 ){
296     Tcl_WrongNumArgs(interp, 1, objv, "");
297     return TCL_ERROR;
298   }
299 
300   for(ii=0; ii<MAX_MUTEXES; ii++){
301     g.aCounter[ii] = 0;
302   }
303   return TCL_OK;
304 }
305 
306 /*
307 ** Create and free a mutex.  Return the mutex pointer.  The pointer
308 ** will be invalid since the mutex has already been freed.  The
309 ** return pointer just checks to see if the mutex really was allocated.
310 */
311 static int test_alloc_mutex(
312   void * clientData,
313   Tcl_Interp *interp,
314   int objc,
315   Tcl_Obj *CONST objv[]
316 ){
317 #if SQLITE_THREADSAFE
318   sqlite3_mutex *p = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
319   char zBuf[100];
320   sqlite3_mutex_free(p);
321   sqlite3_snprintf(sizeof(zBuf), zBuf, "%p", p);
322   Tcl_AppendResult(interp, zBuf, (char*)0);
323 #endif
324   return TCL_OK;
325 }
326 
327 /*
328 ** sqlite3_config OPTION
329 **
330 ** OPTION can be either one of the keywords:
331 **
332 **            SQLITE_CONFIG_SINGLETHREAD
333 **            SQLITE_CONFIG_MULTITHREAD
334 **            SQLITE_CONFIG_SERIALIZED
335 **
336 ** Or OPTION can be an raw integer.
337 */
338 static int test_config(
339   void * clientData,
340   Tcl_Interp *interp,
341   int objc,
342   Tcl_Obj *CONST objv[]
343 ){
344   struct ConfigOption {
345     const char *zName;
346     int iValue;
347   } aOpt[] = {
348     {"singlethread", SQLITE_CONFIG_SINGLETHREAD},
349     {"multithread",  SQLITE_CONFIG_MULTITHREAD},
350     {"serialized",   SQLITE_CONFIG_SERIALIZED},
351     {0, 0}
352   };
353   int s = sizeof(struct ConfigOption);
354   int i;
355   int rc;
356 
357   if( objc!=2 ){
358     Tcl_WrongNumArgs(interp, 1, objv, "");
359     return TCL_ERROR;
360   }
361 
362   if( Tcl_GetIndexFromObjStruct(interp, objv[1], aOpt, s, "flag", 0, &i) ){
363     if( Tcl_GetIntFromObj(interp, objv[1], &i) ){
364       return TCL_ERROR;
365     }
366   }else{
367     i = aOpt[i].iValue;
368   }
369 
370   rc = sqlite3_config(i);
371   Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
372   return TCL_OK;
373 }
374 
375 static sqlite3 *getDbPointer(Tcl_Interp *pInterp, Tcl_Obj *pObj){
376   sqlite3 *db;
377   Tcl_CmdInfo info;
378   char *zCmd = Tcl_GetString(pObj);
379   if( Tcl_GetCommandInfo(pInterp, zCmd, &info) ){
380     db = *((sqlite3 **)info.objClientData);
381   }else{
382     db = (sqlite3*)sqlite3TestTextToPtr(zCmd);
383   }
384   assert( db );
385   return db;
386 }
387 
388 static sqlite3_mutex *getStaticMutexPointer(
389   Tcl_Interp *pInterp,
390   Tcl_Obj *pObj
391 ){
392   int iMutex;
393   if( Tcl_GetIndexFromObj(pInterp, pObj, aName, "mutex name", 0, &iMutex) ){
394     return 0;
395   }
396   assert( iMutex!=SQLITE_MUTEX_FAST && iMutex!=SQLITE_MUTEX_RECURSIVE );
397   return counterMutexAlloc(iMutex);
398 }
399 
400 static int test_enter_static_mutex(
401   void * clientData,
402   Tcl_Interp *interp,
403   int objc,
404   Tcl_Obj *CONST objv[]
405 ){
406   sqlite3_mutex *pMutex;
407   if( objc!=2 ){
408     Tcl_WrongNumArgs(interp, 1, objv, "NAME");
409     return TCL_ERROR;
410   }
411   pMutex = getStaticMutexPointer(interp, objv[1]);
412   if( !pMutex ){
413     return TCL_ERROR;
414   }
415   sqlite3_mutex_enter(pMutex);
416   return TCL_OK;
417 }
418 
419 static int test_leave_static_mutex(
420   void * clientData,
421   Tcl_Interp *interp,
422   int objc,
423   Tcl_Obj *CONST objv[]
424 ){
425   sqlite3_mutex *pMutex;
426   if( objc!=2 ){
427     Tcl_WrongNumArgs(interp, 1, objv, "NAME");
428     return TCL_ERROR;
429   }
430   pMutex = getStaticMutexPointer(interp, objv[1]);
431   if( !pMutex ){
432     return TCL_ERROR;
433   }
434   sqlite3_mutex_leave(pMutex);
435   return TCL_OK;
436 }
437 
438 static int test_enter_db_mutex(
439   void * clientData,
440   Tcl_Interp *interp,
441   int objc,
442   Tcl_Obj *CONST objv[]
443 ){
444   sqlite3 *db;
445   if( objc!=2 ){
446     Tcl_WrongNumArgs(interp, 1, objv, "DB");
447     return TCL_ERROR;
448   }
449   db = getDbPointer(interp, objv[1]);
450   if( !db ){
451     return TCL_ERROR;
452   }
453   sqlite3_mutex_enter(sqlite3_db_mutex(db));
454   return TCL_OK;
455 }
456 
457 static int test_leave_db_mutex(
458   void * clientData,
459   Tcl_Interp *interp,
460   int objc,
461   Tcl_Obj *CONST objv[]
462 ){
463   sqlite3 *db;
464   if( objc!=2 ){
465     Tcl_WrongNumArgs(interp, 1, objv, "DB");
466     return TCL_ERROR;
467   }
468   db = getDbPointer(interp, objv[1]);
469   if( !db ){
470     return TCL_ERROR;
471   }
472   sqlite3_mutex_leave(sqlite3_db_mutex(db));
473   return TCL_OK;
474 }
475 
476 int Sqlitetest_mutex_Init(Tcl_Interp *interp){
477   static struct {
478     char *zName;
479     Tcl_ObjCmdProc *xProc;
480   } aCmd[] = {
481     { "sqlite3_shutdown",        (Tcl_ObjCmdProc*)test_shutdown },
482     { "sqlite3_initialize",      (Tcl_ObjCmdProc*)test_initialize },
483     { "sqlite3_config",          (Tcl_ObjCmdProc*)test_config },
484 
485     { "enter_static_mutex",      (Tcl_ObjCmdProc*)test_enter_static_mutex },
486     { "leave_static_mutex",      (Tcl_ObjCmdProc*)test_leave_static_mutex },
487 
488     { "enter_db_mutex",          (Tcl_ObjCmdProc*)test_enter_db_mutex },
489     { "leave_db_mutex",          (Tcl_ObjCmdProc*)test_leave_db_mutex },
490 
491     { "alloc_dealloc_mutex",     (Tcl_ObjCmdProc*)test_alloc_mutex },
492     { "install_mutex_counters",  (Tcl_ObjCmdProc*)test_install_mutex_counters },
493     { "read_mutex_counters",     (Tcl_ObjCmdProc*)test_read_mutex_counters },
494     { "clear_mutex_counters",    (Tcl_ObjCmdProc*)test_clear_mutex_counters },
495   };
496   int i;
497   for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
498     Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
499   }
500 
501   Tcl_LinkVar(interp, "disable_mutex_init",
502               (char*)&g.disableInit, TCL_LINK_INT);
503   Tcl_LinkVar(interp, "disable_mutex_try",
504               (char*)&g.disableTry, TCL_LINK_INT);
505   return SQLITE_OK;
506 }
507