blob: 7352c2e83b064ba10781db5a010f065872f76c73 [file] [log] [blame]
danielk19771a9ed0b2008-06-18 09:45:56 +00001/*
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**
danielk19775f6d0262008-11-04 14:55:47 +000013** $Id: test_mutex.c,v 1.12 2008/11/04 14:55:47 danielk1977 Exp $
danielk19771a9ed0b2008-06-18 09:45:56 +000014*/
15
16#include "tcl.h"
17#include "sqlite3.h"
danielk1977b27475b2008-07-19 13:43:23 +000018#include "sqliteInt.h"
danielk19771a9ed0b2008-06-18 09:45:56 +000019#include <stdlib.h>
20#include <assert.h>
21#include <string.h>
22
drhfb45d8c2008-07-08 00:06:49 +000023/* defined in test1.c */
danielk19771a9ed0b2008-06-18 09:45:56 +000024const char *sqlite3TestErrorName(int);
25
drhfb45d8c2008-07-08 00:06:49 +000026/* A countable mutex */
danielk19771a9ed0b2008-06-18 09:45:56 +000027struct sqlite3_mutex {
28 sqlite3_mutex *pReal;
29 int eType;
30};
31
drhfb45d8c2008-07-08 00:06:49 +000032/* State variables */
danielk19771a9ed0b2008-06-18 09:45:56 +000033static struct test_mutex_globals {
drhfb45d8c2008-07-08 00:06:49 +000034 int isInstalled; /* True if installed */
35 int disableInit; /* True to cause sqlite3_initalize() to fail */
drh8a42cbd2008-07-10 18:13:42 +000036 int disableTry; /* True to force sqlite3_mutex_try() to fail */
drhfb45d8c2008-07-08 00:06:49 +000037 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 */
danielk19771a9ed0b2008-06-18 09:45:56 +000041} g;
42
drhfb45d8c2008-07-08 00:06:49 +000043/* Return true if the countable mutex is currently held */
danielk19771a9ed0b2008-06-18 09:45:56 +000044static int counterMutexHeld(sqlite3_mutex *p){
45 return g.m.xMutexHeld(p->pReal);
46}
47
drhfb45d8c2008-07-08 00:06:49 +000048/* Return true if the countable mutex is not currently held */
danielk19771a9ed0b2008-06-18 09:45:56 +000049static int counterMutexNotheld(sqlite3_mutex *p){
50 return g.m.xMutexNotheld(p->pReal);
51}
52
drhfb45d8c2008-07-08 00:06:49 +000053/* 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*/
danielk19771a9ed0b2008-06-18 09:45:56 +000058static int counterMutexInit(void){
drhfb45d8c2008-07-08 00:06:49 +000059 int rc;
60 if( g.disableInit ) return g.disableInit;
61 rc = g.m.xMutexInit();
62 g.isInit = 1;
63 return rc;
danielk19771a9ed0b2008-06-18 09:45:56 +000064}
65
drhfb45d8c2008-07-08 00:06:49 +000066/*
67** Uninitialize the mutex subsystem
68*/
danielk19771a9ed0b2008-06-18 09:45:56 +000069static int counterMutexEnd(void){
drhfb45d8c2008-07-08 00:06:49 +000070 g.isInit = 0;
danielk19771a9ed0b2008-06-18 09:45:56 +000071 return g.m.xMutexEnd();
72}
73
drhfb45d8c2008-07-08 00:06:49 +000074/*
75** Allocate a countable mutex
76*/
danielk19771a9ed0b2008-06-18 09:45:56 +000077static sqlite3_mutex *counterMutexAlloc(int eType){
78 sqlite3_mutex *pReal;
79 sqlite3_mutex *pRet = 0;
80
drhfb45d8c2008-07-08 00:06:49 +000081 assert( g.isInit );
danielk19771a9ed0b2008-06-18 09:45:56 +000082 assert(eType<8 && eType>=0);
83
84 pReal = g.m.xMutexAlloc(eType);
85 if( !pReal ) return 0;
86
drhc8d75672008-07-08 02:12:37 +000087 if( eType==SQLITE_MUTEX_FAST || eType==SQLITE_MUTEX_RECURSIVE ){
danielk19771a9ed0b2008-06-18 09:45:56 +000088 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
drhfb45d8c2008-07-08 00:06:49 +000098/*
99** Free a countable mutex
100*/
danielk19771a9ed0b2008-06-18 09:45:56 +0000101static void counterMutexFree(sqlite3_mutex *p){
drhfb45d8c2008-07-08 00:06:49 +0000102 assert( g.isInit );
danielk19771a9ed0b2008-06-18 09:45:56 +0000103 g.m.xMutexFree(p->pReal);
drhc8d75672008-07-08 02:12:37 +0000104 if( p->eType==SQLITE_MUTEX_FAST || p->eType==SQLITE_MUTEX_RECURSIVE ){
danielk19771a9ed0b2008-06-18 09:45:56 +0000105 free(p);
106 }
107}
108
drhfb45d8c2008-07-08 00:06:49 +0000109/*
110** Enter a countable mutex. Block until entry is safe.
111*/
danielk19771a9ed0b2008-06-18 09:45:56 +0000112static void counterMutexEnter(sqlite3_mutex *p){
drhfb45d8c2008-07-08 00:06:49 +0000113 assert( g.isInit );
danielk19771a9ed0b2008-06-18 09:45:56 +0000114 g.aCounter[p->eType]++;
115 g.m.xMutexEnter(p->pReal);
116}
117
drhfb45d8c2008-07-08 00:06:49 +0000118/*
119** Try to enter a mutex. Return true on success.
120*/
danielk19771a9ed0b2008-06-18 09:45:56 +0000121static int counterMutexTry(sqlite3_mutex *p){
drhfb45d8c2008-07-08 00:06:49 +0000122 assert( g.isInit );
danielk19771a9ed0b2008-06-18 09:45:56 +0000123 g.aCounter[p->eType]++;
drh19dd29f2008-07-10 20:41:49 +0000124 if( g.disableTry ) return SQLITE_BUSY;
danielk19771a9ed0b2008-06-18 09:45:56 +0000125 return g.m.xMutexTry(p->pReal);
126}
127
drhfb45d8c2008-07-08 00:06:49 +0000128/* Leave a mutex
129*/
danielk19771a9ed0b2008-06-18 09:45:56 +0000130static void counterMutexLeave(sqlite3_mutex *p){
drhfb45d8c2008-07-08 00:06:49 +0000131 assert( g.isInit );
danielk19771a9ed0b2008-06-18 09:45:56 +0000132 g.m.xMutexLeave(p->pReal);
133}
134
135/*
136** sqlite3_shutdown
137*/
138static 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*/
159static 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*/
180static 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,
danielk19774a9d1f62008-06-19 08:51:23 +0000191 counterMutexEnd,
danielk19771a9ed0b2008-06-18 09:45:56 +0000192 counterMutexAlloc,
193 counterMutexFree,
194 counterMutexEnter,
195 counterMutexTry,
196 counterMutexLeave,
danielk19771a9ed0b2008-06-18 09:45:56 +0000197 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 }
drh8a42cbd2008-07-10 18:13:42 +0000223 g.disableTry = 0;
danielk19771a9ed0b2008-06-18 09:45:56 +0000224 }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*/
241static 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_mem2", "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*/
274static 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
danielk197759f8c082008-06-18 17:09:10 +0000293/*
drhc8d75672008-07-08 02:12:37 +0000294** 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*/
298static int test_alloc_mutex(
299 void * clientData,
300 Tcl_Interp *interp,
301 int objc,
302 Tcl_Obj *CONST objv[]
303){
drhb912f3e2008-07-17 17:34:19 +0000304#if SQLITE_THREADSAFE
drhc8d75672008-07-08 02:12:37 +0000305 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);
drhb912f3e2008-07-17 17:34:19 +0000310#endif
drhc8d75672008-07-08 02:12:37 +0000311 return TCL_OK;
312}
313
314/*
danielk197759f8c082008-06-18 17:09:10 +0000315** sqlite3_config OPTION
drh8a42cbd2008-07-10 18:13:42 +0000316**
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.
danielk197759f8c082008-06-18 17:09:10 +0000324*/
325static 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) ){
drh8a42cbd2008-07-10 18:13:42 +0000350 if( Tcl_GetIntFromObj(interp, objv[1], &i) ){
351 return TCL_ERROR;
352 }
353 }else{
354 i = aOpt[i].iValue;
danielk197759f8c082008-06-18 17:09:10 +0000355 }
356
drh8a42cbd2008-07-10 18:13:42 +0000357 rc = sqlite3_config(i);
danielk197759f8c082008-06-18 17:09:10 +0000358 Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_VOLATILE);
359 return TCL_OK;
360}
361
danielk19775f6d0262008-11-04 14:55:47 +0000362static sqlite3 *getDbPointer(Tcl_Interp *pInterp, Tcl_Obj *pObj){
363 sqlite3 *db;
364 Tcl_CmdInfo info;
365 char *zCmd = Tcl_GetString(pObj);
366 if( 1!=Tcl_GetCommandInfo(pInterp, zCmd, &info) ){
367 Tcl_AppendResult(pInterp, "No such db-handle: \"", zCmd, "\"", 0);
368 return 0;
369 }
370 db = *((sqlite3 **)info.objClientData);
371 assert( db );
372 return db;
373}
374
375static 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
394static 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
danielk19771a9ed0b2008-06-18 09:45:56 +0000413int 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 },
danielk197759f8c082008-06-18 17:09:10 +0000420 { "sqlite3_config", (Tcl_ObjCmdProc*)test_config },
danielk19771a9ed0b2008-06-18 09:45:56 +0000421
danielk19775f6d0262008-11-04 14:55:47 +0000422 { "enter_db_mutex", (Tcl_ObjCmdProc*)test_enter_db_mutex },
423 { "leave_db_mutex", (Tcl_ObjCmdProc*)test_leave_db_mutex },
424
drhc8d75672008-07-08 02:12:37 +0000425 { "alloc_dealloc_mutex", (Tcl_ObjCmdProc*)test_alloc_mutex },
danielk19771a9ed0b2008-06-18 09:45:56 +0000426 { "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 memset(&g, 0, sizeof(g));
drhfb45d8c2008-07-08 00:06:49 +0000435
436 Tcl_LinkVar(interp, "disable_mutex_init",
437 (char*)&g.disableInit, TCL_LINK_INT);
drh8a42cbd2008-07-10 18:13:42 +0000438 Tcl_LinkVar(interp, "disable_mutex_try",
439 (char*)&g.disableTry, TCL_LINK_INT);
danielk19771a9ed0b2008-06-18 09:45:56 +0000440 return SQLITE_OK;
441}