]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Testing of the automatic TSD deallocation logic. The sqlite3_thread_cleanup()
authordrh <drh@noemail.net>
Wed, 11 Jan 2006 23:40:33 +0000 (23:40 +0000)
committerdrh <drh@noemail.net>
Wed, 11 Jan 2006 23:40:33 +0000 (23:40 +0000)
API is documented.  This should close ticket #1601. (CVS 2920)

FossilOrigin-Name: fb518b0ce4ddd4aaca5cccf61e651f173e735119

manifest
manifest.uuid
src/os_unix.c
src/os_win.c
src/server.c
src/test1.c
src/test4.c
src/test7.c
test/tester.tcl
www/capi3ref.tcl

index d84b1927cbd784b9cf11711aa49580a1baed00f4..b178da2dba94fd83e401fb3d623d0d01bab5fe64 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Automatically\sdeallocate\sthread-specific\sdata\swhen\sit\sis\sno\slonger\nbeing\sused.\s\sTicket\s#1601.\s\sAlso\simplemented\sthe\ssuggestion\sof\nticket\s#1603.\sMemory\smanagement\sis\snow\soff\sby\sdefault\sat\scompile-time.\nThe\ssqlite3_enable_memory_management()\sAPI\shas\sbeen\sremoved.\s(CVS\s2919)
-D 2006-01-11T21:41:21
+C Testing\sof\sthe\sautomatic\sTSD\sdeallocation\slogic.\s\sThe\ssqlite3_thread_cleanup()\nAPI\sis\sdocumented.\s\sThis\sshould\sclose\sticket\s#1601.\s(CVS\s2920)
+D 2006-01-11T23:40:33
 F Makefile.in ab3ffd8d469cef4477257169b82810030a6bb967
 F Makefile.linux-gcc aee18d8a05546dcf1888bd4547e442008a49a092
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -55,9 +55,9 @@ F src/os.h 9debc3d3ca4cdafde222a0ea74a4c8415aef4f22
 F src/os_common.h 78bcc34dded9b625b3c16d072b7e5b76d075a674
 F src/os_test.c 49833426101f99aee4bb5f6a44b7c4b2029fda1c
 F src/os_test.h 903c93554c23d88f34f667f1979e4a1cee792af3
-F src/os_unix.c 557a21c563496a1ccef32b1d57376d135884eac5
+F src/os_unix.c d3ec9c61a1194d3e475423298de6ce13871605a1
 F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e
-F src/os_win.c b67dd34bf1ae796851910a229bd94026b42d942f
+F src/os_win.c e372f99c4a89b9c54249a29c8c4912e5a2e67fb4
 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
 F src/pager.c a96b9c43664670576e41eac699277c7862d604d8
 F src/pager.h e0acb095b3ad0bca48f2ab00c87346665643f64f
@@ -67,19 +67,19 @@ F src/prepare.c 60c1f5e3d2901d651f8ca9f06e39e2ff3f335844
 F src/printf.c f47a2f4b5387cd2ebb12e9117a1a5d6bd9a2b812
 F src/random.c d40f8d356cecbd351ccfab6eaedd7ec1b54f5261
 F src/select.c 7b19d350cb2a18ae4a59d6b87049aa2ce9c671ae
-F src/server.c 519e308651e30102dd3d1f4053ac64c14267e44c
+F src/server.c e425729aa7ff374637033a38ba9fd9938c432244
 F src/shell.c 66b073375efbdee19045e7e0cd38b85f9aff71da
 F src/sqlite.h.in 4320cff369e37897d2839d526c2b8917a2756d60
 F src/sqliteInt.h bd3fba6d7163ef402b5633191ee5d7de5b7f6e80
 F src/table.c 486dcfce532685b53b5a2b5da8bba0ded6fb2316
 F src/tclsqlite.c d650bea0248fc0a310ddc2cb94273a3a5021fddf
-F src/test1.c d38c9f4da2136c5e1747517d7b564ba88a91fbee
+F src/test1.c 30ed0d4d594db0bb2beb98be7024cde1fe686f14
 F src/test2.c ca74a1d8aeb7d9606e8f6b762c5daf85c1a3f92b
 F src/test3.c 9742aa146eb750cab81c1d5605286c3a0eb88054
-F src/test4.c a8fd681e139e1c61f22a77d07fc3a99cb28fff3f
+F src/test4.c 0f95de81629a53fc6ab104f64a7ccb1dcbb8af90
 F src/test5.c 7162f8526affb771c4ed256826eee7bb9eca265f
 F src/test6.c 74d91b487c68154156eded457925d96aa2a3fdbb
-F src/test7.c bfe36c62cae189509660acfeeb891ffb9da8ef0c
+F src/test7.c c87fd373a613986113717ba51e6a5ad86407c1f7
 F src/test_async.c 6776f5027ca6378c116ff5ccc2fe41b908e33772
 F src/tokenize.c b75d24cbb419eef9ae7be286dd66723a2ee49f4f
 F src/trigger.c 694b247476d2fc0dce003af564f79e8752fc1158
@@ -234,7 +234,7 @@ F test/table.test 6dc0dfa44dd429520e0e5a0c5e55025f730e9403
 F test/tableapi.test 6a66d58b37d46dc0f2b3c7d4bd2617d209399bd1
 F test/tclsqlite.test 2c4b5fb2f21e6740479463c263f3020f08e472d7
 F test/temptable.test 7927261befdbc7b0a7ffebb85ecc70a74fa7b15b
-F test/tester.tcl b9b2c481dbef7fec4b8b3b91c6b810e7f916ba28
+F test/tester.tcl ed59b4eaa180728e4be1c55dc2823d66e03ae79a
 F test/thread1.test 776c9e459b75ba905193b351926ac4019b049f35
 F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
 F test/threadtest2.c 97a830d53c24c42290501fdfba4a6e5bdd34748b
@@ -297,7 +297,7 @@ F www/audit.tcl 90e09d580f79c7efec0c7d6f447b7ec5c2dce5c0
 F www/autoinc.tcl b357f5ba954b046ee35392ce0f884a2fcfcdea06
 F www/c_interface.tcl b51b08591554c16a0c3ef718364a508ac25abc7e
 F www/capi3.tcl 7a7cc225fe02eb7ab861a6019b08baa0014409e1
-F www/capi3ref.tcl 3885ca3847f394db7aa035647f0ab1c140244042
+F www/capi3ref.tcl 47c6014600805f4e385cf6b2e12b227ba1a906ec
 F www/changes.tcl 661937260b6eac4af510e068f33f3ce7b3298ff6
 F www/common.tcl 14d121c28532ad20c3e349caa4db708b0b822083
 F www/compile.tcl 276546d7eb445add5a867193bbd80f6919a6b084
@@ -340,7 +340,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b
 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P 8c26893c65574b0667bb84bde3ca49751079cc8d
-R a464a0f33259fc5b6aae2e3199e401a7
+P 5d9c6aa964305c3f36741ff0058da5b5f3ce0d24
+R 3399b09fea32a929acfc809e2317093c
 U drh
-Z 7760f8b5862256854e201380c09a40e6
+Z 938c3f3406b28a56fdbffbe4eafe6c93
index 527e175ef3ccef531c4d81553c2be1cd73217241..8fd4c151832352917974756f4aac2621c1eb6e2f 100644 (file)
@@ -1 +1 @@
-5d9c6aa964305c3f36741ff0058da5b5f3ce0d24
\ No newline at end of file
+fb518b0ce4ddd4aaca5cccf61e651f173e735119
\ No newline at end of file
index e603151a953d648b52cb13ba8721339165b52975..b079b5722a1e647e2ab9727b98f772a191ffeda8 100644 (file)
@@ -1626,6 +1626,27 @@ int sqlite3UnixInMutex(){
   return inMutex;
 }
 
+/*
+** Remember the number of thread-specific-data blocks allocated.
+** Use this to verify that we are not leaking thread-specific-data.
+** Ticket #1601
+*/
+#ifdef SQLITE_TEST
+int sqlite3_tsd_count = 0;
+# ifdef SQLITE_UNIX_THREADS
+    static pthread_mutex_t tsd_counter_mutex = PTHREAD_MUTEX_INITIALIZER;
+#   define TSD_COUNTER(N) \
+             pthread_mutex_lock(&tsd_counter_mutex); \
+             sqlite3_tsd_count += N; \
+             pthread_mutex_unlock(&tsd_counter_mutex);
+# else
+#   define TSD_COUNTER(N)  sqlite3_tsd_count += N
+# endif
+#else
+# define TSD_COUNTER(N)  /* no-op */
+#endif
+
+
 /*
 ** If called with allocateFlag==1, then return a pointer to thread
 ** specific data for the current thread.  Allocate and zero the
@@ -1664,11 +1685,13 @@ ThreadData *sqlite3UnixThreadSpecificData(int allocateFlag){
       if( pTsd ){
         *pTsd = zeroData;
         pthread_setspecific(key, pTsd);
+        TSD_COUNTER(+1);
       }
     }
   }else if( pTsd!=0 && memcmp(pTsd, &zeroData, sizeof(zeroData))==0 ){
     sqlite3OsFree(pTsd);
     pthread_setspecific(key, 0);
+    TSD_COUNTER(-1);
     pTsd = 0;
   }
   return pTsd;
@@ -1679,10 +1702,12 @@ ThreadData *sqlite3UnixThreadSpecificData(int allocateFlag){
       pTsd = sqlite3OsMalloc( sizeof(zeroData) );
       if( pTsd ){
         *pTsd = zeroData;
+        TSD_COUNTER(+1);
       }
     }
   }else if( pTsd!=0 && memcmp(pTsd, &zeroData, sizeof(zeroData))==0 ){
     sqlite3OsFree(pTsd);
+    TSD_COUNTER(-1);
     pTsd = 0;
   }
   return pTsd;
index 4675c4888ff02c99ae10cadcd4a9945c91b36ffa..e21ae8b932d7fd4781c5c982cfd930bc860c02b6 100644 (file)
@@ -1148,6 +1148,22 @@ int sqlite3WinCurrentTime(double *prNow){
   return 0;
 }
 
+/*
+** Remember the number of thread-specific-data blocks allocated.
+** Use this to verify that we are not leaking thread-specific-data.
+** Ticket #1601
+*/
+#ifdef SQLITE_TEST
+int sqlite3_tsd_count = 0;
+# define TSD_COUNTER_INCR InterlockedIncrement(&sqlite3_tsd_count)
+# define TSD_COUNTER_DECR InterlockedDecrement(&sqlite3_tsd_count)
+#else
+# define TSD_COUNTER_INCR  /* no-op */
+# define TSD_COUNTER_DECR  /* no-op */
+#endif
+
+
+
 /*
 ** If called with allocateFlag==1, then return a pointer to thread
 ** specific data for the current thread.  Allocate and zero the
@@ -1158,11 +1174,11 @@ int sqlite3WinCurrentTime(double *prNow){
 ** Return a pointer to the thread specific data or NULL if it is
 ** unallocated.
 */
-void *sqlite3WinThreadSpecificData(int allocateFlag){
-  static void *pTsd = 0;
+ThreadData *sqlite3WinThreadSpecificData(int allocateFlag){
   static int key;
   static int keyInit = 0;
   static const ThreadData zeroData;
+  ThreadData *pTsd;
 
   if( !keyInit ){
     sqlite3OsEnterMutex();
@@ -1179,15 +1195,17 @@ void *sqlite3WinThreadSpecificData(int allocateFlag){
   pTsd = TlsGetValue(key);
   if( allocateFlag ){
     if( !pTsd ){
-      pTsd = sqlite3OsMalloc(nByte);
+      pTsd = sqlite3OsMalloc( sizeof(zeroData) );
       if( pTsd ){
         *pTsd = zeroData;
         TlsSetValue(key, pTsd);
+        TSD_COUNTER_INCR;
       }
     }
   }else if( pTsd!=0 && memcmp(pTsd, &zeroData, sizeof(zeroData))==0 ){
     sqlite3OsFree(pTsd);
     TlsSetValue(key, 0);
+    TSD_COUNTER_DECR;
     pTsd = 0;
   }
   return pTsd;
index 20399eacb9469dce935a3b09120cd413ce7a7503..8348286989f6fb39b8877db1a30b49f7478a6bce 100644 (file)
@@ -332,6 +332,7 @@ int sqlite3_client_close(sqlite3 *pDb){
 void *sqlite3_server(void *NotUsed){
   sqlite3_enable_shared_cache(1);
   if( pthread_mutex_trylock(&g.serverMutex) ){
+    sqlite3_enable_shared_cache(0);
     return 0;  /* Another server is already running */
   }
   while( !g.serverHalt ){
@@ -393,6 +394,7 @@ void *sqlite3_server(void *NotUsed){
     pthread_cond_signal(&pMsg->clientWakeup);
   }
   pthread_mutex_unlock(&g.serverMutex);
+  sqlite3_thread_cleanup();
   return 0;
 }
 
index 891b5b4e21513cee0c4cdcfbdc5cf28a61daaa97..282a53f4a8a980371b9548c815d2271726d7ace5 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test1.c,v 1.188 2006/01/11 21:41:22 drh Exp $
+** $Id: test1.c,v 1.189 2006/01/11 23:40:34 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -3001,6 +3001,60 @@ static int test_soft_heap_limit(
   return TCL_OK;
 }
 
+/*
+** Usage:   sqlite3_clear_tsd_memdebug
+**
+** Clear all of the MEMDEBUG information out of thread-specific data.
+** This will allow it to be deallocated.
+*/
+static int test_clear_tsd_memdebug(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+#if defined(SQLITE_MEMDEBUG)
+  ThreadData *pTd = sqlite3ThreadData();
+  pTd->nMaxAlloc = 0;
+  pTd->zFile = 0;
+  pTd->iLine = 0;
+#endif
+  return TCL_OK;
+}
+
+/*
+** Usage:   sqlite3_tsd_release
+**
+** Call sqlite3ReleaseThreadData.
+*/
+static int test_tsd_release(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+#if defined(SQLITE_MEMDEBUG)
+  sqlite3ReleaseThreadData();
+#endif
+  return TCL_OK;
+}
+
+/*
+** Usage:   sqlite3_thread_cleanup
+**
+** Call the sqlite3_thread_cleanup API.
+*/
+static int test_thread_cleanup(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  sqlite3_thread_cleanup();
+  return TCL_OK;
+}
+
+
 /*
 ** This routine sets entries in the global ::sqlite_options() array variable
 ** according to the compile-time configuration of the database.  Test
@@ -3372,8 +3426,11 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
      { "sqlite3_changes",               test_changes       ,0 },
      { "sqlite3_step",                  test_step          ,0 },
 
-     { "sqlite3_release_memory",        test_release_memory,  0},
-     { "sqlite3_soft_heap_limit",       test_soft_heap_limit, 0},
+     { "sqlite3_release_memory",        test_release_memory,     0},
+     { "sqlite3_soft_heap_limit",       test_soft_heap_limit,    0},
+     { "sqlite3_clear_tsd_memdebug",    test_clear_tsd_memdebug, 0},
+     { "sqlite3_tsd_release",           test_tsd_release,        0},
+     { "sqlite3_thread_cleanup",        test_thread_cleanup,     0},
 
      /* sqlite3_column_*() API */
      { "sqlite3_column_count",          test_column_count  ,0 },
@@ -3429,6 +3486,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
   extern int sqlite3_malloc_id;
   extern int sqlite3_memMax;
   extern int sqlite3_like_count;
+  extern int sqlite3_tsd_count;
 #if OS_WIN
   extern int sqlite3_os_type;
 #endif
@@ -3461,6 +3519,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
       (char*)&sqlite3_current_time, TCL_LINK_INT);
   Tcl_LinkVar(interp, "sqlite_os_trace",
       (char*)&sqlite3_os_trace, TCL_LINK_INT);
+  Tcl_LinkVar(interp, "sqlite3_tsd_count",
+      (char*)&sqlite3_tsd_count, TCL_LINK_INT);
 #ifndef SQLITE_OMIT_UTF16
   Tcl_LinkVar(interp, "sqlite_last_needed_collation",
       (char*)&pzNeededCollation, TCL_LINK_STRING|TCL_LINK_READ_ONLY);
@@ -3507,8 +3567,11 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
 #endif /* OS_UNIX */
   set_options(interp);
 
-  int sqlite3_shared_cache_report(void *, Tcl_Interp *, int, Tcl_Obj *CONST[]);
-  Tcl_CreateObjCommand(interp, "sqlite_shared_cache_report", 
-      sqlite3_shared_cache_report, 0, 0);
+  {
+    extern int sqlite3_shared_cache_report(void *, Tcl_Interp *,
+                                    int, Tcl_Obj *CONST[]);
+    Tcl_CreateObjCommand(interp, "sqlite_shared_cache_report", 
+        sqlite3_shared_cache_report, 0, 0);
+  }
   return TCL_OK;
 }
index 573e56ec4504bbcec7216347e27b6d9961241c8c..e0ad006a88488268eed8eece2ba6dd2f587f6b66 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Code for testing the the SQLite library in a multithreaded environment.
 **
-** $Id: test4.c,v 1.13 2005/08/11 02:10:19 drh Exp $
+** $Id: test4.c,v 1.14 2006/01/11 23:40:34 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -97,6 +97,7 @@ static void *thread_main(void *pArg){
     p->zErr = 0;
   }
   p->completed++;
+  sqlite3_thread_cleanup();
   return 0;
 }
 
index 2e2cc93c29fdc6b9289542b8d080974d2c99564d..32f4d3c38e68054d9420c8226c591aee92ebbb6c 100644 (file)
@@ -12,7 +12,7 @@
 ** Code for testing the client/server version of the SQLite library.
 ** Derived from test4.c.
 **
-** $Id: test7.c,v 1.1 2006/01/09 23:40:25 drh Exp $
+** $Id: test7.c,v 1.2 2006/01/11 23:40:34 drh Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -118,6 +118,7 @@ static void *client_main(void *pArg){
     p->zErr = 0;
   }
   p->completed++;
+  sqlite3_thread_cleanup();
   return 0;
 }
 
index 6e86c68c4c0be64d670343e06084e9e3006b627d..c9795f4497473d8b2b5a4dbfb59a0aa7ab515f99 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements some common TCL routines used for regression
 # testing the SQLite library
 #
-# $Id: tester.tcl,v 1.58 2006/01/06 22:11:21 drh Exp $
+# $Id: tester.tcl,v 1.59 2006/01/11 23:40:34 drh Exp $
 
 # Make sure tclsqlite3 was compiled correctly.  Abort now with an
 # error message if not.
@@ -149,7 +149,16 @@ proc finalize_testing {} {
   catch {
     pp_check_for_leaks
   }
-
+  sqlite3 db {}
+  sqlite3_clear_tsd_memdebug
+  db close
+  if {$::sqlite3_tsd_count} {
+     puts "Thread-specific data leak: $::sqlite3_tsd_count instances"
+     incr nErr
+  } else {
+     puts "Thread-specific data deallocated properly"
+  }
+  incr nTest
   puts "$nErr errors out of $nTest tests"
   puts "Failures on these tests: $::failList"
   if {$nProb>0} {
index 47fe3de6bff349ed4a37ce0213580ffd421731f9..41fe7b7252d8dbfac460668b614009ad473370bc 100644 (file)
@@ -1,4 +1,4 @@
-set rcsid {$Id: capi3ref.tcl,v 1.27 2006/01/10 18:08:10 danielk1977 Exp $}
+set rcsid {$Id: capi3ref.tcl,v 1.28 2006/01/11 23:40:34 drh Exp $}
 source common.tcl
 header {C/C++ Interface For SQLite Version 3}
 puts {
@@ -1237,6 +1237,10 @@ api {} {
   There is no mechanism for sharing cache between database connections
   running in different threads.
 
+  Sharing must be disabled prior to shutting down a thread or else
+  the thread will leak memory.  Call this routine with an argument of
+  0 to turn of sharing.  Or use the sqlite3_thread_cleanup() API.
+
   This routine must not be called when any database connections
   are active in the current thread.  Enabling or disabling shared
   cache while there are active database connections will result
@@ -1264,55 +1268,20 @@ api {} {
 }
 
 api {} {
-  int sqlite3_enable_memory_management(int);
+  void sqlite3_thread_cleanup(void);
 } {
-  This routine enables or disables heap memory management for the
-  thread in which it is called.  Memory management is enabled if
-  the argument is true and disabled if the argument is false.
-
-  This routine must not be called when any database connections
-  are active in the current thread.  Enabling or disabling memory
-  management while there are active database connections will result
-  in memory corruption.
-
-  When memory management is enabled, SQLite tries to automatically
-  recover from out-of-memory errors by freeing unused cache memory
-  and retrying the allocation.
-  This allows operations to continue when available memory is limit
-  though with some loss of performance due to the reduction in cache
-  size.
-
-  The sqlite3_soft_heap_limit() API can be used to restrict SQLite's
-  heap memory usage to a preset amount so that the reclamation of
-  cache begins to occur before memory is exhausted.
-
-  Memory management is enabled and disabled on a thread-by-thread basis.
-  Each call to this routine enables or disabled memory management only for
-  database connections created and used in the same thread in which this
-  routine is called.
-
-  For any given database connection, SQLite requires that the
-  following routines always be called from the same thread:
-  sqlite3_open(), sqlite3_prepare(), sqlite3_step(), sqlite3_reset(),
-  sqlite3_finalize(), and sqlite3_close().  On some operating systems
-  (ex: windows and linux 2.6) you can get away with calling these routines
-  from different threads as long as their executions never overlap in time
-  and memory management is disabled.
-  But when the memory management is enabled, some information about the
-  database connections is stored in thread-specific storage so that it
-  will be available to remediate memory shortages.  Consequently,
-  the previously enumerated routines must always be called from the
-  same thread when memory management is enabled, regardless of what
-  operating system is used.
-
-  This routine returns SQLITE_OK if the memory management module was
-  enabled or disabled successfully.  An error code is returned
-  otherwise.
-
-  Memory management is disabled by default for backwards compatibility
-  and because it is normally only useful for embedded devices.  The
-  code that implements the memory management feature can be omitted by
-  recompiling SQLite with the SQLITE_OMIT_MEMORY_MANAGEMENT macro defined.
+  This routine makes sure that all thread local storage used by SQLite
+  in the current thread has been deallocated.  A thread can call this
+  routine prior to terminating in order to make sure there are no memory
+  leaks.
+
+  This routine is not strictly necessary.  If cache sharing has been
+  disabled using sqlite3_enable_shared_cache() and if all database
+  connections have been closed and if SQLITE_ENABLE_MEMORY_MANAGMENT is
+  on and all memory has been freed, then the thread local storage will
+  already have been automatically deallocated.  This routine is provided
+  as a convenience to the program who just wants to make sure that there
+  are no leaks.
 }
 
 api {} {
@@ -1323,30 +1292,29 @@ api {} {
   routine is called.  The value returned is the number of bytes actually
   freed.  
 
-  If memory management has not been enabled by calling
-  sqlite3_enable_memory_management() then this routine is a no-op
-  and always returns 0.
+  This routine is only available if memory management has been enabled
+  by compiling with the SQLITE_ENABLE_MEMORY_MANAGMENT macro.
 }
 
 api {} {
   void sqlite3_soft_heap_limit(int N);
 } {
   This routine sets the soft heap limit for the current thread to N.
-  If memory management is enabled on the thread  by the
-  sqlite3_enable_memory_management() function and the total heap usage
-  by SQLite in that thread exceeds N, then sqlite3_release_memory() is
+  If the total heap usage by SQLite in the current thread exceeds N,
+  then sqlite3_release_memory() is
   called to try to reduce the memory usage below the soft limit.
 
-  A negative value for N means that there is no soft heap limit and
+  A negative or zero value for N means that there is no soft heap limit and
   sqlite3_release_memory() will only be called when memory is exhaused.
-  The default value for the soft heap limit is negative.
+  The default value for the soft heap limit is zero.
 
   SQLite makes a best effort to honor the soft heap limit.  But if it
   is unable to reduce memory usage below the soft limit, execution will
   continue without error or notification.  This is why the limit is 
   called a "soft" limit.  It is advisory only.
 
-  If memory management is not enabled, the soft heap limit is ignored.
+  This routine is only available if memory management has been enabled
+  by compiling with the SQLITE_ENABLE_MEMORY_MANAGMENT macro.
 }
 
 set n 0