Tcl Source Code

Check-in [9c78cc3ee2]
Login
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2019 Conference, Houston/TX, US, Nov 4-8
Send your abstracts to [email protected]
or submit via the online form by Sep 9.

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:* win/tclWinConsole.c: * win/tclWinPipe.c: * win/tclWinSerial.c: Fixed race condition where background threads were terminated while they still held a lock in the notifier.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | core-8-1-branch-old
Files: files | file ages | folders
SHA1: 9c78cc3ee23e83636356212ebfd7a854e049bb3f
User & Date: stanton 1999-04-05 21:58:15
Context
1999-04-05
22:18
* tests/tests/socket.test: Changed so tests don't reuse sockets, since Windows is slow to release so... check-in: 32d82f87bd user: stanton tags: core-8-1-branch-old
21:58
* win/tclWinConsole.c: * win/tclWinPipe.c: * win/tclWinSerial.c: Fixed race condition where backgrou... check-in: 9c78cc3ee2 user: stanton tags: core-8-1-branch-old
1999-04-03
03:02
removed windows-tk restriction on code that forked another shell--able to make this change because b... check-in: 03e6eecd77 user: hershey tags: core-8-1-branch-old
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to win/tclWinConsole.c.

     5      5    *	and the "console" channel driver.
     6      6    *
     7      7    * Copyright (c) 1999 by Scriptics Corp.
     8      8    *
     9      9    * See the file "license.terms" for information on usage and redistribution
    10     10    * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    11     11    *
    12         - * RCS: @(#) $Id: tclWinConsole.c,v 1.1.2.6 1999/04/01 00:56:12 redman Exp $
           12  + * RCS: @(#) $Id: tclWinConsole.c,v 1.1.2.7 1999/04/05 21:58:15 stanton Exp $
    13     13    */
    14     14   
    15     15   #include "tclWinInt.h"
    16     16   
    17     17   #include <dos.h>
    18     18   #include <fcntl.h>
    19     19   #include <io.h>
................................................................................
    21     21   
    22     22   /*
    23     23    * The following variable is used to tell whether this module has been
    24     24    * initialized.
    25     25    */
    26     26   
    27     27   static int initialized = 0;
    28         -TCL_DECLARE_MUTEX(procMutex)
    29     28   
           29  +/*
           30  + * The consoleMutex locks around access to the initialized variable, and it is
           31  + * used to protect background threads from being terminated while they are
           32  + * using APIs that hold locks.
           33  + */
           34  +
           35  +TCL_DECLARE_MUTEX(consoleMutex)
    30     36   
    31     37   /*
    32     38    * Bit masks used in the flags field of the ConsoleInfo structure below.
    33     39    */
    34     40   
    35     41   #define CONSOLE_PENDING	(1<<0)	/* Message is pending in the queue. */
    36     42   #define CONSOLE_ASYNC	(1<<1)	/* Channel is non-blocking. */
................................................................................
   199    205   
   200    206       /*
   201    207        * Check the initialized flag first, then check again in the mutex.
   202    208        * This is a speed enhancement.
   203    209        */
   204    210   
   205    211       if (!initialized) {
   206         -	Tcl_MutexLock(&procMutex);
          212  +	Tcl_MutexLock(&consoleMutex);
   207    213   	if (!initialized) {
   208    214   	    initialized = 1;
   209    215   	    Tcl_CreateExitHandler(ProcExitHandler, NULL);
   210    216   	}
   211         -	Tcl_MutexUnlock(&procMutex);
          217  +	Tcl_MutexUnlock(&consoleMutex);
   212    218       }
   213    219   
   214    220       tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
   215    221       if (tsdPtr == NULL) {
   216    222   	tsdPtr = TCL_TSD_INIT(&dataKey);
   217    223   	tsdPtr->firstConsolePtr = NULL;
   218    224   	Tcl_CreateEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
................................................................................
   262    268    *----------------------------------------------------------------------
   263    269    */
   264    270   
   265    271   static void
   266    272   ProcExitHandler(
   267    273       ClientData clientData)	/* Old window proc */
   268    274   {
   269         -    Tcl_MutexLock(&procMutex);
          275  +    Tcl_MutexLock(&consoleMutex);
   270    276       initialized = 0;
   271         -    Tcl_MutexUnlock(&procMutex);
          277  +    Tcl_MutexUnlock(&consoleMutex);
   272    278   }
   273    279   
   274    280   /*
   275    281    *----------------------------------------------------------------------
   276    282    *
   277    283    * ConsoleSetupProc --
   278    284    *
................................................................................
   464    470       /*
   465    471        * Clean up the background thread if necessary.  Note that this
   466    472        * must be done before we can close the file, since the 
   467    473        * thread may be blocking trying to read from the console.
   468    474        */
   469    475       
   470    476       if (consolePtr->readThread) {
          477  +	/*
          478  +	 * Forcibly terminate the background thread.  We cannot rely on the
          479  +	 * thread to cleanly terminate itself because we have no way of
          480  +	 * closing the handle without blocking in the case where the
          481  +	 * thread is in the middle of an I/O operation.  Note that we need
          482  +	 * to guard against terminating the thread while it is in the
          483  +	 * middle of Tcl_ThreadAlert because it won't be able to release
          484  +	 * the notifier lock.
          485  +	 */
          486  +
          487  +	Tcl_MutexLock(&consoleMutex);
   471    488   	TerminateThread(consolePtr->readThread, 0);
          489  +	Tcl_MutexUnlock(&consoleMutex);
   472    490   
   473    491   	/*
   474    492   	 * Wait for the thread to terminate.  This ensures that we are
   475    493   	 * completely cleaned up before we leave this function. 
   476    494   	 */
   477    495   
   478    496   	WaitForSingleObject(consolePtr->readThread, INFINITE);
................................................................................
   487    505        * Wait for the writer thread to finish the current buffer, then
   488    506        * terminate the thread and close the handles.  If the channel is
   489    507        * nonblocking, there should be no pending write operations.
   490    508        */
   491    509       
   492    510       if (consolePtr->writeThread) {
   493    511   	WaitForSingleObject(consolePtr->writable, INFINITE);
          512  +
          513  +	/*
          514  +	 * Forcibly terminate the background thread.  We cannot rely on the
          515  +	 * thread to cleanly terminate itself because we have no way of
          516  +	 * closing the handle without blocking in the case where the
          517  +	 * thread is in the middle of an I/O operation.  Note that we need
          518  +	 * to guard against terminating the thread while it is in the
          519  +	 * middle of Tcl_ThreadAlert because it won't be able to release
          520  +	 * the notifier lock.
          521  +	 */
          522  +
          523  +	Tcl_MutexLock(&consoleMutex);
   494    524   	TerminateThread(consolePtr->writeThread, 0);
          525  +	Tcl_MutexUnlock(&consoleMutex);
   495    526   
   496    527   	/*
   497    528   	 * Wait for the thread to terminate.  This ensures that we are
   498    529   	 * completely cleaned up before we leave this function. 
   499    530   	 */
   500    531   
   501    532   	WaitForSingleObject(consolePtr->writeThread, INFINITE);
................................................................................
  1066   1097   
  1067   1098   	/*
  1068   1099   	 * Signal the main thread by signalling the readable event and
  1069   1100   	 * then waking up the notifier thread.
  1070   1101   	 */
  1071   1102   
  1072   1103   	SetEvent(infoPtr->readable);
         1104  +
         1105  +	/*
         1106  +	 * Alert the foreground thread.  Note that we need to treat this like
         1107  +	 * a critical section so the foreground thread does not terminate
         1108  +	 * this thread while we are holding a mutex in the notifier code.
         1109  +	 */
         1110  +
         1111  +	Tcl_MutexLock(&consoleMutex);
  1073   1112   	Tcl_ThreadAlert(infoPtr->threadId);
         1113  +	Tcl_MutexUnlock(&consoleMutex);
  1074   1114       }
  1075   1115       return 0;			/* NOT REACHED */
  1076   1116   }
  1077   1117   
  1078   1118   /*
  1079   1119    *----------------------------------------------------------------------
  1080   1120    *
................................................................................
  1128   1168   
  1129   1169   	/*
  1130   1170   	 * Signal the main thread by signalling the writable event and
  1131   1171   	 * then waking up the notifier thread.
  1132   1172   	 */
  1133   1173   	
  1134   1174   	SetEvent(infoPtr->writable);
         1175  +
         1176  +	/*
         1177  +	 * Alert the foreground thread.  Note that we need to treat this like
         1178  +	 * a critical section so the foreground thread does not terminate
         1179  +	 * this thread while we are holding a mutex in the notifier code.
         1180  +	 */
         1181  +
         1182  +	Tcl_MutexLock(&consoleMutex);
  1135   1183   	Tcl_ThreadAlert(infoPtr->threadId);
         1184  +	Tcl_MutexUnlock(&consoleMutex);
  1136   1185       }
  1137   1186       return 0;			/* NOT REACHED */
  1138   1187   }
  1139   1188   
  1140   1189   
  1141   1190   
  1142   1191   /*

Changes to win/tclWinPipe.c.

     5      5    *	the "pipe" channel driver, and the "pid" Tcl command.
     6      6    *
     7      7    * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
     8      8    *
     9      9    * See the file "license.terms" for information on usage and redistribution
    10     10    * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    11     11    *
    12         - * RCS: @(#) $Id: tclWinPipe.c,v 1.1.2.9 1999/03/27 00:39:31 redman Exp $
           12  + * RCS: @(#) $Id: tclWinPipe.c,v 1.1.2.10 1999/04/05 21:58:16 stanton Exp $
    13     13    */
    14     14   
    15     15   #include "tclWinInt.h"
    16     16   
    17     17   #include <dos.h>
    18     18   #include <fcntl.h>
    19     19   #include <io.h>
................................................................................
    21     21   
    22     22   /*
    23     23    * The following variable is used to tell whether this module has been
    24     24    * initialized.
    25     25    */
    26     26   
    27     27   static int initialized = 0;
    28         -TCL_DECLARE_MUTEX(procMutex)
           28  +
           29  +/*
           30  + * The pipeMutex locks around access to the initialized and procList variables,
           31  + * and it is used to protect background threads from being terminated while
           32  + * they are using APIs that hold locks.
           33  + */
           34  +
           35  +TCL_DECLARE_MUTEX(pipeMutex)
    29     36   
    30     37   /*
    31     38    * The following defines identify the various types of applications that 
    32     39    * run under windows.  There is special case code for the various types.
    33     40    */
    34     41   
    35     42   #define APPL_NONE	0
................................................................................
   270    277   
   271    278       /*
   272    279        * Check the initialized flag first, then check again in the mutex.
   273    280        * This is a speed enhancement.
   274    281        */
   275    282   
   276    283       if (!initialized) {
   277         -	Tcl_MutexLock(&procMutex);
          284  +	Tcl_MutexLock(&pipeMutex);
   278    285   	if (!initialized) {
   279    286   	    initialized = 1;
   280    287   	    procList = NULL;
   281    288   	    Tcl_CreateExitHandler(ProcExitHandler, NULL);
   282    289   	}
   283         -	Tcl_MutexUnlock(&procMutex);
          290  +	Tcl_MutexUnlock(&pipeMutex);
   284    291       }
   285    292   
   286    293       tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
   287    294       if (tsdPtr == NULL) {
   288    295   	tsdPtr = TCL_TSD_INIT(&dataKey);
   289    296   	tsdPtr->firstPipePtr = NULL;
   290    297   	Tcl_CreateEventSource(PipeSetupProc, PipeCheckProc, NULL);
................................................................................
   333    340    *----------------------------------------------------------------------
   334    341    */
   335    342   
   336    343   static void
   337    344   ProcExitHandler(
   338    345       ClientData clientData)	/* Old window proc */
   339    346   {
   340         -    Tcl_MutexLock(&procMutex);
          347  +    Tcl_MutexLock(&pipeMutex);
   341    348       initialized = 0;
   342         -    Tcl_MutexUnlock(&procMutex);
          349  +    Tcl_MutexUnlock(&pipeMutex);
   343    350   }
   344    351   
   345    352   /*
   346    353    *----------------------------------------------------------------------
   347    354    *
   348    355    * PipeSetupProc --
   349    356    *
................................................................................
   952    959   
   953    960   unsigned long
   954    961   TclpGetPid(
   955    962       Tcl_Pid pid)		/* The HANDLE of the child process. */
   956    963   {
   957    964       ProcInfo *infoPtr;
   958    965   
   959         -    Tcl_MutexLock(&procMutex);
          966  +    Tcl_MutexLock(&pipeMutex);
   960    967       for (infoPtr = procList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
   961    968   	if (infoPtr->hProcess == (HANDLE) pid) {
   962         -	    Tcl_MutexUnlock(&procMutex);
          969  +	    Tcl_MutexUnlock(&pipeMutex);
   963    970   	    return infoPtr->dwProcessId;
   964    971   	}
   965    972       }
   966         -    Tcl_MutexUnlock(&procMutex);
          973  +    Tcl_MutexUnlock(&pipeMutex);
   967    974       return (unsigned long) -1;
   968    975   }
   969    976   
   970    977   /*
   971    978    *----------------------------------------------------------------------
   972    979    *
   973    980    * TclpCreateProcess --
................................................................................
  2141   2148   	/*
  2142   2149   	 * Clean up the background thread if necessary.  Note that this
  2143   2150   	 * must be done before we can close the file, since the 
  2144   2151   	 * thread may be blocking trying to read from the pipe.
  2145   2152   	 */
  2146   2153   
  2147   2154   	if (pipePtr->readThread) {
         2155  +	    /*
         2156  +	     * Forcibly terminate the background thread.  We cannot rely on the
         2157  +	     * thread to cleanly terminate itself because we have no way of
         2158  +	     * closing the pipe handle without blocking in the case where the
         2159  +	     * thread is in the middle of an I/O operation.  Note that we need
         2160  +	     * to guard against terminating the thread while it is in the
         2161  +	     * middle of Tcl_ThreadAlert because it won't be able to release
         2162  +	     * the notifier lock.
         2163  +	     */
         2164  +
         2165  +	    Tcl_MutexLock(&pipeMutex);
  2148   2166   	    TerminateThread(pipePtr->readThread, 0);
         2167  +	    Tcl_MutexUnlock(&pipeMutex);
  2149   2168   
  2150   2169   	    /*
  2151   2170   	     * Wait for the thread to terminate.  This ensures that we are
  2152   2171   	     * completely cleaned up before we leave this function. 
  2153   2172   	     */
  2154   2173   
  2155   2174   	    WaitForSingleObject(pipePtr->readThread, INFINITE);
................................................................................
  2170   2189   	 * Wait for the writer thread to finish the current buffer, then
  2171   2190   	 * terminate the thread and close the handles.  If the channel is
  2172   2191   	 * nonblocking, there should be no pending write operations.
  2173   2192   	 */
  2174   2193   
  2175   2194   	if (pipePtr->writeThread) {
  2176   2195   	    WaitForSingleObject(pipePtr->writable, INFINITE);
         2196  +
         2197  +	    /*
         2198  +	     * Forcibly terminate the background thread.  We cannot rely on the
         2199  +	     * thread to cleanly terminate itself because we have no way of
         2200  +	     * closing the pipe handle without blocking in the case where the
         2201  +	     * thread is in the middle of an I/O operation.  Note that we need
         2202  +	     * to guard against terminating the thread while it is in the
         2203  +	     * middle of Tcl_ThreadAlert because it won't be able to release
         2204  +	     * the notifier lock.
         2205  +	     */
         2206  +
         2207  +	    Tcl_MutexLock(&pipeMutex);
  2177   2208   	    TerminateThread(pipePtr->writeThread, 0);
         2209  +	    Tcl_MutexUnlock(&pipeMutex);
         2210  +
         2211  +
  2178   2212   	    /*
  2179   2213   	     * Wait for the thread to terminate.  This ensures that we are
  2180   2214   	     * completely cleaned up before we leave this function. 
  2181   2215   	     */
  2182   2216   
  2183   2217   	    WaitForSingleObject(pipePtr->writeThread, INFINITE);
  2184   2218   	    CloseHandle(pipePtr->writeThread);
................................................................................
  2190   2224   	    if (errorCode == 0) {
  2191   2225   		errorCode = errno;
  2192   2226   	    }
  2193   2227   	}
  2194   2228   	pipePtr->validMask &= ~TCL_WRITABLE;
  2195   2229   	pipePtr->writeFile = NULL;
  2196   2230       }
         2231  +
  2197   2232       pipePtr->watchMask &= pipePtr->validMask;
  2198   2233   
  2199   2234       /*
  2200   2235        * Don't free the channel if any of the flags were set.
  2201   2236        */
  2202   2237   
  2203   2238       if (flags) {
................................................................................
  2711   2746   	return 0;
  2712   2747       }
  2713   2748   
  2714   2749       /*
  2715   2750        * Find the process on the process list.
  2716   2751        */
  2717   2752   
  2718         -    Tcl_MutexLock(&procMutex);
         2753  +    Tcl_MutexLock(&pipeMutex);
  2719   2754       prevPtrPtr = &procList;
  2720   2755       for (infoPtr = procList; infoPtr != NULL;
  2721   2756   	    prevPtrPtr = &infoPtr->nextPtr, infoPtr = infoPtr->nextPtr) {
  2722   2757   	 if (infoPtr->hProcess == (HANDLE) pid) {
  2723   2758   	    break;
  2724   2759   	}
  2725   2760       }
  2726         -    Tcl_MutexUnlock(&procMutex);
         2761  +    Tcl_MutexUnlock(&pipeMutex);
  2727   2762   
  2728   2763       /*
  2729   2764        * If the pid is not one of the processes we know about (we started it)
  2730   2765        * then do nothing.
  2731   2766        */
  2732   2767       		     
  2733   2768       if (infoPtr == NULL) {
................................................................................
  2796   2831   TclWinAddProcess(hProcess, id)
  2797   2832       HANDLE hProcess;           /* Handle to process */
  2798   2833       DWORD id;                  /* Global process identifier */
  2799   2834   {
  2800   2835       ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo));
  2801   2836       procPtr->hProcess = hProcess;
  2802   2837       procPtr->dwProcessId = id;
  2803         -    Tcl_MutexLock(&procMutex);
         2838  +    Tcl_MutexLock(&pipeMutex);
  2804   2839       procPtr->nextPtr = procList;
  2805   2840       procList = procPtr;
  2806         -    Tcl_MutexUnlock(&procMutex);
         2841  +    Tcl_MutexUnlock(&pipeMutex);
  2807   2842   }
  2808   2843   
  2809   2844   /*
  2810   2845    *----------------------------------------------------------------------
  2811   2846    *
  2812   2847    * Tcl_PidObjCmd --
  2813   2848    *
................................................................................
  3004   3039    */
  3005   3040   
  3006   3041   static DWORD WINAPI
  3007   3042   PipeReaderThread(LPVOID arg)
  3008   3043   {
  3009   3044       PipeInfo *infoPtr = (PipeInfo *)arg;
  3010   3045       HANDLE *handle = ((WinFile *) infoPtr->readFile)->handle;
  3011         -    DWORD count;
         3046  +    DWORD count, err;
         3047  +    int done = 0;
  3012   3048   
  3013         -    for (;;) {
         3049  +    while (!done) {
  3014   3050   	/*
  3015   3051   	 * Wait for the main thread to signal before attempting to wait.
  3016   3052   	 */
  3017   3053   
  3018   3054   	WaitForSingleObject(infoPtr->startReader, INFINITE);
  3019   3055   
  3020   3056   	/*
................................................................................
  3028   3064   		|| (PeekNamedPipe(handle, NULL, 0, NULL, &count,
  3029   3065   			NULL) == FALSE)) {
  3030   3066   	    /*
  3031   3067   	     * The error is a result of an EOF condition, so set the
  3032   3068   	     * EOF bit before signalling the main thread.
  3033   3069   	     */
  3034   3070   
  3035         -	    if (GetLastError() == ERROR_BROKEN_PIPE) {
         3071  +	    err = GetLastError();
         3072  +	    if (err == ERROR_BROKEN_PIPE) {
  3036   3073   		infoPtr->readFlags |= PIPE_EOF;
         3074  +		done = 1;
         3075  +	    } else if (err == ERROR_INVALID_HANDLE) {
         3076  +		break;
  3037   3077   	    }
  3038   3078   	} else if (count == 0) {
  3039   3079   	    if (ReadFile(handle, &(infoPtr->extraByte), 1, &count, NULL)
  3040   3080   		    != FALSE) {
  3041   3081   		/*
  3042   3082   		 * One byte was consumed as a side effect of waiting
  3043   3083   		 * for the pipe to become readable.
  3044   3084   		 */
  3045   3085   
  3046   3086   		infoPtr->readFlags |= PIPE_EXTRABYTE;
  3047   3087   	    } else {
  3048         -		DWORD err;
  3049   3088   		err = GetLastError();
  3050   3089   		if (err == ERROR_BROKEN_PIPE) {
  3051   3090   		    /*
  3052   3091   		     * The error is a result of an EOF condition, so set the
  3053   3092   		     * EOF bit before signalling the main thread.
  3054   3093   		     */
  3055   3094   
  3056   3095   		    infoPtr->readFlags |= PIPE_EOF;
         3096  +		    done = 1;
         3097  +		} else if (err == ERROR_INVALID_HANDLE) {
         3098  +		    break;
  3057   3099   		}
  3058   3100   	    }
  3059   3101   	}
         3102  +
  3060   3103   		
  3061   3104   	/*
  3062   3105   	 * Signal the main thread by signalling the readable event and
  3063   3106   	 * then waking up the notifier thread.
  3064   3107   	 */
  3065   3108   
  3066   3109   	SetEvent(infoPtr->readable);
         3110  +	
         3111  +	/*
         3112  +	 * Alert the foreground thread.  Note that we need to treat this like
         3113  +	 * a critical section so the foreground thread does not terminate
         3114  +	 * this thread while we are holding a mutex in the notifier code.
         3115  +	 */
         3116  +
         3117  +	Tcl_MutexLock(&pipeMutex);
  3067   3118   	Tcl_ThreadAlert(infoPtr->threadId);
         3119  +	Tcl_MutexUnlock(&pipeMutex);
  3068   3120       }
  3069         -    return 0;			/* NOT REACHED */
         3121  +    return 0;
  3070   3122   }
  3071   3123   
  3072   3124   /*
  3073   3125    *----------------------------------------------------------------------
  3074   3126    *
  3075   3127    * PipeWriterThread --
  3076   3128    *
................................................................................
  3091   3143   PipeWriterThread(LPVOID arg)
  3092   3144   {
  3093   3145   
  3094   3146       PipeInfo *infoPtr = (PipeInfo *)arg;
  3095   3147       HANDLE *handle = ((WinFile *) infoPtr->writeFile)->handle;
  3096   3148       DWORD count, toWrite;
  3097   3149       char *buf;
         3150  +    int done = 0;
  3098   3151   
  3099         -    for (;;) {
         3152  +    while (!done) {
  3100   3153   	/*
  3101   3154   	 * Wait for the main thread to signal before attempting to write.
  3102   3155   	 */
  3103   3156   
  3104   3157   	WaitForSingleObject(infoPtr->startWriter, INFINITE);
  3105   3158   
  3106   3159   	buf = infoPtr->writeBuf;
................................................................................
  3109   3162   	/*
  3110   3163   	 * Loop until all of the bytes are written or an error occurs.
  3111   3164   	 */
  3112   3165   
  3113   3166   	while (toWrite > 0) {
  3114   3167   	    if (WriteFile(handle, buf, toWrite, &count, NULL) == FALSE) {
  3115   3168   		infoPtr->writeError = GetLastError();
         3169  +		done = 1; 
  3116   3170   		break;
  3117   3171   	    } else {
  3118   3172   		toWrite -= count;
  3119   3173   		buf += count;
  3120   3174   	    }
  3121   3175   	}
  3122   3176   	
  3123   3177   	/*
  3124   3178   	 * Signal the main thread by signalling the writable event and
  3125   3179   	 * then waking up the notifier thread.
  3126   3180   	 */
  3127   3181   
  3128   3182   	SetEvent(infoPtr->writable);
         3183  +
         3184  +	/*
         3185  +	 * Alert the foreground thread.  Note that we need to treat this like
         3186  +	 * a critical section so the foreground thread does not terminate
         3187  +	 * this thread while we are holding a mutex in the notifier code.
         3188  +	 */
         3189  +
         3190  +	Tcl_MutexLock(&pipeMutex);
  3129   3191   	Tcl_ThreadAlert(infoPtr->threadId);
         3192  +	Tcl_MutexUnlock(&pipeMutex);
  3130   3193       }
  3131         -    return 0;			/* NOT REACHED */
         3194  +    return 0;
  3132   3195   }
  3133   3196   

Changes to win/tclWinSerial.c.

     5      5    *	and the "serial" channel driver.
     6      6    *
     7      7    * Copyright (c) 1999 by Scriptics Corp.
     8      8    *
     9      9    * See the file "license.terms" for information on usage and redistribution
    10     10    * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    11     11    *
    12         - * RCS: @(#) $Id: tclWinSerial.c,v 1.1.2.5 1999/03/27 00:39:32 redman Exp $
           12  + * RCS: @(#) $Id: tclWinSerial.c,v 1.1.2.6 1999/04/05 21:58:16 stanton Exp $
    13     13    */
    14     14   
    15     15   #include "tclWinInt.h"
    16     16   
    17     17   #include <dos.h>
    18     18   #include <fcntl.h>
    19     19   #include <io.h>
................................................................................
    21     21   
    22     22   /*
    23     23    * The following variable is used to tell whether this module has been
    24     24    * initialized.
    25     25    */
    26     26   
    27     27   static int initialized = 0;
    28         -TCL_DECLARE_MUTEX(procMutex)
           28  +
           29  +/*
           30  + * The serialMutex locks around access to the initialized variable, and it is
           31  + * used to protect background threads from being terminated while they are
           32  + * using APIs that hold locks.
           33  + */
           34  +
           35  +TCL_DECLARE_MUTEX(serialMutex)
    29     36   
    30     37   /*
    31     38    * Bit masks used in the flags field of the SerialInfo structure below.
    32     39    */
    33     40   
    34     41   #define SERIAL_PENDING	(1<<0)	/* Message is pending in the queue. */
    35     42   #define SERIAL_ASYNC	(1<<1)	/* Channel is non-blocking. */
................................................................................
   193    200   
   194    201       /*
   195    202        * Check the initialized flag first, then check it again in the mutex.
   196    203        * This is a speed enhancement.
   197    204        */
   198    205       
   199    206       if (!initialized) {
   200         -	Tcl_MutexLock(&procMutex);
          207  +	Tcl_MutexLock(&serialMutex);
   201    208   	if (!initialized) {
   202    209   	    initialized = 1;
   203    210   	    Tcl_CreateExitHandler(ProcExitHandler, NULL);
   204    211   	}
   205         -	Tcl_MutexUnlock(&procMutex);
          212  +	Tcl_MutexUnlock(&serialMutex);
   206    213       }
   207    214   
   208    215       tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
   209    216       if (tsdPtr == NULL) {
   210    217   	tsdPtr = TCL_TSD_INIT(&dataKey);
   211    218   	tsdPtr->firstSerialPtr = NULL;
   212    219   	Tcl_CreateEventSource(SerialSetupProc, SerialCheckProc, NULL);
................................................................................
   256    263    *----------------------------------------------------------------------
   257    264    */
   258    265   
   259    266   static void
   260    267   ProcExitHandler(
   261    268       ClientData clientData)	/* Old window proc */
   262    269   {
   263         -    Tcl_MutexLock(&procMutex);
          270  +    Tcl_MutexLock(&serialMutex);
   264    271       initialized = 0;
   265         -    Tcl_MutexUnlock(&procMutex);
          272  +    Tcl_MutexUnlock(&serialMutex);
   266    273   }
   267    274   
   268    275   /*
   269    276    *----------------------------------------------------------------------
   270    277    *
   271    278    * SerialSetupProc --
   272    279    *
................................................................................
   450    457       SerialInfo *serialPtr = (SerialInfo *) instanceData;
   451    458       int errorCode, result = 0;
   452    459       SerialInfo *infoPtr, **nextPtrPtr;
   453    460       ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
   454    461   
   455    462       errorCode = 0;
   456    463       if (serialPtr->readThread) {
          464  +	/*
          465  +	 * Forcibly terminate the background thread.  We cannot rely on the
          466  +	 * thread to cleanly terminate itself because we have no way of
          467  +	 * closing the handle without blocking in the case where the
          468  +	 * thread is in the middle of an I/O operation.  Note that we need
          469  +	 * to guard against terminating the thread while it is in the
          470  +	 * middle of Tcl_ThreadAlert because it won't be able to release
          471  +	 * the notifier lock.
          472  +	 */
          473  +
          474  +	Tcl_MutexLock(&serialMutex);
   457    475   	TerminateThread(serialPtr->readThread, 0);
          476  +	Tcl_MutexUnlock(&serialMutex);
          477  +
   458    478   	/*
   459    479   	 * Wait for the thread to terminate.  This ensures that we are
   460    480   	 * completely cleaned up before we leave this function. 
   461    481   	 */
   462    482   
   463    483   	WaitForSingleObject(serialPtr->readThread, INFINITE);
   464    484   	CloseHandle(serialPtr->readThread);
................................................................................
   466    486   	CloseHandle(serialPtr->startReader);
   467    487   	serialPtr->readThread = NULL;
   468    488       }
   469    489       serialPtr->validMask &= ~TCL_READABLE;
   470    490   
   471    491       if (serialPtr->writeThread) {
   472    492   	WaitForSingleObject(serialPtr->writable, INFINITE);
          493  +
          494  +	/*
          495  +	 * Forcibly terminate the background thread.  We cannot rely on the
          496  +	 * thread to cleanly terminate itself because we have no way of
          497  +	 * closing the handle without blocking in the case where the
          498  +	 * thread is in the middle of an I/O operation.  Note that we need
          499  +	 * to guard against terminating the thread while it is in the
          500  +	 * middle of Tcl_ThreadAlert because it won't be able to release
          501  +	 * the notifier lock.
          502  +	 */
          503  +
          504  +	Tcl_MutexLock(&serialMutex);
   473    505   	TerminateThread(serialPtr->writeThread, 0);
          506  +	Tcl_MutexUnlock(&serialMutex);
   474    507   
   475    508   	/*
   476    509   	 * Wait for the thread to terminate.  This ensures that we are
   477    510   	 * completely cleaned up before we leave this function. 
   478    511   	 */
   479    512   
   480    513   	WaitForSingleObject(serialPtr->writeThread, INFINITE);
................................................................................
  1050   1083   
  1051   1084   	/*
  1052   1085   	 * Signal the main thread by signalling the readable event and
  1053   1086   	 * then waking up the notifier thread.
  1054   1087   	 */
  1055   1088   
  1056   1089   	SetEvent(infoPtr->readable);
         1090  +
         1091  +	/*
         1092  +	 * Alert the foreground thread.  Note that we need to treat this like
         1093  +	 * a critical section so the foreground thread does not terminate
         1094  +	 * this thread while we are holding a mutex in the notifier code.
         1095  +	 */
         1096  +
         1097  +	Tcl_MutexLock(&serialMutex);
  1057   1098   	Tcl_ThreadAlert(infoPtr->threadId);
         1099  +	Tcl_MutexUnlock(&serialMutex);
  1058   1100       }
  1059   1101       return 0;			/* NOT REACHED */
  1060   1102   }
  1061   1103   
  1062   1104   /*
  1063   1105    *----------------------------------------------------------------------
  1064   1106    *
................................................................................
  1117   1159   	
  1118   1160   	/*
  1119   1161   	 * Signal the main thread by signalling the writable event and
  1120   1162   	 * then waking up the notifier thread.
  1121   1163   	 */
  1122   1164   
  1123   1165   	SetEvent(infoPtr->writable);
         1166  +
         1167  +	/*
         1168  +	 * Alert the foreground thread.  Note that we need to treat this like
         1169  +	 * a critical section so the foreground thread does not terminate
         1170  +	 * this thread while we are holding a mutex in the notifier code.
         1171  +	 */
         1172  +
         1173  +	Tcl_MutexLock(&serialMutex);
  1124   1174   	Tcl_ThreadAlert(infoPtr->threadId);
         1175  +	Tcl_MutexUnlock(&serialMutex);
  1125   1176       }
  1126   1177       return 0;			/* NOT REACHED */
  1127   1178   }
  1128   1179   
  1129   1180   
  1130   1181   
  1131   1182   /*
  1132   1183    *----------------------------------------------------------------------
  1133   1184    *
  1134         - * TclWinOpenConsoleChannel --
         1185  + * TclWinOpenSerialChannel --
  1135   1186    *
  1136   1187    *	Constructs a Serial port channel for the specified standard OS handle.
  1137   1188    *      This is a helper function to break up the construction of 
  1138   1189    *      channels into File, Console, or Serial.
  1139   1190    *
  1140   1191    * Results:
  1141   1192    *	Returns the new channel, or NULL.