Add the sqlite3_os_routine_set()/get() functions. (CVS 2818)

FossilOrigin-Name: c1ed79f594fb85009c2e9e5e281cbe66a9d2fa17
diff --git a/src/os.c b/src/os.c
index 9703674..a1acd76 100644
--- a/src/os.c
+++ b/src/os.c
@@ -69,3 +69,42 @@
 int sqlite3OsCheckReservedLock(OsFile *id){
   return id->pMethod->xCheckReservedLock(id);
 }
+
+static void**getOsRoutinePtr(int eRoutine){
+  switch( eRoutine ){
+    case SQLITE_OS_ROUTINE_OPENREADWRITE:
+      return (void **)(&sqlite3Os.xOpenReadWrite);
+    case SQLITE_OS_ROUTINE_OPENREADONLY:
+      return (void **)(&sqlite3Os.xOpenReadOnly);
+    case SQLITE_OS_ROUTINE_OPENEXCLUSIVE:
+      return (void **)(&sqlite3Os.xOpenExclusive);
+    case SQLITE_OS_ROUTINE_DELETE:
+      return (void **)(&sqlite3Os.xDelete);
+    case SQLITE_OS_ROUTINE_FILEEXISTS:
+      return (void **)(&sqlite3Os.xFileExists);
+    case SQLITE_OS_ROUTINE_SYNCDIRECTORY:
+      return (void **)(&sqlite3Os.xSyncDirectory);
+    default:
+      assert(!"Illegal eRoutine value");
+  }
+  return 0;
+}
+
+void *sqlite3_os_routine_get(int eRoutine){
+  return *getOsRoutinePtr(eRoutine);
+}
+
+void *sqlite3_os_routine_set(int eRoutine, void *pRoutine){
+  void **ppRet = getOsRoutinePtr(eRoutine);
+  void *pRet = *ppRet;
+  *ppRet = pRoutine;
+  return pRet;
+}
+
+void sqlite3_os_enter_mutex(){
+  sqlite3Os.xEnterMutex();
+}
+void sqlite3_os_leave_mutex(){
+  sqlite3Os.xLeaveMutex();
+}
+
diff --git a/src/os.h b/src/os.h
index dc0e8d8..739c8a9 100644
--- a/src/os.h
+++ b/src/os.h
@@ -222,8 +222,30 @@
   int  (*xCurrentTime)(double*);
   void (*xEnterMutex)(void);
   void (*xLeaveMutex)(void);
+  void *(*xThreadSpecificData)(int);
 } sqlite3Os;
 
+
+/*
+** The semi-published API for setting and getting methods from the 
+** global sqlite3OsVtbl structure. Neither sqlite3_os_routine_XXX() function
+** is intriniscally thread-safe.
+**
+** External get/set access is only provided to the routines identified 
+** by the hash-defined SQLITE_OS_ROUTINE symbols.
+*/
+#define SQLITE_OS_ROUTINE_OPENREADWRITE   1
+#define SQLITE_OS_ROUTINE_OPENREADONLY    2
+#define SQLITE_OS_ROUTINE_OPENEXCLUSIVE   3
+#define SQLITE_OS_ROUTINE_DELETE          4
+#define SQLITE_OS_ROUTINE_FILEEXISTS      5
+#define SQLITE_OS_ROUTINE_SYNCDIRECTORY   6
+void *sqlite3_os_routine_get(int);
+void *sqlite3_os_routine_set(int, void *);
+
+void sqlite3_os_enter_mutex();
+void sqlite3_os_leave_mutex();
+
 /*
 ** Prototypes for routines found in os.c
 */
diff --git a/src/os_unix.c b/src/os_unix.c
index 349db3d..a7f1271 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -1587,6 +1587,66 @@
 }
 
 /*
+** This function is called automatically when a thread exists to delete
+** the threads SqliteTsd structure. 
+**
+** Because the SqliteTsd structure is required by higher level routines
+** such as sqliteMalloc() we use OsFree() and OsMalloc() directly to
+** allocate the thread specific data.
+*/
+static void deleteTsd(void *pTsd){
+  sqlite3OsFree(pTsd);
+}
+
+/* 
+** The first time this function is called from a specific thread, nByte 
+** bytes of data area are allocated and zeroed. A pointer to the new 
+** allocation is returned to the caller. 
+**
+** Each subsequent call to this function from the thread returns the same
+** pointer. The argument is ignored in this case.
+*/
+static void *unixThreadSpecificData(int nByte){
+#ifdef SQLITE_UNIX_THREADS
+  static pthread_key_t key;
+  static int keyInit = 0;
+  void *pTsd;
+
+  if( !keyInit ){
+    sqlite3Os.xEnterMutex();
+    if( !keyInit ){
+      int rc;
+      rc = pthread_key_create(&key, deleteTsd);
+      if( rc ){
+        return 0;
+      }
+      keyInit = 1;
+    }
+    sqlite3Os.xLeaveMutex();
+  }
+
+  pTsd = (SqliteTsd *)pthread_getspecific(key);
+  if( !pTsd ){
+    pTsd = sqlite3OsMalloc(sizeof(SqliteTsd));
+    if( pTsd ){
+      memset(pTsd, 0, sizeof(SqliteTsd));
+      pthread_setspecific(key, pTsd);
+    }
+  }
+  return pTsd;
+#else
+  static char tsd[sizeof(SqliteTsd)];
+  static isInit = 0;
+  assert( nByte==sizeof(SqliteTsd) );
+  if( !isInit ){
+    memset(tsd, 0, sizeof(SqliteTsd));
+    isInit = 1;
+  }
+  return (void *)tsd;
+#endif
+}
+
+/*
 ** The following variable, if set to a non-zero value, becomes the result
 ** returned from sqlite3Os.xCurrentTime().  This is used for testing.
 */
@@ -1644,6 +1704,7 @@
   unixCurrentTime,
   unixEnterMutex,
   unixLeaveMutex,
+  unixThreadSpecificData
 };
 
 
diff --git a/src/os_win.c b/src/os_win.c
index be561b7..bfa0af0 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -1020,6 +1020,21 @@
   return 0;
 }
 
+
+/*
+** Todo: This is a place-holder only
+*/
+static void *winThreadSpecificData(int nByte){
+  static char tsd[sizeof(SqliteTsd)];
+  static isInit = 0;
+  assert( nByte==sizeof(SqliteTsd) );
+  if( !isInit ){
+    memset(tsd, 0, sizeof(SqliteTsd));
+    isInit = 1;
+  }
+  return (void *)tsd;
+}
+
 /* Macro used to comment out routines that do not exists when there is
 ** no disk I/O
 */
@@ -1047,6 +1062,7 @@
   winCurrentTime,
   winEnterMutex,
   winLeaveMutex,
+  winThreadSpecificData
 };
 
 #endif /* OS_WIN */
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 8c9e95a..27551e5 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -12,7 +12,7 @@
 ** This header file defines the interface that the SQLite library
 ** presents to client programs.
 **
-** @(#) $Id: sqlite.h.in,v 1.144 2005/12/12 06:53:05 danielk1977 Exp $
+** @(#) $Id: sqlite.h.in,v 1.145 2005/12/15 10:11:32 danielk1977 Exp $
 */
 #ifndef _SQLITE3_H_
 #define _SQLITE3_H_
@@ -1291,6 +1291,10 @@
 */
 void sqlite3_soft_heap_limit(int);
 
+
+int sqlite3_set_io_routine(int, void *);
+void *sqlite3_get_io_routine(int);
+
 /*
 ** Undo the hack that converts floating point types to integer for
 ** builds on processors without floating point support.
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index aa1bf3e..daf146d 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.435 2005/12/15 03:04:11 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.436 2005/12/15 10:11:32 danielk1977 Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -268,6 +268,7 @@
 */
 typedef struct SqliteTsd SqliteTsd;
 struct SqliteTsd {
+  int isInit;                     /* True if structure has been initialised */
   int mallocFailed;               /* True after a malloc() has failed */
 #ifndef SQLITE_OMIT_SOFTHEAPLIMIT
   unsigned int nSoftHeapLimit;    /* (uint)-1 for unlimited */
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index 3b58df7..af6c910 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -11,7 +11,7 @@
 *************************************************************************
 ** A TCL Interface to SQLite
 **
-** $Id: tclsqlite.c,v 1.137 2005/12/12 06:53:05 danielk1977 Exp $
+** $Id: tclsqlite.c,v 1.138 2005/12/15 10:11:32 danielk1977 Exp $
 */
 #ifndef NO_TCL     /* Omit this whole file if TCL is unavailable */
 
@@ -2092,6 +2092,7 @@
     extern int Sqlitetest4_Init(Tcl_Interp*);
     extern int Sqlitetest5_Init(Tcl_Interp*);
     extern int Sqlitetest6_Init(Tcl_Interp*);
+    extern int Sqlitetestasync_Init(Tcl_Interp*);
     extern int Md5_Init(Tcl_Interp*);
     extern int Sqlitetestsse_Init(Tcl_Interp*);
 
@@ -2101,6 +2102,7 @@
     Sqlitetest4_Init(interp);
     Sqlitetest5_Init(interp);
     Sqlitetest6_Init(interp);
+    Sqlitetestasync_Init(interp);
     Md5_Init(interp);
 #ifdef SQLITE_SSE
     Sqlitetestsse_Init(interp);
diff --git a/src/util.c b/src/util.c
index 49b2c76..b91339f 100644
--- a/src/util.c
+++ b/src/util.c
@@ -14,9 +14,10 @@
 ** This file contains functions for allocating memory, comparing
 ** strings, and stuff like that.
 **
-** $Id: util.c,v 1.152 2005/12/12 06:53:05 danielk1977 Exp $
+** $Id: util.c,v 1.153 2005/12/15 10:11:32 danielk1977 Exp $
 */
 #include "sqliteInt.h"
+#include "os.h"
 #include <stdarg.h>
 #include <ctype.h>
 
@@ -107,6 +108,10 @@
 ** Begin code for memory allocation system test layer.
 **
 ** Memory debugging is turned on by defining the SQLITE_MEMDEBUG macro.
+**
+** SQLITE_MEMDEBUG==1    -> Fence-posting only (thread safe) 
+** SQLITE_MEMDEBUG==2    -> Fence-posting + linked list of allocations (not ts)
+** SQLITE_MEMDEBUG==3    -> Above + backtraces (not thread safe, req. glibc)
 */
 
 /* Figure out whether or not to store backtrace() information for each malloc.
@@ -114,7 +119,7 @@
 ** greater and glibc is in use. If we don't want to use backtrace(), then just
 ** define it as an empty macro and set the amount of space reserved to 0.
 */
-#if defined(__GLIBC__) && SQLITE_MEMDEBUG>1
+#if defined(__GLIBC__) && SQLITE_MEMDEBUG>2
   extern int backtrace(void **, int);
   #define TESTALLOC_STACKSIZE 128
   #define TESTALLOC_STACKFRAMES ((TESTALLOC_STACKSIZE-8)/sizeof(void*))
@@ -302,6 +307,8 @@
   return (void *)(&z[-1 * TESTALLOC_OFFSET_DATA(p)]);
 }
 
+
+#if SQLITE_MEMDEBUG>1
 /*
 ** The argument points to an Os level allocation. Link it into the threads list
 ** of allocations.
@@ -363,6 +370,11 @@
     ((void **)(pp[1]))[0] = p;
   }
 }
+#else
+#define linkAlloc(x)
+#define relinkAlloc(x)
+#define unlinkAlloc(x)
+#endif
 
 /*
 ** This function sets the result of the Tcl interpreter passed as an argument
@@ -1248,26 +1260,17 @@
 
 /*
 ** Return a pointer to the SqliteTsd associated with the calling thread.
-** TODO: Actually return thread-specific-data instead of this global pointer.
 */
 SqliteTsd *sqlite3Tsd(){
-  static SqliteTsd tsd = {
-    0                    /* mallocFailed flag */
-  #ifndef SQLITE_OMIT_SOFTHEAPLIMIT
-    , 0xFFFFFFFF         /* nSoftHeapLimit */
-    , 0                  /* nAlloc */
-  #endif
-  #ifndef NDEBUG
-    , 1                  /* mallocAllowed flag */
-  #endif
-  #ifdef SQLITE_MEMDEBUG
-    , 0
-    , 0
-    , 0
-    , 0
-  #endif
-  };
-  return &tsd;
+  SqliteTsd *pTsd = sqlite3Os.xThreadSpecificData(sizeof(SqliteTsd));
+  if( pTsd && !pTsd->isInit ){
+    pTsd->nSoftHeapLimit = 0xFFFFFFFF;
+#ifndef NDEBUG
+    pTsd->mallocAllowed = 1;
+#endif
+    pTsd->isInit = 1;
+  }
+  return pTsd;
 }
 
 /*