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