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 test1.c */
23const char *sqlite3TestErrorName(int);
24
25/* A countable mutex */
26struct sqlite3_mutex {
27  sqlite3_mutex *pReal;
28  int eType;
29};
30
31/* State variables */
32static 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 */
43static 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 */
48static 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*/
57static 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*/
68static int counterMutexEnd(void){
69  g.isInit = 0;
70  return g.m.xMutexEnd();
71}
72
73/*
74** Allocate a countable mutex
75*/
76static 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*/
100static 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*/
111static 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*/
120static 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*/
129static void counterMutexLeave(sqlite3_mutex *p){
130  assert( g.isInit );
131  g.m.xMutexLeave(p->pReal);
132}
133
134/*
135** sqlite3_shutdown
136*/
137static 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 *)sqlite3TestErrorName(rc), TCL_VOLATILE);
152  return TCL_OK;
153}
154
155/*
156** sqlite3_initialize
157*/
158static 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 *)sqlite3TestErrorName(rc), TCL_VOLATILE);
173  return TCL_OK;
174}
175
176/*
177** install_mutex_counters BOOLEAN
178*/
179static 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 *)sqlite3TestErrorName(rc), TCL_VOLATILE);
234  return TCL_OK;
235}
236
237/*
238** read_mutex_counters
239*/
240static 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*/
273static 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*/
297static 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*/
324static 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 *)sqlite3TestErrorName(rc), TCL_VOLATILE);
358  return TCL_OK;
359}
360
361static 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
374static 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
393static 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
412int 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}
440