OSDN Git Service

Insert a hack in pl/tcl to disable Tcl's built-in Notifier subsystem, which
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 21 Sep 2007 00:30:49 +0000 (00:30 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 21 Sep 2007 00:30:49 +0000 (00:30 +0000)
has a bad habit of launching multiple threads within the backend and thereby
causing all kinds of havoc.  Fortunately, we don't need it, and recent Tcl
versions provide an easy way to disable it.  Diagnosis and fix by
Steve Marshall, Paul Bayer, and Doug Knight of WSI Corporation.

src/pl/tcl/pltcl.c

index 093d990..d218108 100644 (file)
@@ -2,7 +2,7 @@
  * pltcl.c             - PostgreSQL support for Tcl as
  *                               procedural language (PL)
  *
- *       $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.112 2007/04/02 03:49:42 tgl Exp $
+ *       $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.113 2007/09/21 00:30:49 tgl Exp $
  *
  **********************************************************************/
 
 #include "utils/syscache.h"
 #include "utils/typcache.h"
 
+#define HAVE_TCL_VERSION(maj,min) \
+       ((TCL_MAJOR_VERSION > maj) || \
+        (TCL_MAJOR_VERSION == maj && TCL_MINOR_VERSION >= min))
 
-#if defined(UNICODE_CONVERSION) && TCL_MAJOR_VERSION == 8 \
-       && TCL_MINOR_VERSION > 0
+#if defined(UNICODE_CONVERSION) && HAVE_TCL_VERSION(8,1)
 
 #include "mb/pg_wchar.h"
 
@@ -165,6 +167,68 @@ static void pltcl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc,
 
 
 /*
+ * Hack to override Tcl's builtin Notifier subsystem.  This prevents the
+ * backend from becoming multithreaded, which breaks all sorts of things.
+ * That happens in the default version of Tcl_InitNotifier if the TCL library
+ * has been compiled with multithreading support (i.e. when TCL_THREADS is
+ * defined under Unix, and in all cases under Windows).
+ * It's okay to disable the notifier because we never enter the Tcl event loop
+ * from Postgres, so the notifier capabilities are initialized, but never
+ * used.  Only InitNotifier and DeleteFileHandler ever seem to get called
+ * within Postgres, but we implement all the functions for completeness.
+ * We can only fix this with Tcl >= 8.2, when Tcl_SetNotifier() appeared.
+ */
+#if HAVE_TCL_VERSION(8,2)
+
+static ClientData
+pltcl_InitNotifier(void)
+{
+       static int fakeThreadKey;       /* To give valid address for ClientData */
+
+       return (ClientData) &(fakeThreadKey);
+}
+
+static void
+pltcl_FinalizeNotifier(ClientData clientData)
+{
+}
+
+static void
+pltcl_SetTimer(Tcl_Time *timePtr)
+{
+}
+
+static void
+pltcl_AlertNotifier(ClientData clientData)
+{
+}
+
+static void
+pltcl_CreateFileHandler(int fd, int mask,
+                                               Tcl_FileProc *proc, ClientData clientData)
+{
+}
+
+static void
+pltcl_DeleteFileHandler(int fd)
+{
+}
+
+static void
+pltcl_ServiceModeHook(int mode)
+{
+}
+
+static int
+pltcl_WaitForEvent(Tcl_Time *timePtr)
+{
+       return 0;
+}
+
+#endif /* HAVE_TCL_VERSION(8,2) */
+
+
+/*
  * This routine is a crock, and so is everyplace that calls it.  The problem
  * is that the cached form of pltcl functions/queries is allocated permanently
  * (mostly via malloc()) and never released until backend exit.  Subsidiary
@@ -198,6 +262,25 @@ _PG_init(void)
        Tcl_FindExecutable("");
 #endif
 
+#if HAVE_TCL_VERSION(8,2)
+       /*
+        * Override the functions in the Notifier subsystem.  See comments above.
+        */
+       {
+               Tcl_NotifierProcs notifier;
+
+               notifier.setTimerProc          = pltcl_SetTimer;
+               notifier.waitForEventProc      = pltcl_WaitForEvent;
+               notifier.createFileHandlerProc = pltcl_CreateFileHandler;
+               notifier.deleteFileHandlerProc = pltcl_DeleteFileHandler;
+               notifier.initNotifierProc      = pltcl_InitNotifier;
+               notifier.finalizeNotifierProc  = pltcl_FinalizeNotifier;
+               notifier.alertNotifierProc     = pltcl_AlertNotifier;
+               notifier.serviceModeHookProc   = pltcl_ServiceModeHook;
+               Tcl_SetNotifier(&notifier);
+       }
+#endif
+
        /************************************************************
         * Create the dummy hold interpreter to prevent close of
         * stdout and stderr on DeleteInterp
@@ -1808,9 +1891,9 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
        PG_TRY();
        {
                /************************************************************
-                * Resolve argument type names and then look them up by oid 
-         * in the system cache, and remember the required information 
-         * for input conversion.
+                * Resolve argument type names and then look them up by oid
+                * in the system cache, and remember the required information
+                * for input conversion.
                 ************************************************************/
                for (i = 0; i < nargs; i++)
                {