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