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 Unified Diffs Ignore Whitespace Patch

Changes to win/tclWinConsole.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
21
22
23
24
25
26
27
28
29







30
31
32
33
34
35
36
...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
...
464
465
466
467
468
469
470











471

472
473
474
475
476
477
478
...
487
488
489
490
491
492
493












494

495
496
497
498
499
500
501
....
1066
1067
1068
1069
1070
1071
1072








1073

1074
1075
1076
1077
1078
1079
1080
....
1128
1129
1130
1131
1132
1133
1134








1135

1136
1137
1138
1139
1140
1141
1142
 *	and the "console" channel driver.
 *
 * Copyright (c) 1999 by Scriptics Corp.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tclWinConsole.c,v 1.1.2.6 1999/04/01 00:56:12 redman Exp $
 */

#include "tclWinInt.h"

#include <dos.h>
#include <fcntl.h>
#include <io.h>
................................................................................

/*
 * The following variable is used to tell whether this module has been
 * initialized.
 */

static int initialized = 0;
TCL_DECLARE_MUTEX(procMutex)









/*
 * Bit masks used in the flags field of the ConsoleInfo structure below.
 */

#define CONSOLE_PENDING	(1<<0)	/* Message is pending in the queue. */
#define CONSOLE_ASYNC	(1<<1)	/* Channel is non-blocking. */
................................................................................

    /*
     * Check the initialized flag first, then check again in the mutex.
     * This is a speed enhancement.
     */

    if (!initialized) {
	Tcl_MutexLock(&procMutex);
	if (!initialized) {
	    initialized = 1;
	    Tcl_CreateExitHandler(ProcExitHandler, NULL);
	}
	Tcl_MutexUnlock(&procMutex);
    }

    tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
    if (tsdPtr == NULL) {
	tsdPtr = TCL_TSD_INIT(&dataKey);
	tsdPtr->firstConsolePtr = NULL;
	Tcl_CreateEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
................................................................................
 *----------------------------------------------------------------------
 */

static void
ProcExitHandler(
    ClientData clientData)	/* Old window proc */
{
    Tcl_MutexLock(&procMutex);
    initialized = 0;
    Tcl_MutexUnlock(&procMutex);
}
 
/*
 *----------------------------------------------------------------------
 *
 * ConsoleSetupProc --
 *
................................................................................
    /*
     * Clean up the background thread if necessary.  Note that this
     * must be done before we can close the file, since the 
     * thread may be blocking trying to read from the console.
     */
    
    if (consolePtr->readThread) {











	TerminateThread(consolePtr->readThread, 0);


	/*
	 * Wait for the thread to terminate.  This ensures that we are
	 * completely cleaned up before we leave this function. 
	 */

	WaitForSingleObject(consolePtr->readThread, INFINITE);
................................................................................
     * Wait for the writer thread to finish the current buffer, then
     * terminate the thread and close the handles.  If the channel is
     * nonblocking, there should be no pending write operations.
     */
    
    if (consolePtr->writeThread) {
	WaitForSingleObject(consolePtr->writable, INFINITE);












	TerminateThread(consolePtr->writeThread, 0);


	/*
	 * Wait for the thread to terminate.  This ensures that we are
	 * completely cleaned up before we leave this function. 
	 */

	WaitForSingleObject(consolePtr->writeThread, INFINITE);
................................................................................

	/*
	 * Signal the main thread by signalling the readable event and
	 * then waking up the notifier thread.
	 */

	SetEvent(infoPtr->readable);








	Tcl_ThreadAlert(infoPtr->threadId);

    }
    return 0;			/* NOT REACHED */
}
 
/*
 *----------------------------------------------------------------------
 *
................................................................................

	/*
	 * Signal the main thread by signalling the writable event and
	 * then waking up the notifier thread.
	 */
	
	SetEvent(infoPtr->writable);








	Tcl_ThreadAlert(infoPtr->threadId);

    }
    return 0;			/* NOT REACHED */
}


 
/*






|







 







<

>
>
>
>
>
>
>







 







|




|







 







|

|







 







>
>
>
>
>
>
>
>
>
>
>

>







 







>
>
>
>
>
>
>
>
>
>
>
>

>







 







>
>
>
>
>
>
>
>

>







 







>
>
>
>
>
>
>
>

>







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
21
22
23
24
25
26
27

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
...
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
...
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
...
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
....
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
....
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
 *	and the "console" channel driver.
 *
 * Copyright (c) 1999 by Scriptics Corp.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tclWinConsole.c,v 1.1.2.7 1999/04/05 21:58:15 stanton Exp $
 */

#include "tclWinInt.h"

#include <dos.h>
#include <fcntl.h>
#include <io.h>
................................................................................

/*
 * The following variable is used to tell whether this module has been
 * initialized.
 */

static int initialized = 0;


/*
 * The consoleMutex locks around access to the initialized variable, and it is
 * used to protect background threads from being terminated while they are
 * using APIs that hold locks.
 */

TCL_DECLARE_MUTEX(consoleMutex)

/*
 * Bit masks used in the flags field of the ConsoleInfo structure below.
 */

#define CONSOLE_PENDING	(1<<0)	/* Message is pending in the queue. */
#define CONSOLE_ASYNC	(1<<1)	/* Channel is non-blocking. */
................................................................................

    /*
     * Check the initialized flag first, then check again in the mutex.
     * This is a speed enhancement.
     */

    if (!initialized) {
	Tcl_MutexLock(&consoleMutex);
	if (!initialized) {
	    initialized = 1;
	    Tcl_CreateExitHandler(ProcExitHandler, NULL);
	}
	Tcl_MutexUnlock(&consoleMutex);
    }

    tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
    if (tsdPtr == NULL) {
	tsdPtr = TCL_TSD_INIT(&dataKey);
	tsdPtr->firstConsolePtr = NULL;
	Tcl_CreateEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
................................................................................
 *----------------------------------------------------------------------
 */

static void
ProcExitHandler(
    ClientData clientData)	/* Old window proc */
{
    Tcl_MutexLock(&consoleMutex);
    initialized = 0;
    Tcl_MutexUnlock(&consoleMutex);
}
 
/*
 *----------------------------------------------------------------------
 *
 * ConsoleSetupProc --
 *
................................................................................
    /*
     * Clean up the background thread if necessary.  Note that this
     * must be done before we can close the file, since the 
     * thread may be blocking trying to read from the console.
     */
    
    if (consolePtr->readThread) {
	/*
	 * Forcibly terminate the background thread.  We cannot rely on the
	 * thread to cleanly terminate itself because we have no way of
	 * closing the handle without blocking in the case where the
	 * thread is in the middle of an I/O operation.  Note that we need
	 * to guard against terminating the thread while it is in the
	 * middle of Tcl_ThreadAlert because it won't be able to release
	 * the notifier lock.
	 */

	Tcl_MutexLock(&consoleMutex);
	TerminateThread(consolePtr->readThread, 0);
	Tcl_MutexUnlock(&consoleMutex);

	/*
	 * Wait for the thread to terminate.  This ensures that we are
	 * completely cleaned up before we leave this function. 
	 */

	WaitForSingleObject(consolePtr->readThread, INFINITE);
................................................................................
     * Wait for the writer thread to finish the current buffer, then
     * terminate the thread and close the handles.  If the channel is
     * nonblocking, there should be no pending write operations.
     */
    
    if (consolePtr->writeThread) {
	WaitForSingleObject(consolePtr->writable, INFINITE);

	/*
	 * Forcibly terminate the background thread.  We cannot rely on the
	 * thread to cleanly terminate itself because we have no way of
	 * closing the handle without blocking in the case where the
	 * thread is in the middle of an I/O operation.  Note that we need
	 * to guard against terminating the thread while it is in the
	 * middle of Tcl_ThreadAlert because it won't be able to release
	 * the notifier lock.
	 */

	Tcl_MutexLock(&consoleMutex);
	TerminateThread(consolePtr->writeThread, 0);
	Tcl_MutexUnlock(&consoleMutex);

	/*
	 * Wait for the thread to terminate.  This ensures that we are
	 * completely cleaned up before we leave this function. 
	 */

	WaitForSingleObject(consolePtr->writeThread, INFINITE);
................................................................................

	/*
	 * Signal the main thread by signalling the readable event and
	 * then waking up the notifier thread.
	 */

	SetEvent(infoPtr->readable);

	/*
	 * Alert the foreground thread.  Note that we need to treat this like
	 * a critical section so the foreground thread does not terminate
	 * this thread while we are holding a mutex in the notifier code.
	 */

	Tcl_MutexLock(&consoleMutex);
	Tcl_ThreadAlert(infoPtr->threadId);
	Tcl_MutexUnlock(&consoleMutex);
    }
    return 0;			/* NOT REACHED */
}
 
/*
 *----------------------------------------------------------------------
 *
................................................................................

	/*
	 * Signal the main thread by signalling the writable event and
	 * then waking up the notifier thread.
	 */
	
	SetEvent(infoPtr->writable);

	/*
	 * Alert the foreground thread.  Note that we need to treat this like
	 * a critical section so the foreground thread does not terminate
	 * this thread while we are holding a mutex in the notifier code.
	 */

	Tcl_MutexLock(&consoleMutex);
	Tcl_ThreadAlert(infoPtr->threadId);
	Tcl_MutexUnlock(&consoleMutex);
    }
    return 0;			/* NOT REACHED */
}


 
/*

Changes to win/tclWinPipe.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
21
22
23
24
25
26
27







28
29
30
31
32
33
34
35
...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
...
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
...
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
....
2141
2142
2143
2144
2145
2146
2147











2148

2149
2150
2151
2152
2153
2154
2155
....
2170
2171
2172
2173
2174
2175
2176












2177



2178
2179
2180
2181
2182
2183
2184
....
2190
2191
2192
2193
2194
2195
2196

2197
2198
2199
2200
2201
2202
2203
....
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
....
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
....
3004
3005
3006
3007
3008
3009
3010
3011

3012
3013

3014
3015
3016
3017
3018
3019
3020
....
3028
3029
3030
3031
3032
3033
3034

3035
3036



3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056



3057
3058
3059

3060
3061
3062
3063
3064
3065
3066








3067

3068
3069
3070
3071
3072
3073
3074
3075
3076
....
3091
3092
3093
3094
3095
3096
3097

3098
3099

3100
3101
3102
3103
3104
3105
3106
....
3109
3110
3111
3112
3113
3114
3115

3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128








3129

3130
3131
3132
3133
 *	the "pipe" channel driver, and the "pid" Tcl command.
 *
 * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tclWinPipe.c,v 1.1.2.9 1999/03/27 00:39:31 redman Exp $
 */

#include "tclWinInt.h"

#include <dos.h>
#include <fcntl.h>
#include <io.h>
................................................................................

/*
 * The following variable is used to tell whether this module has been
 * initialized.
 */

static int initialized = 0;







TCL_DECLARE_MUTEX(procMutex)

/*
 * The following defines identify the various types of applications that 
 * run under windows.  There is special case code for the various types.
 */

#define APPL_NONE	0
................................................................................

    /*
     * Check the initialized flag first, then check again in the mutex.
     * This is a speed enhancement.
     */

    if (!initialized) {
	Tcl_MutexLock(&procMutex);
	if (!initialized) {
	    initialized = 1;
	    procList = NULL;
	    Tcl_CreateExitHandler(ProcExitHandler, NULL);
	}
	Tcl_MutexUnlock(&procMutex);
    }

    tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
    if (tsdPtr == NULL) {
	tsdPtr = TCL_TSD_INIT(&dataKey);
	tsdPtr->firstPipePtr = NULL;
	Tcl_CreateEventSource(PipeSetupProc, PipeCheckProc, NULL);
................................................................................
 *----------------------------------------------------------------------
 */

static void
ProcExitHandler(
    ClientData clientData)	/* Old window proc */
{
    Tcl_MutexLock(&procMutex);
    initialized = 0;
    Tcl_MutexUnlock(&procMutex);
}
 
/*
 *----------------------------------------------------------------------
 *
 * PipeSetupProc --
 *
................................................................................

unsigned long
TclpGetPid(
    Tcl_Pid pid)		/* The HANDLE of the child process. */
{
    ProcInfo *infoPtr;

    Tcl_MutexLock(&procMutex);
    for (infoPtr = procList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
	if (infoPtr->hProcess == (HANDLE) pid) {
	    Tcl_MutexUnlock(&procMutex);
	    return infoPtr->dwProcessId;
	}
    }
    Tcl_MutexUnlock(&procMutex);
    return (unsigned long) -1;
}
 
/*
 *----------------------------------------------------------------------
 *
 * TclpCreateProcess --
................................................................................
	/*
	 * Clean up the background thread if necessary.  Note that this
	 * must be done before we can close the file, since the 
	 * thread may be blocking trying to read from the pipe.
	 */

	if (pipePtr->readThread) {











	    TerminateThread(pipePtr->readThread, 0);


	    /*
	     * Wait for the thread to terminate.  This ensures that we are
	     * completely cleaned up before we leave this function. 
	     */

	    WaitForSingleObject(pipePtr->readThread, INFINITE);
................................................................................
	 * Wait for the writer thread to finish the current buffer, then
	 * terminate the thread and close the handles.  If the channel is
	 * nonblocking, there should be no pending write operations.
	 */

	if (pipePtr->writeThread) {
	    WaitForSingleObject(pipePtr->writable, INFINITE);












	    TerminateThread(pipePtr->writeThread, 0);



	    /*
	     * Wait for the thread to terminate.  This ensures that we are
	     * completely cleaned up before we leave this function. 
	     */

	    WaitForSingleObject(pipePtr->writeThread, INFINITE);
	    CloseHandle(pipePtr->writeThread);
................................................................................
	    if (errorCode == 0) {
		errorCode = errno;
	    }
	}
	pipePtr->validMask &= ~TCL_WRITABLE;
	pipePtr->writeFile = NULL;
    }

    pipePtr->watchMask &= pipePtr->validMask;

    /*
     * Don't free the channel if any of the flags were set.
     */

    if (flags) {
................................................................................
	return 0;
    }

    /*
     * Find the process on the process list.
     */

    Tcl_MutexLock(&procMutex);
    prevPtrPtr = &procList;
    for (infoPtr = procList; infoPtr != NULL;
	    prevPtrPtr = &infoPtr->nextPtr, infoPtr = infoPtr->nextPtr) {
	 if (infoPtr->hProcess == (HANDLE) pid) {
	    break;
	}
    }
    Tcl_MutexUnlock(&procMutex);

    /*
     * If the pid is not one of the processes we know about (we started it)
     * then do nothing.
     */
    		     
    if (infoPtr == NULL) {
................................................................................
TclWinAddProcess(hProcess, id)
    HANDLE hProcess;           /* Handle to process */
    DWORD id;                  /* Global process identifier */
{
    ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo));
    procPtr->hProcess = hProcess;
    procPtr->dwProcessId = id;
    Tcl_MutexLock(&procMutex);
    procPtr->nextPtr = procList;
    procList = procPtr;
    Tcl_MutexUnlock(&procMutex);
}
 
/*
 *----------------------------------------------------------------------
 *
 * Tcl_PidObjCmd --
 *
................................................................................
 */

static DWORD WINAPI
PipeReaderThread(LPVOID arg)
{
    PipeInfo *infoPtr = (PipeInfo *)arg;
    HANDLE *handle = ((WinFile *) infoPtr->readFile)->handle;
    DWORD count;


    for (;;) {

	/*
	 * Wait for the main thread to signal before attempting to wait.
	 */

	WaitForSingleObject(infoPtr->startReader, INFINITE);

	/*
................................................................................
		|| (PeekNamedPipe(handle, NULL, 0, NULL, &count,
			NULL) == FALSE)) {
	    /*
	     * The error is a result of an EOF condition, so set the
	     * EOF bit before signalling the main thread.
	     */


	    if (GetLastError() == ERROR_BROKEN_PIPE) {
		infoPtr->readFlags |= PIPE_EOF;



	    }
	} else if (count == 0) {
	    if (ReadFile(handle, &(infoPtr->extraByte), 1, &count, NULL)
		    != FALSE) {
		/*
		 * One byte was consumed as a side effect of waiting
		 * for the pipe to become readable.
		 */

		infoPtr->readFlags |= PIPE_EXTRABYTE;
	    } else {
		DWORD err;
		err = GetLastError();
		if (err == ERROR_BROKEN_PIPE) {
		    /*
		     * The error is a result of an EOF condition, so set the
		     * EOF bit before signalling the main thread.
		     */

		    infoPtr->readFlags |= PIPE_EOF;



		}
	    }
	}

		
	/*
	 * Signal the main thread by signalling the readable event and
	 * then waking up the notifier thread.
	 */

	SetEvent(infoPtr->readable);








	Tcl_ThreadAlert(infoPtr->threadId);

    }
    return 0;			/* NOT REACHED */
}
 
/*
 *----------------------------------------------------------------------
 *
 * PipeWriterThread --
 *
................................................................................
PipeWriterThread(LPVOID arg)
{

    PipeInfo *infoPtr = (PipeInfo *)arg;
    HANDLE *handle = ((WinFile *) infoPtr->writeFile)->handle;
    DWORD count, toWrite;
    char *buf;


    for (;;) {

	/*
	 * Wait for the main thread to signal before attempting to write.
	 */

	WaitForSingleObject(infoPtr->startWriter, INFINITE);

	buf = infoPtr->writeBuf;
................................................................................
	/*
	 * Loop until all of the bytes are written or an error occurs.
	 */

	while (toWrite > 0) {
	    if (WriteFile(handle, buf, toWrite, &count, NULL) == FALSE) {
		infoPtr->writeError = GetLastError();

		break;
	    } else {
		toWrite -= count;
		buf += count;
	    }
	}
	
	/*
	 * Signal the main thread by signalling the writable event and
	 * then waking up the notifier thread.
	 */

	SetEvent(infoPtr->writable);








	Tcl_ThreadAlert(infoPtr->threadId);

    }
    return 0;			/* NOT REACHED */
}







|







 







>
>
>
>
>
>
>
|







 







|





|







 







|

|







 







|


|



|







 







>
>
>
>
>
>
>
>
>
>
>

>







 







>
>
>
>
>
>
>
>
>
>
>
>

>
>
>







 







>







 







|







|







 







|


|







 







|
>

<
>







 







>
|

>
>
>











<








>
>
>



>







>
>
>
>
>
>
>
>

>

|







 







>

<
>







 







>













>
>
>
>
>
>
>
>

>

|


5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
...
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
...
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
...
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
....
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
....
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
....
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
....
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
....
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
....
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048

3049
3050
3051
3052
3053
3054
3055
3056
....
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087

3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
....
3143
3144
3145
3146
3147
3148
3149
3150
3151

3152
3153
3154
3155
3156
3157
3158
3159
....
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
 *	the "pipe" channel driver, and the "pid" Tcl command.
 *
 * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tclWinPipe.c,v 1.1.2.10 1999/04/05 21:58:16 stanton Exp $
 */

#include "tclWinInt.h"

#include <dos.h>
#include <fcntl.h>
#include <io.h>
................................................................................

/*
 * The following variable is used to tell whether this module has been
 * initialized.
 */

static int initialized = 0;

/*
 * The pipeMutex locks around access to the initialized and procList variables,
 * and it is used to protect background threads from being terminated while
 * they are using APIs that hold locks.
 */

TCL_DECLARE_MUTEX(pipeMutex)

/*
 * The following defines identify the various types of applications that 
 * run under windows.  There is special case code for the various types.
 */

#define APPL_NONE	0
................................................................................

    /*
     * Check the initialized flag first, then check again in the mutex.
     * This is a speed enhancement.
     */

    if (!initialized) {
	Tcl_MutexLock(&pipeMutex);
	if (!initialized) {
	    initialized = 1;
	    procList = NULL;
	    Tcl_CreateExitHandler(ProcExitHandler, NULL);
	}
	Tcl_MutexUnlock(&pipeMutex);
    }

    tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
    if (tsdPtr == NULL) {
	tsdPtr = TCL_TSD_INIT(&dataKey);
	tsdPtr->firstPipePtr = NULL;
	Tcl_CreateEventSource(PipeSetupProc, PipeCheckProc, NULL);
................................................................................
 *----------------------------------------------------------------------
 */

static void
ProcExitHandler(
    ClientData clientData)	/* Old window proc */
{
    Tcl_MutexLock(&pipeMutex);
    initialized = 0;
    Tcl_MutexUnlock(&pipeMutex);
}
 
/*
 *----------------------------------------------------------------------
 *
 * PipeSetupProc --
 *
................................................................................

unsigned long
TclpGetPid(
    Tcl_Pid pid)		/* The HANDLE of the child process. */
{
    ProcInfo *infoPtr;

    Tcl_MutexLock(&pipeMutex);
    for (infoPtr = procList; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
	if (infoPtr->hProcess == (HANDLE) pid) {
	    Tcl_MutexUnlock(&pipeMutex);
	    return infoPtr->dwProcessId;
	}
    }
    Tcl_MutexUnlock(&pipeMutex);
    return (unsigned long) -1;
}
 
/*
 *----------------------------------------------------------------------
 *
 * TclpCreateProcess --
................................................................................
	/*
	 * Clean up the background thread if necessary.  Note that this
	 * must be done before we can close the file, since the 
	 * thread may be blocking trying to read from the pipe.
	 */

	if (pipePtr->readThread) {
	    /*
	     * Forcibly terminate the background thread.  We cannot rely on the
	     * thread to cleanly terminate itself because we have no way of
	     * closing the pipe handle without blocking in the case where the
	     * thread is in the middle of an I/O operation.  Note that we need
	     * to guard against terminating the thread while it is in the
	     * middle of Tcl_ThreadAlert because it won't be able to release
	     * the notifier lock.
	     */

	    Tcl_MutexLock(&pipeMutex);
	    TerminateThread(pipePtr->readThread, 0);
	    Tcl_MutexUnlock(&pipeMutex);

	    /*
	     * Wait for the thread to terminate.  This ensures that we are
	     * completely cleaned up before we leave this function. 
	     */

	    WaitForSingleObject(pipePtr->readThread, INFINITE);
................................................................................
	 * Wait for the writer thread to finish the current buffer, then
	 * terminate the thread and close the handles.  If the channel is
	 * nonblocking, there should be no pending write operations.
	 */

	if (pipePtr->writeThread) {
	    WaitForSingleObject(pipePtr->writable, INFINITE);

	    /*
	     * Forcibly terminate the background thread.  We cannot rely on the
	     * thread to cleanly terminate itself because we have no way of
	     * closing the pipe handle without blocking in the case where the
	     * thread is in the middle of an I/O operation.  Note that we need
	     * to guard against terminating the thread while it is in the
	     * middle of Tcl_ThreadAlert because it won't be able to release
	     * the notifier lock.
	     */

	    Tcl_MutexLock(&pipeMutex);
	    TerminateThread(pipePtr->writeThread, 0);
	    Tcl_MutexUnlock(&pipeMutex);


	    /*
	     * Wait for the thread to terminate.  This ensures that we are
	     * completely cleaned up before we leave this function. 
	     */

	    WaitForSingleObject(pipePtr->writeThread, INFINITE);
	    CloseHandle(pipePtr->writeThread);
................................................................................
	    if (errorCode == 0) {
		errorCode = errno;
	    }
	}
	pipePtr->validMask &= ~TCL_WRITABLE;
	pipePtr->writeFile = NULL;
    }

    pipePtr->watchMask &= pipePtr->validMask;

    /*
     * Don't free the channel if any of the flags were set.
     */

    if (flags) {
................................................................................
	return 0;
    }

    /*
     * Find the process on the process list.
     */

    Tcl_MutexLock(&pipeMutex);
    prevPtrPtr = &procList;
    for (infoPtr = procList; infoPtr != NULL;
	    prevPtrPtr = &infoPtr->nextPtr, infoPtr = infoPtr->nextPtr) {
	 if (infoPtr->hProcess == (HANDLE) pid) {
	    break;
	}
    }
    Tcl_MutexUnlock(&pipeMutex);

    /*
     * If the pid is not one of the processes we know about (we started it)
     * then do nothing.
     */
    		     
    if (infoPtr == NULL) {
................................................................................
TclWinAddProcess(hProcess, id)
    HANDLE hProcess;           /* Handle to process */
    DWORD id;                  /* Global process identifier */
{
    ProcInfo *procPtr = (ProcInfo *) ckalloc(sizeof(ProcInfo));
    procPtr->hProcess = hProcess;
    procPtr->dwProcessId = id;
    Tcl_MutexLock(&pipeMutex);
    procPtr->nextPtr = procList;
    procList = procPtr;
    Tcl_MutexUnlock(&pipeMutex);
}
 
/*
 *----------------------------------------------------------------------
 *
 * Tcl_PidObjCmd --
 *
................................................................................
 */

static DWORD WINAPI
PipeReaderThread(LPVOID arg)
{
    PipeInfo *infoPtr = (PipeInfo *)arg;
    HANDLE *handle = ((WinFile *) infoPtr->readFile)->handle;
    DWORD count, err;
    int done = 0;


    while (!done) {
	/*
	 * Wait for the main thread to signal before attempting to wait.
	 */

	WaitForSingleObject(infoPtr->startReader, INFINITE);

	/*
................................................................................
		|| (PeekNamedPipe(handle, NULL, 0, NULL, &count,
			NULL) == FALSE)) {
	    /*
	     * The error is a result of an EOF condition, so set the
	     * EOF bit before signalling the main thread.
	     */

	    err = GetLastError();
	    if (err == ERROR_BROKEN_PIPE) {
		infoPtr->readFlags |= PIPE_EOF;
		done = 1;
	    } else if (err == ERROR_INVALID_HANDLE) {
		break;
	    }
	} else if (count == 0) {
	    if (ReadFile(handle, &(infoPtr->extraByte), 1, &count, NULL)
		    != FALSE) {
		/*
		 * One byte was consumed as a side effect of waiting
		 * for the pipe to become readable.
		 */

		infoPtr->readFlags |= PIPE_EXTRABYTE;
	    } else {

		err = GetLastError();
		if (err == ERROR_BROKEN_PIPE) {
		    /*
		     * The error is a result of an EOF condition, so set the
		     * EOF bit before signalling the main thread.
		     */

		    infoPtr->readFlags |= PIPE_EOF;
		    done = 1;
		} else if (err == ERROR_INVALID_HANDLE) {
		    break;
		}
	    }
	}

		
	/*
	 * Signal the main thread by signalling the readable event and
	 * then waking up the notifier thread.
	 */

	SetEvent(infoPtr->readable);
	
	/*
	 * Alert the foreground thread.  Note that we need to treat this like
	 * a critical section so the foreground thread does not terminate
	 * this thread while we are holding a mutex in the notifier code.
	 */

	Tcl_MutexLock(&pipeMutex);
	Tcl_ThreadAlert(infoPtr->threadId);
	Tcl_MutexUnlock(&pipeMutex);
    }
    return 0;
}
 
/*
 *----------------------------------------------------------------------
 *
 * PipeWriterThread --
 *
................................................................................
PipeWriterThread(LPVOID arg)
{

    PipeInfo *infoPtr = (PipeInfo *)arg;
    HANDLE *handle = ((WinFile *) infoPtr->writeFile)->handle;
    DWORD count, toWrite;
    char *buf;
    int done = 0;


    while (!done) {
	/*
	 * Wait for the main thread to signal before attempting to write.
	 */

	WaitForSingleObject(infoPtr->startWriter, INFINITE);

	buf = infoPtr->writeBuf;
................................................................................
	/*
	 * Loop until all of the bytes are written or an error occurs.
	 */

	while (toWrite > 0) {
	    if (WriteFile(handle, buf, toWrite, &count, NULL) == FALSE) {
		infoPtr->writeError = GetLastError();
		done = 1; 
		break;
	    } else {
		toWrite -= count;
		buf += count;
	    }
	}
	
	/*
	 * Signal the main thread by signalling the writable event and
	 * then waking up the notifier thread.
	 */

	SetEvent(infoPtr->writable);

	/*
	 * Alert the foreground thread.  Note that we need to treat this like
	 * a critical section so the foreground thread does not terminate
	 * this thread while we are holding a mutex in the notifier code.
	 */

	Tcl_MutexLock(&pipeMutex);
	Tcl_ThreadAlert(infoPtr->threadId);
	Tcl_MutexUnlock(&pipeMutex);
    }
    return 0;
}

Changes to win/tclWinSerial.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
21
22
23
24
25
26
27







28
29
30
31
32
33
34
35
...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
...
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
...
450
451
452
453
454
455
456











457


458
459
460
461
462
463
464
...
466
467
468
469
470
471
472












473

474
475
476
477
478
479
480
....
1050
1051
1052
1053
1054
1055
1056








1057

1058
1059
1060
1061
1062
1063
1064
....
1117
1118
1119
1120
1121
1122
1123








1124

1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
 *	and the "serial" channel driver.
 *
 * Copyright (c) 1999 by Scriptics Corp.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tclWinSerial.c,v 1.1.2.5 1999/03/27 00:39:32 redman Exp $
 */

#include "tclWinInt.h"

#include <dos.h>
#include <fcntl.h>
#include <io.h>
................................................................................

/*
 * The following variable is used to tell whether this module has been
 * initialized.
 */

static int initialized = 0;







TCL_DECLARE_MUTEX(procMutex)

/*
 * Bit masks used in the flags field of the SerialInfo structure below.
 */

#define SERIAL_PENDING	(1<<0)	/* Message is pending in the queue. */
#define SERIAL_ASYNC	(1<<1)	/* Channel is non-blocking. */
................................................................................

    /*
     * Check the initialized flag first, then check it again in the mutex.
     * This is a speed enhancement.
     */
    
    if (!initialized) {
	Tcl_MutexLock(&procMutex);
	if (!initialized) {
	    initialized = 1;
	    Tcl_CreateExitHandler(ProcExitHandler, NULL);
	}
	Tcl_MutexUnlock(&procMutex);
    }

    tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
    if (tsdPtr == NULL) {
	tsdPtr = TCL_TSD_INIT(&dataKey);
	tsdPtr->firstSerialPtr = NULL;
	Tcl_CreateEventSource(SerialSetupProc, SerialCheckProc, NULL);
................................................................................
 *----------------------------------------------------------------------
 */

static void
ProcExitHandler(
    ClientData clientData)	/* Old window proc */
{
    Tcl_MutexLock(&procMutex);
    initialized = 0;
    Tcl_MutexUnlock(&procMutex);
}
 
/*
 *----------------------------------------------------------------------
 *
 * SerialSetupProc --
 *
................................................................................
    SerialInfo *serialPtr = (SerialInfo *) instanceData;
    int errorCode, result = 0;
    SerialInfo *infoPtr, **nextPtrPtr;
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    errorCode = 0;
    if (serialPtr->readThread) {











	TerminateThread(serialPtr->readThread, 0);


	/*
	 * Wait for the thread to terminate.  This ensures that we are
	 * completely cleaned up before we leave this function. 
	 */

	WaitForSingleObject(serialPtr->readThread, INFINITE);
	CloseHandle(serialPtr->readThread);
................................................................................
	CloseHandle(serialPtr->startReader);
	serialPtr->readThread = NULL;
    }
    serialPtr->validMask &= ~TCL_READABLE;

    if (serialPtr->writeThread) {
	WaitForSingleObject(serialPtr->writable, INFINITE);












	TerminateThread(serialPtr->writeThread, 0);


	/*
	 * Wait for the thread to terminate.  This ensures that we are
	 * completely cleaned up before we leave this function. 
	 */

	WaitForSingleObject(serialPtr->writeThread, INFINITE);
................................................................................

	/*
	 * Signal the main thread by signalling the readable event and
	 * then waking up the notifier thread.
	 */

	SetEvent(infoPtr->readable);








	Tcl_ThreadAlert(infoPtr->threadId);

    }
    return 0;			/* NOT REACHED */
}
 
/*
 *----------------------------------------------------------------------
 *
................................................................................
	
	/*
	 * Signal the main thread by signalling the writable event and
	 * then waking up the notifier thread.
	 */

	SetEvent(infoPtr->writable);








	Tcl_ThreadAlert(infoPtr->threadId);

    }
    return 0;			/* NOT REACHED */
}


 
/*
 *----------------------------------------------------------------------
 *
 * TclWinOpenConsoleChannel --
 *
 *	Constructs a Serial port channel for the specified standard OS handle.
 *      This is a helper function to break up the construction of 
 *      channels into File, Console, or Serial.
 *
 * Results:
 *	Returns the new channel, or NULL.






|







 







>
>
>
>
>
>
>
|







 







|




|







 







|

|







 







>
>
>
>
>
>
>
>
>
>
>

>
>







 







>
>
>
>
>
>
>
>
>
>
>
>

>







 







>
>
>
>
>
>
>
>

>







 







>
>
>
>
>
>
>
>

>









|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
...
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
...
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
...
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
...
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
....
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
....
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
 *	and the "serial" channel driver.
 *
 * Copyright (c) 1999 by Scriptics Corp.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * RCS: @(#) $Id: tclWinSerial.c,v 1.1.2.6 1999/04/05 21:58:16 stanton Exp $
 */

#include "tclWinInt.h"

#include <dos.h>
#include <fcntl.h>
#include <io.h>
................................................................................

/*
 * The following variable is used to tell whether this module has been
 * initialized.
 */

static int initialized = 0;

/*
 * The serialMutex locks around access to the initialized variable, and it is
 * used to protect background threads from being terminated while they are
 * using APIs that hold locks.
 */

TCL_DECLARE_MUTEX(serialMutex)

/*
 * Bit masks used in the flags field of the SerialInfo structure below.
 */

#define SERIAL_PENDING	(1<<0)	/* Message is pending in the queue. */
#define SERIAL_ASYNC	(1<<1)	/* Channel is non-blocking. */
................................................................................

    /*
     * Check the initialized flag first, then check it again in the mutex.
     * This is a speed enhancement.
     */
    
    if (!initialized) {
	Tcl_MutexLock(&serialMutex);
	if (!initialized) {
	    initialized = 1;
	    Tcl_CreateExitHandler(ProcExitHandler, NULL);
	}
	Tcl_MutexUnlock(&serialMutex);
    }

    tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
    if (tsdPtr == NULL) {
	tsdPtr = TCL_TSD_INIT(&dataKey);
	tsdPtr->firstSerialPtr = NULL;
	Tcl_CreateEventSource(SerialSetupProc, SerialCheckProc, NULL);
................................................................................
 *----------------------------------------------------------------------
 */

static void
ProcExitHandler(
    ClientData clientData)	/* Old window proc */
{
    Tcl_MutexLock(&serialMutex);
    initialized = 0;
    Tcl_MutexUnlock(&serialMutex);
}
 
/*
 *----------------------------------------------------------------------
 *
 * SerialSetupProc --
 *
................................................................................
    SerialInfo *serialPtr = (SerialInfo *) instanceData;
    int errorCode, result = 0;
    SerialInfo *infoPtr, **nextPtrPtr;
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);

    errorCode = 0;
    if (serialPtr->readThread) {
	/*
	 * Forcibly terminate the background thread.  We cannot rely on the
	 * thread to cleanly terminate itself because we have no way of
	 * closing the handle without blocking in the case where the
	 * thread is in the middle of an I/O operation.  Note that we need
	 * to guard against terminating the thread while it is in the
	 * middle of Tcl_ThreadAlert because it won't be able to release
	 * the notifier lock.
	 */

	Tcl_MutexLock(&serialMutex);
	TerminateThread(serialPtr->readThread, 0);
	Tcl_MutexUnlock(&serialMutex);

	/*
	 * Wait for the thread to terminate.  This ensures that we are
	 * completely cleaned up before we leave this function. 
	 */

	WaitForSingleObject(serialPtr->readThread, INFINITE);
	CloseHandle(serialPtr->readThread);
................................................................................
	CloseHandle(serialPtr->startReader);
	serialPtr->readThread = NULL;
    }
    serialPtr->validMask &= ~TCL_READABLE;

    if (serialPtr->writeThread) {
	WaitForSingleObject(serialPtr->writable, INFINITE);

	/*
	 * Forcibly terminate the background thread.  We cannot rely on the
	 * thread to cleanly terminate itself because we have no way of
	 * closing the handle without blocking in the case where the
	 * thread is in the middle of an I/O operation.  Note that we need
	 * to guard against terminating the thread while it is in the
	 * middle of Tcl_ThreadAlert because it won't be able to release
	 * the notifier lock.
	 */

	Tcl_MutexLock(&serialMutex);
	TerminateThread(serialPtr->writeThread, 0);
	Tcl_MutexUnlock(&serialMutex);

	/*
	 * Wait for the thread to terminate.  This ensures that we are
	 * completely cleaned up before we leave this function. 
	 */

	WaitForSingleObject(serialPtr->writeThread, INFINITE);
................................................................................

	/*
	 * Signal the main thread by signalling the readable event and
	 * then waking up the notifier thread.
	 */

	SetEvent(infoPtr->readable);

	/*
	 * Alert the foreground thread.  Note that we need to treat this like
	 * a critical section so the foreground thread does not terminate
	 * this thread while we are holding a mutex in the notifier code.
	 */

	Tcl_MutexLock(&serialMutex);
	Tcl_ThreadAlert(infoPtr->threadId);
	Tcl_MutexUnlock(&serialMutex);
    }
    return 0;			/* NOT REACHED */
}
 
/*
 *----------------------------------------------------------------------
 *
................................................................................
	
	/*
	 * Signal the main thread by signalling the writable event and
	 * then waking up the notifier thread.
	 */

	SetEvent(infoPtr->writable);

	/*
	 * Alert the foreground thread.  Note that we need to treat this like
	 * a critical section so the foreground thread does not terminate
	 * this thread while we are holding a mutex in the notifier code.
	 */

	Tcl_MutexLock(&serialMutex);
	Tcl_ThreadAlert(infoPtr->threadId);
	Tcl_MutexUnlock(&serialMutex);
    }
    return 0;			/* NOT REACHED */
}


 
/*
 *----------------------------------------------------------------------
 *
 * TclWinOpenSerialChannel --
 *
 *	Constructs a Serial port channel for the specified standard OS handle.
 *      This is a helper function to break up the construction of 
 *      channels into File, Console, or Serial.
 *
 * Results:
 *	Returns the new channel, or NULL.