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