OLD | NEW |
| (Empty) |
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 } | |
OLD | NEW |