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