|
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.11 2008/07/19 13:43:24 danielk1977 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; |
|
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_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 */ |
|
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 int Sqlitetest_mutex_Init(Tcl_Interp *interp){ |
|
363 static struct { |
|
364 char *zName; |
|
365 Tcl_ObjCmdProc *xProc; |
|
366 } aCmd[] = { |
|
367 { "sqlite3_shutdown", (Tcl_ObjCmdProc*)test_shutdown }, |
|
368 { "sqlite3_initialize", (Tcl_ObjCmdProc*)test_initialize }, |
|
369 { "sqlite3_config", (Tcl_ObjCmdProc*)test_config }, |
|
370 |
|
371 { "alloc_dealloc_mutex", (Tcl_ObjCmdProc*)test_alloc_mutex }, |
|
372 { "install_mutex_counters", (Tcl_ObjCmdProc*)test_install_mutex_counters }, |
|
373 { "read_mutex_counters", (Tcl_ObjCmdProc*)test_read_mutex_counters }, |
|
374 { "clear_mutex_counters", (Tcl_ObjCmdProc*)test_clear_mutex_counters }, |
|
375 }; |
|
376 int i; |
|
377 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ |
|
378 Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); |
|
379 } |
|
380 memset(&g, 0, sizeof(g)); |
|
381 |
|
382 Tcl_LinkVar(interp, "disable_mutex_init", |
|
383 (char*)&g.disableInit, TCL_LINK_INT); |
|
384 Tcl_LinkVar(interp, "disable_mutex_try", |
|
385 (char*)&g.disableTry, TCL_LINK_INT); |
|
386 return SQLITE_OK; |
|
387 } |