Add tests for the new code on this branch.
[sqlite.git] / src / test_mutex.c
bloba203208abe7172dd391ef5b746068442c04a52e6
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.
11 *************************************************************************
12 ** This file contains test logic for the sqlite3_mutex interfaces.
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>
26 #define MAX_MUTEXES (SQLITE_MUTEX_STATIC_VFS3+1)
27 #define STATIC_MUTEXES (MAX_MUTEXES-(SQLITE_MUTEX_RECURSIVE+1))
29 /* defined in main.c */
30 extern const char *sqlite3ErrName(int);
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
39 /* A countable mutex */
40 struct sqlite3_mutex {
41 sqlite3_mutex *pReal;
42 int eType;
45 /* State variables */
46 static struct test_mutex_globals {
47 int isInstalled; /* True if installed */
48 int disableInit; /* True to cause sqlite3_initialize() 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};
56 /* Return true if the countable mutex is currently held */
57 static int counterMutexHeld(sqlite3_mutex *p){
58 return g.m.xMutexHeld(p->pReal);
61 /* Return true if the countable mutex is not currently held */
62 static int counterMutexNotheld(sqlite3_mutex *p){
63 return g.m.xMutexNotheld(p->pReal);
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.
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;
80 ** Uninitialize the mutex subsystem
82 static int counterMutexEnd(void){
83 g.isInit = 0;
84 return g.m.xMutexEnd();
88 ** Allocate a countable mutex
90 static sqlite3_mutex *counterMutexAlloc(int eType){
91 sqlite3_mutex *pReal;
92 sqlite3_mutex *pRet = 0;
94 assert( g.isInit );
95 assert( eType>=SQLITE_MUTEX_FAST );
96 assert( eType<=SQLITE_MUTEX_STATIC_VFS3 );
98 pReal = g.m.xMutexAlloc(eType);
99 if( !pReal ) return 0;
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];
110 pRet->eType = eType;
111 pRet->pReal = pReal;
112 return pRet;
116 ** Free a countable mutex
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);
127 ** Enter a countable mutex. Block until entry is safe.
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);
138 ** Try to enter a mutex. Return true on success.
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);
149 /* Leave a mutex
151 static void counterMutexLeave(sqlite3_mutex *p){
152 assert( g.isInit );
153 g.m.xMutexLeave(p->pReal);
157 ** sqlite3_shutdown
159 static int SQLITE_TCLAPI test_shutdown(
160 void * clientData,
161 Tcl_Interp *interp,
162 int objc,
163 Tcl_Obj *CONST objv[]
165 int rc;
167 if( objc!=1 ){
168 Tcl_WrongNumArgs(interp, 1, objv, "");
169 return TCL_ERROR;
172 rc = sqlite3_shutdown();
173 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
174 return TCL_OK;
178 ** sqlite3_initialize
180 static int SQLITE_TCLAPI test_initialize(
181 void * clientData,
182 Tcl_Interp *interp,
183 int objc,
184 Tcl_Obj *CONST objv[]
186 int rc;
188 if( objc!=1 ){
189 Tcl_WrongNumArgs(interp, 1, objv, "");
190 return TCL_ERROR;
193 rc = sqlite3_initialize();
194 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
195 return TCL_OK;
199 ** install_mutex_counters BOOLEAN
201 static int SQLITE_TCLAPI test_install_mutex_counters(
202 void * clientData,
203 Tcl_Interp *interp,
204 int objc,
205 Tcl_Obj *CONST objv[]
207 int rc = SQLITE_OK;
208 int isInstall;
210 sqlite3_mutex_methods counter_methods = {
211 counterMutexInit,
212 counterMutexEnd,
213 counterMutexAlloc,
214 counterMutexFree,
215 counterMutexEnter,
216 counterMutexTry,
217 counterMutexLeave,
218 counterMutexHeld,
219 counterMutexNotheld
222 if( objc!=2 ){
223 Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN");
224 return TCL_ERROR;
226 if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[1], &isInstall) ){
227 return TCL_ERROR;
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;
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);
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));
251 if( rc==SQLITE_OK ){
252 g.isInstalled = isInstall;
255 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
256 return TCL_OK;
260 ** read_mutex_counters
262 static int SQLITE_TCLAPI test_read_mutex_counters(
263 void * clientData,
264 Tcl_Interp *interp,
265 int objc,
266 Tcl_Obj *CONST objv[]
268 Tcl_Obj *pRet;
269 int ii;
271 if( objc!=1 ){
272 Tcl_WrongNumArgs(interp, 1, objv, "");
273 return TCL_ERROR;
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]));
282 Tcl_SetObjResult(interp, pRet);
283 Tcl_DecrRefCount(pRet);
285 return TCL_OK;
289 ** clear_mutex_counters
291 static int SQLITE_TCLAPI test_clear_mutex_counters(
292 void * clientData,
293 Tcl_Interp *interp,
294 int objc,
295 Tcl_Obj *CONST objv[]
297 int ii;
299 if( objc!=1 ){
300 Tcl_WrongNumArgs(interp, 1, objv, "");
301 return TCL_ERROR;
304 for(ii=0; ii<MAX_MUTEXES; ii++){
305 g.aCounter[ii] = 0;
307 return TCL_OK;
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.
315 static int SQLITE_TCLAPI test_alloc_mutex(
316 void * clientData,
317 Tcl_Interp *interp,
318 int objc,
319 Tcl_Obj *CONST objv[]
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;
332 ** sqlite3_config OPTION
334 ** OPTION can be either one of the keywords:
336 ** SQLITE_CONFIG_SINGLETHREAD
337 ** SQLITE_CONFIG_MULTITHREAD
338 ** SQLITE_CONFIG_SERIALIZED
340 ** Or OPTION can be an raw integer.
342 static int SQLITE_TCLAPI test_config(
343 void * clientData,
344 Tcl_Interp *interp,
345 int objc,
346 Tcl_Obj *CONST objv[]
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}
357 int s = sizeof(struct ConfigOption);
358 int i;
359 int rc;
361 if( objc!=2 ){
362 Tcl_WrongNumArgs(interp, 1, objv, "");
363 return TCL_ERROR;
366 if( Tcl_GetIndexFromObjStruct(interp, objv[1], aOpt, s, "flag", 0, &i) ){
367 if( Tcl_GetIntFromObj(interp, objv[1], &i) ){
368 return TCL_ERROR;
370 }else{
371 i = aOpt[i].iValue;
374 rc = sqlite3_config(i);
375 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_VOLATILE);
376 return TCL_OK;
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);
388 assert( db );
389 return db;
392 static sqlite3_mutex *getStaticMutexPointer(
393 Tcl_Interp *pInterp,
394 Tcl_Obj *pObj
396 int iMutex;
397 if( Tcl_GetIndexFromObj(pInterp, pObj, aName, "mutex name", 0, &iMutex) ){
398 return 0;
400 assert( iMutex!=SQLITE_MUTEX_FAST && iMutex!=SQLITE_MUTEX_RECURSIVE );
401 return counterMutexAlloc(iMutex);
404 static int SQLITE_TCLAPI test_enter_static_mutex(
405 void * clientData,
406 Tcl_Interp *interp,
407 int objc,
408 Tcl_Obj *CONST objv[]
410 sqlite3_mutex *pMutex;
411 if( objc!=2 ){
412 Tcl_WrongNumArgs(interp, 1, objv, "NAME");
413 return TCL_ERROR;
415 pMutex = getStaticMutexPointer(interp, objv[1]);
416 if( !pMutex ){
417 return TCL_ERROR;
419 sqlite3_mutex_enter(pMutex);
420 return TCL_OK;
423 static int SQLITE_TCLAPI test_leave_static_mutex(
424 void * clientData,
425 Tcl_Interp *interp,
426 int objc,
427 Tcl_Obj *CONST objv[]
429 sqlite3_mutex *pMutex;
430 if( objc!=2 ){
431 Tcl_WrongNumArgs(interp, 1, objv, "NAME");
432 return TCL_ERROR;
434 pMutex = getStaticMutexPointer(interp, objv[1]);
435 if( !pMutex ){
436 return TCL_ERROR;
438 sqlite3_mutex_leave(pMutex);
439 return TCL_OK;
442 static int SQLITE_TCLAPI test_enter_db_mutex(
443 void * clientData,
444 Tcl_Interp *interp,
445 int objc,
446 Tcl_Obj *CONST objv[]
448 sqlite3 *db;
449 if( objc!=2 ){
450 Tcl_WrongNumArgs(interp, 1, objv, "DB");
451 return TCL_ERROR;
453 db = getDbPointer(interp, objv[1]);
454 if( !db ){
455 return TCL_ERROR;
457 sqlite3_mutex_enter(sqlite3_db_mutex(db));
458 return TCL_OK;
461 static int SQLITE_TCLAPI test_leave_db_mutex(
462 void * clientData,
463 Tcl_Interp *interp,
464 int objc,
465 Tcl_Obj *CONST objv[]
467 sqlite3 *db;
468 if( objc!=2 ){
469 Tcl_WrongNumArgs(interp, 1, objv, "DB");
470 return TCL_ERROR;
472 db = getDbPointer(interp, objv[1]);
473 if( !db ){
474 return TCL_ERROR;
476 sqlite3_mutex_leave(sqlite3_db_mutex(db));
477 return TCL_OK;
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 },
489 { "enter_static_mutex", (Tcl_ObjCmdProc*)test_enter_static_mutex },
490 { "leave_static_mutex", (Tcl_ObjCmdProc*)test_leave_static_mutex },
492 { "enter_db_mutex", (Tcl_ObjCmdProc*)test_enter_db_mutex },
493 { "leave_db_mutex", (Tcl_ObjCmdProc*)test_leave_db_mutex },
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 },
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);
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;