os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/win/tclWinConsole.c
author sl
Tue, 10 Jun 2014 14:32:02 +0200 (2014-06-10)
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
sl@0
     1
/* 
sl@0
     2
 * tclWinConsole.c --
sl@0
     3
 *
sl@0
     4
 *	This file implements the Windows-specific console functions,
sl@0
     5
 *	and the "console" channel driver.
sl@0
     6
 *
sl@0
     7
 * Copyright (c) 1999 by Scriptics Corp.
sl@0
     8
 *
sl@0
     9
 * See the file "license.terms" for information on usage and redistribution
sl@0
    10
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
sl@0
    11
 *
sl@0
    12
 * RCS: @(#) $Id: tclWinConsole.c,v 1.11.2.3 2006/03/28 21:02:37 hobbs Exp $
sl@0
    13
 */
sl@0
    14
sl@0
    15
#include "tclWinInt.h"
sl@0
    16
sl@0
    17
#include <fcntl.h>
sl@0
    18
#include <io.h>
sl@0
    19
#include <sys/stat.h>
sl@0
    20
sl@0
    21
/*
sl@0
    22
 * The following variable is used to tell whether this module has been
sl@0
    23
 * initialized.
sl@0
    24
 */
sl@0
    25
sl@0
    26
static int initialized = 0;
sl@0
    27
sl@0
    28
/*
sl@0
    29
 * The consoleMutex locks around access to the initialized variable, and it is
sl@0
    30
 * used to protect background threads from being terminated while they are
sl@0
    31
 * using APIs that hold locks.
sl@0
    32
 */
sl@0
    33
sl@0
    34
TCL_DECLARE_MUTEX(consoleMutex)
sl@0
    35
sl@0
    36
/*
sl@0
    37
 * Bit masks used in the flags field of the ConsoleInfo structure below.
sl@0
    38
 */
sl@0
    39
sl@0
    40
#define CONSOLE_PENDING	(1<<0)	/* Message is pending in the queue. */
sl@0
    41
#define CONSOLE_ASYNC	(1<<1)	/* Channel is non-blocking. */
sl@0
    42
sl@0
    43
/*
sl@0
    44
 * Bit masks used in the sharedFlags field of the ConsoleInfo structure below.
sl@0
    45
 */
sl@0
    46
sl@0
    47
#define CONSOLE_EOF	  (1<<2)  /* Console has reached EOF. */
sl@0
    48
#define CONSOLE_BUFFERED  (1<<3)  /* data was read into a buffer by the reader
sl@0
    49
				     thread */
sl@0
    50
sl@0
    51
#define CONSOLE_BUFFER_SIZE (8*1024)
sl@0
    52
/*
sl@0
    53
 * This structure describes per-instance data for a console based channel.
sl@0
    54
 */
sl@0
    55
sl@0
    56
typedef struct ConsoleInfo {
sl@0
    57
    HANDLE handle;
sl@0
    58
    int type;
sl@0
    59
    struct ConsoleInfo *nextPtr;/* Pointer to next registered console. */
sl@0
    60
    Tcl_Channel channel;	/* Pointer to channel structure. */
sl@0
    61
    int validMask;		/* OR'ed combination of TCL_READABLE,
sl@0
    62
				 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
sl@0
    63
				 * which operations are valid on the file. */
sl@0
    64
    int watchMask;		/* OR'ed combination of TCL_READABLE,
sl@0
    65
				 * TCL_WRITABLE, or TCL_EXCEPTION: indicates
sl@0
    66
				 * which events should be reported. */
sl@0
    67
    int flags;			/* State flags, see above for a list. */
sl@0
    68
    Tcl_ThreadId threadId;	/* Thread to which events should be reported.
sl@0
    69
				 * This value is used by the reader/writer
sl@0
    70
				 * threads. */
sl@0
    71
    HANDLE writeThread;		/* Handle to writer thread. */
sl@0
    72
    HANDLE readThread;		/* Handle to reader thread. */
sl@0
    73
    HANDLE writable;		/* Manual-reset event to signal when the
sl@0
    74
				 * writer thread has finished waiting for
sl@0
    75
				 * the current buffer to be written. */
sl@0
    76
    HANDLE readable;		/* Manual-reset event to signal when the
sl@0
    77
				 * reader thread has finished waiting for
sl@0
    78
				 * input. */
sl@0
    79
    HANDLE startWriter;		/* Auto-reset event used by the main thread to
sl@0
    80
				 * signal when the writer thread should attempt
sl@0
    81
				 * to write to the console. */
sl@0
    82
    HANDLE stopWriter;		/* Auto-reset event used by the main thread to
sl@0
    83
				 * signal when the writer thread should exit.
sl@0
    84
				 */
sl@0
    85
    HANDLE startReader;		/* Auto-reset event used by the main thread to
sl@0
    86
				 * signal when the reader thread should attempt
sl@0
    87
				 * to read from the console. */
sl@0
    88
    HANDLE stopReader;		/* Auto-reset event used by the main thread to
sl@0
    89
				 * signal when the reader thread should exit.
sl@0
    90
				 */
sl@0
    91
    DWORD writeError;		/* An error caused by the last background
sl@0
    92
				 * write.  Set to 0 if no error has been
sl@0
    93
				 * detected.  This word is shared with the
sl@0
    94
				 * writer thread so access must be
sl@0
    95
				 * synchronized with the writable object.
sl@0
    96
				 */
sl@0
    97
    char *writeBuf;		/* Current background output buffer.
sl@0
    98
				 * Access is synchronized with the writable
sl@0
    99
				 * object. */
sl@0
   100
    int writeBufLen;		/* Size of write buffer.  Access is
sl@0
   101
				 * synchronized with the writable
sl@0
   102
				 * object. */
sl@0
   103
    int toWrite;		/* Current amount to be written.  Access is
sl@0
   104
				 * synchronized with the writable object. */
sl@0
   105
    int readFlags;		/* Flags that are shared with the reader
sl@0
   106
				 * thread.  Access is synchronized with the
sl@0
   107
				 * readable object.  */
sl@0
   108
    int bytesRead;              /* number of bytes in the buffer */
sl@0
   109
    int offset;                 /* number of bytes read out of the buffer */
sl@0
   110
    char buffer[CONSOLE_BUFFER_SIZE];
sl@0
   111
                                /* Data consumed by reader thread. */
sl@0
   112
} ConsoleInfo;
sl@0
   113
sl@0
   114
typedef struct ThreadSpecificData {
sl@0
   115
    /*
sl@0
   116
     * The following pointer refers to the head of the list of consoles
sl@0
   117
     * that are being watched for file events.
sl@0
   118
     */
sl@0
   119
    
sl@0
   120
    ConsoleInfo *firstConsolePtr;
sl@0
   121
} ThreadSpecificData;
sl@0
   122
sl@0
   123
static Tcl_ThreadDataKey dataKey;
sl@0
   124
sl@0
   125
/*
sl@0
   126
 * The following structure is what is added to the Tcl event queue when
sl@0
   127
 * console events are generated.
sl@0
   128
 */
sl@0
   129
sl@0
   130
typedef struct ConsoleEvent {
sl@0
   131
    Tcl_Event header;		/* Information that is standard for
sl@0
   132
				 * all events. */
sl@0
   133
    ConsoleInfo *infoPtr;	/* Pointer to console info structure.  Note
sl@0
   134
				 * that we still have to verify that the
sl@0
   135
				 * console exists before dereferencing this
sl@0
   136
				 * pointer. */
sl@0
   137
} ConsoleEvent;
sl@0
   138
sl@0
   139
/*
sl@0
   140
 * Declarations for functions used only in this file.
sl@0
   141
 */
sl@0
   142
sl@0
   143
static int		ConsoleBlockModeProc(ClientData instanceData, int mode);
sl@0
   144
static void		ConsoleCheckProc(ClientData clientData, int flags);
sl@0
   145
static int		ConsoleCloseProc(ClientData instanceData,
sl@0
   146
			    Tcl_Interp *interp);
sl@0
   147
static int		ConsoleEventProc(Tcl_Event *evPtr, int flags);
sl@0
   148
static void		ConsoleExitHandler(ClientData clientData);
sl@0
   149
static int		ConsoleGetHandleProc(ClientData instanceData,
sl@0
   150
			    int direction, ClientData *handlePtr);
sl@0
   151
static void             ConsoleInit(void);
sl@0
   152
static int		ConsoleInputProc(ClientData instanceData, char *buf,
sl@0
   153
			    int toRead, int *errorCode);
sl@0
   154
static int		ConsoleOutputProc(ClientData instanceData,
sl@0
   155
			    CONST char *buf, int toWrite, int *errorCode);
sl@0
   156
static DWORD WINAPI	ConsoleReaderThread(LPVOID arg);
sl@0
   157
static void		ConsoleSetupProc(ClientData clientData, int flags);
sl@0
   158
static void		ConsoleWatchProc(ClientData instanceData, int mask);
sl@0
   159
static DWORD WINAPI	ConsoleWriterThread(LPVOID arg);
sl@0
   160
static void		ProcExitHandler(ClientData clientData);
sl@0
   161
static int		WaitForRead(ConsoleInfo *infoPtr, int blocking);
sl@0
   162
sl@0
   163
static void             ConsoleThreadActionProc _ANSI_ARGS_ ((
sl@0
   164
			   ClientData instanceData, int action));
sl@0
   165
sl@0
   166
/*
sl@0
   167
 * This structure describes the channel type structure for command console
sl@0
   168
 * based IO.
sl@0
   169
 */
sl@0
   170
sl@0
   171
static Tcl_ChannelType consoleChannelType = {
sl@0
   172
    "console",			/* Type name. */
sl@0
   173
    TCL_CHANNEL_VERSION_4,	/* v4 channel */
sl@0
   174
    ConsoleCloseProc,		/* Close proc. */
sl@0
   175
    ConsoleInputProc,		/* Input proc. */
sl@0
   176
    ConsoleOutputProc,		/* Output proc. */
sl@0
   177
    NULL,			/* Seek proc. */
sl@0
   178
    NULL,			/* Set option proc. */
sl@0
   179
    NULL,			/* Get option proc. */
sl@0
   180
    ConsoleWatchProc,		/* Set up notifier to watch the channel. */
sl@0
   181
    ConsoleGetHandleProc,	/* Get an OS handle from channel. */
sl@0
   182
    NULL,			/* close2proc. */
sl@0
   183
    ConsoleBlockModeProc,	/* Set blocking or non-blocking mode.*/
sl@0
   184
    NULL,			/* flush proc. */
sl@0
   185
    NULL,			/* handler proc. */
sl@0
   186
    NULL,                       /* wide seek proc */
sl@0
   187
    ConsoleThreadActionProc,    /* thread action proc */
sl@0
   188
};
sl@0
   189

sl@0
   190
/*
sl@0
   191
 *----------------------------------------------------------------------
sl@0
   192
 *
sl@0
   193
 * ConsoleInit --
sl@0
   194
 *
sl@0
   195
 *	This function initializes the static variables for this file.
sl@0
   196
 *
sl@0
   197
 * Results:
sl@0
   198
 *	None.
sl@0
   199
 *
sl@0
   200
 * Side effects:
sl@0
   201
 *	Creates a new event source.
sl@0
   202
 *
sl@0
   203
 *----------------------------------------------------------------------
sl@0
   204
 */
sl@0
   205
sl@0
   206
static void
sl@0
   207
ConsoleInit()
sl@0
   208
{
sl@0
   209
    ThreadSpecificData *tsdPtr;
sl@0
   210
sl@0
   211
    /*
sl@0
   212
     * Check the initialized flag first, then check again in the mutex.
sl@0
   213
     * This is a speed enhancement.
sl@0
   214
     */
sl@0
   215
sl@0
   216
    if (!initialized) {
sl@0
   217
	Tcl_MutexLock(&consoleMutex);
sl@0
   218
	if (!initialized) {
sl@0
   219
	    initialized = 1;
sl@0
   220
	    Tcl_CreateExitHandler(ProcExitHandler, NULL);
sl@0
   221
	}
sl@0
   222
	Tcl_MutexUnlock(&consoleMutex);
sl@0
   223
    }
sl@0
   224
sl@0
   225
    tsdPtr = (ThreadSpecificData *)TclThreadDataKeyGet(&dataKey);
sl@0
   226
    if (tsdPtr == NULL) {
sl@0
   227
	tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   228
	tsdPtr->firstConsolePtr = NULL;
sl@0
   229
	Tcl_CreateEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
sl@0
   230
	Tcl_CreateThreadExitHandler(ConsoleExitHandler, NULL);
sl@0
   231
    }
sl@0
   232
}
sl@0
   233

sl@0
   234
/*
sl@0
   235
 *----------------------------------------------------------------------
sl@0
   236
 *
sl@0
   237
 * ConsoleExitHandler --
sl@0
   238
 *
sl@0
   239
 *	This function is called to cleanup the console module before
sl@0
   240
 *	Tcl is unloaded.
sl@0
   241
 *
sl@0
   242
 * Results:
sl@0
   243
 *	None.
sl@0
   244
 *
sl@0
   245
 * Side effects:
sl@0
   246
 *	Removes the console event source.
sl@0
   247
 *
sl@0
   248
 *----------------------------------------------------------------------
sl@0
   249
 */
sl@0
   250
sl@0
   251
static void
sl@0
   252
ConsoleExitHandler(
sl@0
   253
    ClientData clientData)	/* Old window proc */
sl@0
   254
{
sl@0
   255
    Tcl_DeleteEventSource(ConsoleSetupProc, ConsoleCheckProc, NULL);
sl@0
   256
}
sl@0
   257

sl@0
   258
/*
sl@0
   259
 *----------------------------------------------------------------------
sl@0
   260
 *
sl@0
   261
 * ProcExitHandler --
sl@0
   262
 *
sl@0
   263
 *	This function is called to cleanup the process list before
sl@0
   264
 *	Tcl is unloaded.
sl@0
   265
 *
sl@0
   266
 * Results:
sl@0
   267
 *	None.
sl@0
   268
 *
sl@0
   269
 * Side effects:
sl@0
   270
 *	Resets the process list.
sl@0
   271
 *
sl@0
   272
 *----------------------------------------------------------------------
sl@0
   273
 */
sl@0
   274
sl@0
   275
static void
sl@0
   276
ProcExitHandler(
sl@0
   277
    ClientData clientData)	/* Old window proc */
sl@0
   278
{
sl@0
   279
    Tcl_MutexLock(&consoleMutex);
sl@0
   280
    initialized = 0;
sl@0
   281
    Tcl_MutexUnlock(&consoleMutex);
sl@0
   282
}
sl@0
   283

sl@0
   284
/*
sl@0
   285
 *----------------------------------------------------------------------
sl@0
   286
 *
sl@0
   287
 * ConsoleSetupProc --
sl@0
   288
 *
sl@0
   289
 *	This procedure is invoked before Tcl_DoOneEvent blocks waiting
sl@0
   290
 *	for an event.
sl@0
   291
 *
sl@0
   292
 * Results:
sl@0
   293
 *	None.
sl@0
   294
 *
sl@0
   295
 * Side effects:
sl@0
   296
 *	Adjusts the block time if needed.
sl@0
   297
 *
sl@0
   298
 *----------------------------------------------------------------------
sl@0
   299
 */
sl@0
   300
sl@0
   301
void
sl@0
   302
ConsoleSetupProc(
sl@0
   303
    ClientData data,		/* Not used. */
sl@0
   304
    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
sl@0
   305
{
sl@0
   306
    ConsoleInfo *infoPtr;
sl@0
   307
    Tcl_Time blockTime = { 0, 0 };
sl@0
   308
    int block = 1;
sl@0
   309
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   310
sl@0
   311
    if (!(flags & TCL_FILE_EVENTS)) {
sl@0
   312
	return;
sl@0
   313
    }
sl@0
   314
    
sl@0
   315
    /*
sl@0
   316
     * Look to see if any events are already pending.  If they are, poll.
sl@0
   317
     */
sl@0
   318
sl@0
   319
    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL; 
sl@0
   320
	    infoPtr = infoPtr->nextPtr) {
sl@0
   321
	if (infoPtr->watchMask & TCL_WRITABLE) {
sl@0
   322
	    if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
sl@0
   323
		block = 0;
sl@0
   324
	    }
sl@0
   325
	}
sl@0
   326
	if (infoPtr->watchMask & TCL_READABLE) {
sl@0
   327
	    if (WaitForRead(infoPtr, 0) >= 0) {
sl@0
   328
		block = 0;
sl@0
   329
	    }
sl@0
   330
	}
sl@0
   331
    }
sl@0
   332
    if (!block) {
sl@0
   333
	Tcl_SetMaxBlockTime(&blockTime);
sl@0
   334
    }
sl@0
   335
}
sl@0
   336

sl@0
   337
/*
sl@0
   338
 *----------------------------------------------------------------------
sl@0
   339
 *
sl@0
   340
 * ConsoleCheckProc --
sl@0
   341
 *
sl@0
   342
 *	This procedure is called by Tcl_DoOneEvent to check the console
sl@0
   343
 *	event source for events. 
sl@0
   344
 *
sl@0
   345
 * Results:
sl@0
   346
 *	None.
sl@0
   347
 *
sl@0
   348
 * Side effects:
sl@0
   349
 *	May queue an event.
sl@0
   350
 *
sl@0
   351
 *----------------------------------------------------------------------
sl@0
   352
 */
sl@0
   353
sl@0
   354
static void
sl@0
   355
ConsoleCheckProc(
sl@0
   356
    ClientData data,		/* Not used. */
sl@0
   357
    int flags)			/* Event flags as passed to Tcl_DoOneEvent. */
sl@0
   358
{
sl@0
   359
    ConsoleInfo *infoPtr;
sl@0
   360
    ConsoleEvent *evPtr;
sl@0
   361
    int needEvent;
sl@0
   362
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   363
sl@0
   364
    if (!(flags & TCL_FILE_EVENTS)) {
sl@0
   365
	return;
sl@0
   366
    }
sl@0
   367
    
sl@0
   368
    /*
sl@0
   369
     * Queue events for any ready consoles that don't already have events
sl@0
   370
     * queued.
sl@0
   371
     */
sl@0
   372
sl@0
   373
    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL; 
sl@0
   374
	    infoPtr = infoPtr->nextPtr) {
sl@0
   375
	if (infoPtr->flags & CONSOLE_PENDING) {
sl@0
   376
	    continue;
sl@0
   377
	}
sl@0
   378
	
sl@0
   379
	/*
sl@0
   380
	 * Queue an event if the console is signaled for reading or writing.
sl@0
   381
	 */
sl@0
   382
sl@0
   383
	needEvent = 0;
sl@0
   384
	if (infoPtr->watchMask & TCL_WRITABLE) {
sl@0
   385
	    if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
sl@0
   386
		needEvent = 1;
sl@0
   387
	    }
sl@0
   388
	}
sl@0
   389
	
sl@0
   390
	if (infoPtr->watchMask & TCL_READABLE) {
sl@0
   391
	    if (WaitForRead(infoPtr, 0) >= 0) {
sl@0
   392
		needEvent = 1;
sl@0
   393
	    }
sl@0
   394
	}
sl@0
   395
sl@0
   396
	if (needEvent) {
sl@0
   397
	    infoPtr->flags |= CONSOLE_PENDING;
sl@0
   398
	    evPtr = (ConsoleEvent *) ckalloc(sizeof(ConsoleEvent));
sl@0
   399
	    evPtr->header.proc = ConsoleEventProc;
sl@0
   400
	    evPtr->infoPtr = infoPtr;
sl@0
   401
	    Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
sl@0
   402
	}
sl@0
   403
    }
sl@0
   404
}
sl@0
   405

sl@0
   406

sl@0
   407
/*
sl@0
   408
 *----------------------------------------------------------------------
sl@0
   409
 *
sl@0
   410
 * ConsoleBlockModeProc --
sl@0
   411
 *
sl@0
   412
 *	Set blocking or non-blocking mode on channel.
sl@0
   413
 *
sl@0
   414
 * Results:
sl@0
   415
 *	0 if successful, errno when failed.
sl@0
   416
 *
sl@0
   417
 * Side effects:
sl@0
   418
 *	Sets the device into blocking or non-blocking mode.
sl@0
   419
 *
sl@0
   420
 *----------------------------------------------------------------------
sl@0
   421
 */
sl@0
   422
sl@0
   423
static int
sl@0
   424
ConsoleBlockModeProc(
sl@0
   425
    ClientData instanceData,	/* Instance data for channel. */
sl@0
   426
    int mode)			/* TCL_MODE_BLOCKING or
sl@0
   427
                                 * TCL_MODE_NONBLOCKING. */
sl@0
   428
{
sl@0
   429
    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
sl@0
   430
    
sl@0
   431
    /*
sl@0
   432
     * Consoles on Windows can not be switched between blocking and nonblocking,
sl@0
   433
     * hence we have to emulate the behavior. This is done in the input
sl@0
   434
     * function by checking against a bit in the state. We set or unset the
sl@0
   435
     * bit here to cause the input function to emulate the correct behavior.
sl@0
   436
     */
sl@0
   437
sl@0
   438
    if (mode == TCL_MODE_NONBLOCKING) {
sl@0
   439
	infoPtr->flags |= CONSOLE_ASYNC;
sl@0
   440
    } else {
sl@0
   441
	infoPtr->flags &= ~(CONSOLE_ASYNC);
sl@0
   442
    }
sl@0
   443
    return 0;
sl@0
   444
}
sl@0
   445

sl@0
   446
/*
sl@0
   447
 *----------------------------------------------------------------------
sl@0
   448
 *
sl@0
   449
 * ConsoleCloseProc --
sl@0
   450
 *
sl@0
   451
 *	Closes a console based IO channel.
sl@0
   452
 *
sl@0
   453
 * Results:
sl@0
   454
 *	0 on success, errno otherwise.
sl@0
   455
 *
sl@0
   456
 * Side effects:
sl@0
   457
 *	Closes the physical channel.
sl@0
   458
 *
sl@0
   459
 *----------------------------------------------------------------------
sl@0
   460
 */
sl@0
   461
sl@0
   462
static int
sl@0
   463
ConsoleCloseProc(
sl@0
   464
    ClientData instanceData,	/* Pointer to ConsoleInfo structure. */
sl@0
   465
    Tcl_Interp *interp)		/* For error reporting. */
sl@0
   466
{
sl@0
   467
    ConsoleInfo *consolePtr = (ConsoleInfo *) instanceData;
sl@0
   468
    int errorCode;
sl@0
   469
    ConsoleInfo *infoPtr, **nextPtrPtr;
sl@0
   470
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   471
    DWORD exitCode;
sl@0
   472
sl@0
   473
    errorCode = 0;
sl@0
   474
    
sl@0
   475
    /*
sl@0
   476
     * Clean up the background thread if necessary.  Note that this
sl@0
   477
     * must be done before we can close the file, since the 
sl@0
   478
     * thread may be blocking trying to read from the console.
sl@0
   479
     */
sl@0
   480
    
sl@0
   481
    if (consolePtr->readThread) {
sl@0
   482
sl@0
   483
	/*
sl@0
   484
	 * The thread may already have closed on it's own.  Check it's
sl@0
   485
	 * exit code.
sl@0
   486
	 */
sl@0
   487
sl@0
   488
	GetExitCodeThread(consolePtr->readThread, &exitCode);
sl@0
   489
sl@0
   490
	if (exitCode == STILL_ACTIVE) {
sl@0
   491
sl@0
   492
	    /*
sl@0
   493
	     * Set the stop event so that if the reader thread is blocked
sl@0
   494
	     * in ConsoleReaderThread on WaitForMultipleEvents, it will exit
sl@0
   495
	     * cleanly.
sl@0
   496
	     */
sl@0
   497
sl@0
   498
	    SetEvent(consolePtr->stopReader);
sl@0
   499
sl@0
   500
	    /*
sl@0
   501
	     * Wait at most 20 milliseconds for the reader thread to close.
sl@0
   502
	     */
sl@0
   503
sl@0
   504
	    if (WaitForSingleObject(consolePtr->readThread, 20)
sl@0
   505
		    == WAIT_TIMEOUT) {
sl@0
   506
		/*
sl@0
   507
		 * Forcibly terminate the background thread as a last
sl@0
   508
		 * resort.  Note that we need to guard against
sl@0
   509
		 * terminating the thread while it is in the middle of
sl@0
   510
		 * Tcl_ThreadAlert because it won't be able to release
sl@0
   511
		 * the notifier lock.
sl@0
   512
		 */
sl@0
   513
sl@0
   514
		Tcl_MutexLock(&consoleMutex);
sl@0
   515
sl@0
   516
		/* BUG: this leaks memory. */
sl@0
   517
		TerminateThread(consolePtr->readThread, 0);
sl@0
   518
		Tcl_MutexUnlock(&consoleMutex);
sl@0
   519
	    }
sl@0
   520
	}
sl@0
   521
sl@0
   522
	CloseHandle(consolePtr->readThread);
sl@0
   523
	CloseHandle(consolePtr->readable);
sl@0
   524
	CloseHandle(consolePtr->startReader);
sl@0
   525
	CloseHandle(consolePtr->stopReader);
sl@0
   526
	consolePtr->readThread = NULL;
sl@0
   527
    }
sl@0
   528
    consolePtr->validMask &= ~TCL_READABLE;
sl@0
   529
sl@0
   530
    /*
sl@0
   531
     * Wait for the writer thread to finish the current buffer, then
sl@0
   532
     * terminate the thread and close the handles.  If the channel is
sl@0
   533
     * nonblocking, there should be no pending write operations.
sl@0
   534
     */
sl@0
   535
    
sl@0
   536
    if (consolePtr->writeThread) {
sl@0
   537
	if (consolePtr->toWrite) {
sl@0
   538
	    /*
sl@0
   539
	     * We only need to wait if there is something to write.
sl@0
   540
	     * This may prevent infinite wait on exit. [python bug 216289]
sl@0
   541
	     */
sl@0
   542
	    WaitForSingleObject(consolePtr->writable, INFINITE);
sl@0
   543
	}
sl@0
   544
sl@0
   545
	/*
sl@0
   546
	 * The thread may already have closed on it's own.  Check it's
sl@0
   547
	 * exit code.
sl@0
   548
	 */
sl@0
   549
sl@0
   550
	GetExitCodeThread(consolePtr->writeThread, &exitCode);
sl@0
   551
sl@0
   552
	if (exitCode == STILL_ACTIVE) {
sl@0
   553
	    /*
sl@0
   554
	     * Set the stop event so that if the reader thread is blocked
sl@0
   555
	     * in ConsoleWriterThread on WaitForMultipleEvents, it will
sl@0
   556
	     * exit cleanly.
sl@0
   557
	     */
sl@0
   558
sl@0
   559
	    SetEvent(consolePtr->stopWriter);
sl@0
   560
sl@0
   561
	    /*
sl@0
   562
	     * Wait at most 20 milliseconds for the writer thread to close.
sl@0
   563
	     */
sl@0
   564
sl@0
   565
	    if (WaitForSingleObject(consolePtr->writeThread, 20)
sl@0
   566
		    == WAIT_TIMEOUT) {
sl@0
   567
		/*
sl@0
   568
		 * Forcibly terminate the background thread as a last
sl@0
   569
		 * resort.  Note that we need to guard against
sl@0
   570
		 * terminating the thread while it is in the middle of
sl@0
   571
		 * Tcl_ThreadAlert because it won't be able to release
sl@0
   572
		 * the notifier lock.
sl@0
   573
		 */
sl@0
   574
sl@0
   575
		Tcl_MutexLock(&consoleMutex);
sl@0
   576
sl@0
   577
		/* BUG: this leaks memory. */
sl@0
   578
		TerminateThread(consolePtr->writeThread, 0);
sl@0
   579
		Tcl_MutexUnlock(&consoleMutex);
sl@0
   580
	    }
sl@0
   581
	}
sl@0
   582
sl@0
   583
	CloseHandle(consolePtr->writeThread);
sl@0
   584
	CloseHandle(consolePtr->writable);
sl@0
   585
	CloseHandle(consolePtr->startWriter);
sl@0
   586
	CloseHandle(consolePtr->stopWriter);
sl@0
   587
	consolePtr->writeThread = NULL;
sl@0
   588
    }
sl@0
   589
    consolePtr->validMask &= ~TCL_WRITABLE;
sl@0
   590
sl@0
   591
sl@0
   592
    /*
sl@0
   593
     * Don't close the Win32 handle if the handle is a standard channel
sl@0
   594
     * during the thread exit process.  Otherwise, one thread may kill
sl@0
   595
     * the stdio of another.
sl@0
   596
     */
sl@0
   597
sl@0
   598
    if (!TclInThreadExit() 
sl@0
   599
	    || ((GetStdHandle(STD_INPUT_HANDLE) != consolePtr->handle)
sl@0
   600
		&& (GetStdHandle(STD_OUTPUT_HANDLE) != consolePtr->handle)
sl@0
   601
		&& (GetStdHandle(STD_ERROR_HANDLE) != consolePtr->handle))) {
sl@0
   602
	if (CloseHandle(consolePtr->handle) == FALSE) {
sl@0
   603
	    TclWinConvertError(GetLastError());
sl@0
   604
	    errorCode = errno;
sl@0
   605
	}
sl@0
   606
    }
sl@0
   607
    
sl@0
   608
    consolePtr->watchMask &= consolePtr->validMask;
sl@0
   609
sl@0
   610
    /*
sl@0
   611
     * Remove the file from the list of watched files.
sl@0
   612
     */
sl@0
   613
sl@0
   614
    for (nextPtrPtr = &(tsdPtr->firstConsolePtr), infoPtr = *nextPtrPtr;
sl@0
   615
	    infoPtr != NULL;
sl@0
   616
	    nextPtrPtr = &infoPtr->nextPtr, infoPtr = *nextPtrPtr) {
sl@0
   617
	if (infoPtr == (ConsoleInfo *)consolePtr) {
sl@0
   618
	    *nextPtrPtr = infoPtr->nextPtr;
sl@0
   619
	    break;
sl@0
   620
	}
sl@0
   621
    }
sl@0
   622
    if (consolePtr->writeBuf != NULL) {
sl@0
   623
	ckfree(consolePtr->writeBuf);
sl@0
   624
	consolePtr->writeBuf = 0;
sl@0
   625
    }
sl@0
   626
    ckfree((char*) consolePtr);
sl@0
   627
sl@0
   628
    return errorCode;
sl@0
   629
}
sl@0
   630

sl@0
   631
/*
sl@0
   632
 *----------------------------------------------------------------------
sl@0
   633
 *
sl@0
   634
 * ConsoleInputProc --
sl@0
   635
 *
sl@0
   636
 *	Reads input from the IO channel into the buffer given. Returns
sl@0
   637
 *	count of how many bytes were actually read, and an error indication.
sl@0
   638
 *
sl@0
   639
 * Results:
sl@0
   640
 *	A count of how many bytes were read is returned and an error
sl@0
   641
 *	indication is returned in an output argument.
sl@0
   642
 *
sl@0
   643
 * Side effects:
sl@0
   644
 *	Reads input from the actual channel.
sl@0
   645
 *
sl@0
   646
 *----------------------------------------------------------------------
sl@0
   647
 */
sl@0
   648
sl@0
   649
static int
sl@0
   650
ConsoleInputProc(
sl@0
   651
    ClientData instanceData,		/* Console state. */
sl@0
   652
    char *buf,				/* Where to store data read. */
sl@0
   653
    int bufSize,			/* How much space is available
sl@0
   654
                                         * in the buffer? */
sl@0
   655
    int *errorCode)			/* Where to store error code. */
sl@0
   656
{
sl@0
   657
    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
sl@0
   658
    DWORD count, bytesRead = 0;
sl@0
   659
    int result;
sl@0
   660
sl@0
   661
    *errorCode = 0;
sl@0
   662
sl@0
   663
    /*
sl@0
   664
     * Synchronize with the reader thread.
sl@0
   665
     */
sl@0
   666
    
sl@0
   667
    result = WaitForRead(infoPtr, (infoPtr->flags & CONSOLE_ASYNC) ? 0 : 1);
sl@0
   668
    
sl@0
   669
    /*
sl@0
   670
     * If an error occurred, return immediately.
sl@0
   671
     */
sl@0
   672
    
sl@0
   673
    if (result == -1) {
sl@0
   674
	*errorCode = errno;
sl@0
   675
	return -1;
sl@0
   676
    }
sl@0
   677
sl@0
   678
    if (infoPtr->readFlags & CONSOLE_BUFFERED) {
sl@0
   679
	/*
sl@0
   680
	 * Data is stored in the buffer.
sl@0
   681
	 */
sl@0
   682
sl@0
   683
	if (bufSize < (infoPtr->bytesRead - infoPtr->offset)) {
sl@0
   684
	    memcpy(buf, &infoPtr->buffer[infoPtr->offset], (size_t) bufSize);
sl@0
   685
	    bytesRead = bufSize;
sl@0
   686
	    infoPtr->offset += bufSize;
sl@0
   687
	} else {
sl@0
   688
	    memcpy(buf, &infoPtr->buffer[infoPtr->offset], (size_t) bufSize);
sl@0
   689
	    bytesRead = infoPtr->bytesRead - infoPtr->offset;
sl@0
   690
sl@0
   691
	    /*
sl@0
   692
	     * Reset the buffer
sl@0
   693
	     */
sl@0
   694
	    
sl@0
   695
	    infoPtr->readFlags &= ~CONSOLE_BUFFERED;
sl@0
   696
	    infoPtr->offset = 0;
sl@0
   697
	}
sl@0
   698
sl@0
   699
	return bytesRead;
sl@0
   700
    }
sl@0
   701
    
sl@0
   702
    /*
sl@0
   703
     * Attempt to read bufSize bytes.  The read will return immediately
sl@0
   704
     * if there is any data available.  Otherwise it will block until
sl@0
   705
     * at least one byte is available or an EOF occurs.
sl@0
   706
     */
sl@0
   707
sl@0
   708
    if (ReadConsole(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &count,
sl@0
   709
		    (LPOVERLAPPED) NULL) == TRUE) {
sl@0
   710
	buf[count] = '\0';
sl@0
   711
	return count;
sl@0
   712
    }
sl@0
   713
sl@0
   714
    return -1;
sl@0
   715
}
sl@0
   716

sl@0
   717
/*
sl@0
   718
 *----------------------------------------------------------------------
sl@0
   719
 *
sl@0
   720
 * ConsoleOutputProc --
sl@0
   721
 *
sl@0
   722
 *	Writes the given output on the IO channel. Returns count of how
sl@0
   723
 *	many characters were actually written, and an error indication.
sl@0
   724
 *
sl@0
   725
 * Results:
sl@0
   726
 *	A count of how many characters were written is returned and an
sl@0
   727
 *	error indication is returned in an output argument.
sl@0
   728
 *
sl@0
   729
 * Side effects:
sl@0
   730
 *	Writes output on the actual channel.
sl@0
   731
 *
sl@0
   732
 *----------------------------------------------------------------------
sl@0
   733
 */
sl@0
   734
sl@0
   735
static int
sl@0
   736
ConsoleOutputProc(
sl@0
   737
    ClientData instanceData,		/* Console state. */
sl@0
   738
    CONST char *buf,			/* The data buffer. */
sl@0
   739
    int toWrite,			/* How many bytes to write? */
sl@0
   740
    int *errorCode)			/* Where to store error code. */
sl@0
   741
{
sl@0
   742
    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
sl@0
   743
    DWORD bytesWritten, timeout;
sl@0
   744
    
sl@0
   745
    *errorCode = 0;
sl@0
   746
    timeout = (infoPtr->flags & CONSOLE_ASYNC) ? 0 : INFINITE;
sl@0
   747
    if (WaitForSingleObject(infoPtr->writable, timeout) == WAIT_TIMEOUT) {
sl@0
   748
	/*
sl@0
   749
	 * The writer thread is blocked waiting for a write to complete
sl@0
   750
	 * and the channel is in non-blocking mode.
sl@0
   751
	 */
sl@0
   752
sl@0
   753
	errno = EAGAIN;
sl@0
   754
	goto error;
sl@0
   755
    }
sl@0
   756
    
sl@0
   757
    /*
sl@0
   758
     * Check for a background error on the last write.
sl@0
   759
     */
sl@0
   760
sl@0
   761
    if (infoPtr->writeError) {
sl@0
   762
	TclWinConvertError(infoPtr->writeError);
sl@0
   763
	infoPtr->writeError = 0;
sl@0
   764
	goto error;
sl@0
   765
    }
sl@0
   766
sl@0
   767
    if (infoPtr->flags & CONSOLE_ASYNC) {
sl@0
   768
	/*
sl@0
   769
	 * The console is non-blocking, so copy the data into the output
sl@0
   770
	 * buffer and restart the writer thread.
sl@0
   771
	 */
sl@0
   772
sl@0
   773
	if (toWrite > infoPtr->writeBufLen) {
sl@0
   774
	    /*
sl@0
   775
	     * Reallocate the buffer to be large enough to hold the data.
sl@0
   776
	     */
sl@0
   777
sl@0
   778
	    if (infoPtr->writeBuf) {
sl@0
   779
		ckfree(infoPtr->writeBuf);
sl@0
   780
	    }
sl@0
   781
	    infoPtr->writeBufLen = toWrite;
sl@0
   782
	    infoPtr->writeBuf = ckalloc((unsigned int) toWrite);
sl@0
   783
	}
sl@0
   784
	memcpy(infoPtr->writeBuf, buf, (size_t) toWrite);
sl@0
   785
	infoPtr->toWrite = toWrite;
sl@0
   786
	ResetEvent(infoPtr->writable);
sl@0
   787
	SetEvent(infoPtr->startWriter);
sl@0
   788
	bytesWritten = toWrite;
sl@0
   789
    } else {
sl@0
   790
	/*
sl@0
   791
	 * In the blocking case, just try to write the buffer directly.
sl@0
   792
	 * This avoids an unnecessary copy.
sl@0
   793
	 */
sl@0
   794
sl@0
   795
	if (WriteFile(infoPtr->handle, (LPVOID) buf, (DWORD) toWrite,
sl@0
   796
		&bytesWritten, (LPOVERLAPPED) NULL) == FALSE) {
sl@0
   797
	    TclWinConvertError(GetLastError());
sl@0
   798
	    goto error;
sl@0
   799
	}
sl@0
   800
    }
sl@0
   801
    return bytesWritten;
sl@0
   802
sl@0
   803
    error:
sl@0
   804
    *errorCode = errno;
sl@0
   805
    return -1;
sl@0
   806
sl@0
   807
}
sl@0
   808

sl@0
   809
/*
sl@0
   810
 *----------------------------------------------------------------------
sl@0
   811
 *
sl@0
   812
 * ConsoleEventProc --
sl@0
   813
 *
sl@0
   814
 *	This function is invoked by Tcl_ServiceEvent when a file event
sl@0
   815
 *	reaches the front of the event queue.  This procedure invokes
sl@0
   816
 *	Tcl_NotifyChannel on the console.
sl@0
   817
 *
sl@0
   818
 * Results:
sl@0
   819
 *	Returns 1 if the event was handled, meaning it should be removed
sl@0
   820
 *	from the queue.  Returns 0 if the event was not handled, meaning
sl@0
   821
 *	it should stay on the queue.  The only time the event isn't
sl@0
   822
 *	handled is if the TCL_FILE_EVENTS flag bit isn't set.
sl@0
   823
 *
sl@0
   824
 * Side effects:
sl@0
   825
 *	Whatever the notifier callback does.
sl@0
   826
 *
sl@0
   827
 *----------------------------------------------------------------------
sl@0
   828
 */
sl@0
   829
sl@0
   830
static int
sl@0
   831
ConsoleEventProc(
sl@0
   832
    Tcl_Event *evPtr,		/* Event to service. */
sl@0
   833
    int flags)			/* Flags that indicate what events to
sl@0
   834
				 * handle, such as TCL_FILE_EVENTS. */
sl@0
   835
{
sl@0
   836
    ConsoleEvent *consoleEvPtr = (ConsoleEvent *)evPtr;
sl@0
   837
    ConsoleInfo *infoPtr;
sl@0
   838
    int mask;
sl@0
   839
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   840
sl@0
   841
    if (!(flags & TCL_FILE_EVENTS)) {
sl@0
   842
	return 0;
sl@0
   843
    }
sl@0
   844
sl@0
   845
    /*
sl@0
   846
     * Search through the list of watched consoles for the one whose handle
sl@0
   847
     * matches the event.  We do this rather than simply dereferencing
sl@0
   848
     * the handle in the event so that consoles can be deleted while the
sl@0
   849
     * event is in the queue.
sl@0
   850
     */
sl@0
   851
sl@0
   852
    for (infoPtr = tsdPtr->firstConsolePtr; infoPtr != NULL;
sl@0
   853
	    infoPtr = infoPtr->nextPtr) {
sl@0
   854
	if (consoleEvPtr->infoPtr == infoPtr) {
sl@0
   855
	    infoPtr->flags &= ~(CONSOLE_PENDING);
sl@0
   856
	    break;
sl@0
   857
	}
sl@0
   858
    }
sl@0
   859
sl@0
   860
    /*
sl@0
   861
     * Remove stale events.
sl@0
   862
     */
sl@0
   863
sl@0
   864
    if (!infoPtr) {
sl@0
   865
	return 1;
sl@0
   866
    }
sl@0
   867
sl@0
   868
    /*
sl@0
   869
     * Check to see if the console is readable.  Note
sl@0
   870
     * that we can't tell if a console is writable, so we always report it
sl@0
   871
     * as being writable unless we have detected EOF.
sl@0
   872
     */
sl@0
   873
sl@0
   874
    mask = 0;
sl@0
   875
    if (infoPtr->watchMask & TCL_WRITABLE) {
sl@0
   876
	if (WaitForSingleObject(infoPtr->writable, 0) != WAIT_TIMEOUT) {
sl@0
   877
	    mask = TCL_WRITABLE;
sl@0
   878
	}
sl@0
   879
    }
sl@0
   880
sl@0
   881
    if (infoPtr->watchMask & TCL_READABLE) {
sl@0
   882
	if (WaitForRead(infoPtr, 0) >= 0) {
sl@0
   883
	    if (infoPtr->readFlags & CONSOLE_EOF) {
sl@0
   884
		mask = TCL_READABLE;
sl@0
   885
	    } else {
sl@0
   886
		mask |= TCL_READABLE;
sl@0
   887
	    }
sl@0
   888
	} 
sl@0
   889
    }
sl@0
   890
sl@0
   891
    /*
sl@0
   892
     * Inform the channel of the events.
sl@0
   893
     */
sl@0
   894
sl@0
   895
    Tcl_NotifyChannel(infoPtr->channel, infoPtr->watchMask & mask);
sl@0
   896
    return 1;
sl@0
   897
}
sl@0
   898

sl@0
   899
/*
sl@0
   900
 *----------------------------------------------------------------------
sl@0
   901
 *
sl@0
   902
 * ConsoleWatchProc --
sl@0
   903
 *
sl@0
   904
 *	Called by the notifier to set up to watch for events on this
sl@0
   905
 *	channel.
sl@0
   906
 *
sl@0
   907
 * Results:
sl@0
   908
 *	None.
sl@0
   909
 *
sl@0
   910
 * Side effects:
sl@0
   911
 *	None.
sl@0
   912
 *
sl@0
   913
 *----------------------------------------------------------------------
sl@0
   914
 */
sl@0
   915
sl@0
   916
static void
sl@0
   917
ConsoleWatchProc(
sl@0
   918
    ClientData instanceData,		/* Console state. */
sl@0
   919
    int mask)				/* What events to watch for, OR-ed
sl@0
   920
                                         * combination of TCL_READABLE,
sl@0
   921
                                         * TCL_WRITABLE and TCL_EXCEPTION. */
sl@0
   922
{
sl@0
   923
    ConsoleInfo **nextPtrPtr, *ptr;
sl@0
   924
    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
sl@0
   925
    int oldMask = infoPtr->watchMask;
sl@0
   926
    ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
sl@0
   927
sl@0
   928
    /*
sl@0
   929
     * Since most of the work is handled by the background threads,
sl@0
   930
     * we just need to update the watchMask and then force the notifier
sl@0
   931
     * to poll once. 
sl@0
   932
     */
sl@0
   933
sl@0
   934
    infoPtr->watchMask = mask & infoPtr->validMask;
sl@0
   935
    if (infoPtr->watchMask) {
sl@0
   936
	Tcl_Time blockTime = { 0, 0 };
sl@0
   937
	if (!oldMask) {
sl@0
   938
	    infoPtr->nextPtr = tsdPtr->firstConsolePtr;
sl@0
   939
	    tsdPtr->firstConsolePtr = infoPtr;
sl@0
   940
	}
sl@0
   941
	Tcl_SetMaxBlockTime(&blockTime);
sl@0
   942
    } else {
sl@0
   943
	if (oldMask) {
sl@0
   944
	    /*
sl@0
   945
	     * Remove the console from the list of watched consoles.
sl@0
   946
	     */
sl@0
   947
sl@0
   948
	    for (nextPtrPtr = &(tsdPtr->firstConsolePtr), ptr = *nextPtrPtr;
sl@0
   949
		 ptr != NULL;
sl@0
   950
		 nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
sl@0
   951
		if (infoPtr == ptr) {
sl@0
   952
		    *nextPtrPtr = ptr->nextPtr;
sl@0
   953
		    break;
sl@0
   954
		}
sl@0
   955
	    }
sl@0
   956
	}
sl@0
   957
    }
sl@0
   958
}
sl@0
   959

sl@0
   960
/*
sl@0
   961
 *----------------------------------------------------------------------
sl@0
   962
 *
sl@0
   963
 * ConsoleGetHandleProc --
sl@0
   964
 *
sl@0
   965
 *	Called from Tcl_GetChannelHandle to retrieve OS handles from
sl@0
   966
 *	inside a command consoleline based channel.
sl@0
   967
 *
sl@0
   968
 * Results:
sl@0
   969
 *	Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
sl@0
   970
 *	there is no handle for the specified direction. 
sl@0
   971
 *
sl@0
   972
 * Side effects:
sl@0
   973
 *	None.
sl@0
   974
 *
sl@0
   975
 *----------------------------------------------------------------------
sl@0
   976
 */
sl@0
   977
sl@0
   978
static int
sl@0
   979
ConsoleGetHandleProc(
sl@0
   980
    ClientData instanceData,	/* The console state. */
sl@0
   981
    int direction,		/* TCL_READABLE or TCL_WRITABLE */
sl@0
   982
    ClientData *handlePtr)	/* Where to store the handle.  */
sl@0
   983
{
sl@0
   984
    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
sl@0
   985
sl@0
   986
    *handlePtr = (ClientData) infoPtr->handle;
sl@0
   987
    return TCL_OK;
sl@0
   988
}
sl@0
   989

sl@0
   990
/*
sl@0
   991
 *----------------------------------------------------------------------
sl@0
   992
 *
sl@0
   993
 * WaitForRead --
sl@0
   994
 *
sl@0
   995
 *	Wait until some data is available, the console is at
sl@0
   996
 *	EOF or the reader thread is blocked waiting for data (if the
sl@0
   997
 *	channel is in non-blocking mode).
sl@0
   998
 *
sl@0
   999
 * Results:
sl@0
  1000
 *	Returns 1 if console is readable.  Returns 0 if there is no data
sl@0
  1001
 *	on the console, but there is buffered data.  Returns -1 if an
sl@0
  1002
 *	error occurred.  If an error occurred, the threads may not
sl@0
  1003
 *	be synchronized.
sl@0
  1004
 *
sl@0
  1005
 * Side effects:
sl@0
  1006
 *	Updates the shared state flags.  If no error occurred,
sl@0
  1007
 *      the reader thread is blocked waiting for a signal from the 
sl@0
  1008
 *      main thread.
sl@0
  1009
 *
sl@0
  1010
 *----------------------------------------------------------------------
sl@0
  1011
 */
sl@0
  1012
sl@0
  1013
static int
sl@0
  1014
WaitForRead(
sl@0
  1015
    ConsoleInfo *infoPtr,		/* Console state. */
sl@0
  1016
    int blocking)		/* Indicates whether call should be
sl@0
  1017
				 * blocking or not. */
sl@0
  1018
{
sl@0
  1019
    DWORD timeout, count;
sl@0
  1020
    HANDLE *handle = infoPtr->handle;
sl@0
  1021
    INPUT_RECORD input;
sl@0
  1022
    
sl@0
  1023
    while (1) {
sl@0
  1024
	/*
sl@0
  1025
	 * Synchronize with the reader thread.
sl@0
  1026
	 */
sl@0
  1027
       
sl@0
  1028
	timeout = blocking ? INFINITE : 0;
sl@0
  1029
	if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) {
sl@0
  1030
	    /*
sl@0
  1031
	     * The reader thread is blocked waiting for data and the channel
sl@0
  1032
	     * is in non-blocking mode.
sl@0
  1033
	     */
sl@0
  1034
	    errno = EAGAIN;
sl@0
  1035
	    return -1;
sl@0
  1036
	}
sl@0
  1037
	
sl@0
  1038
	/*
sl@0
  1039
	 * At this point, the two threads are synchronized, so it is safe
sl@0
  1040
	 * to access shared state.
sl@0
  1041
	 */
sl@0
  1042
	
sl@0
  1043
	/*
sl@0
  1044
	 * If the console has hit EOF, it is always readable.
sl@0
  1045
	 */
sl@0
  1046
	
sl@0
  1047
	if (infoPtr->readFlags & CONSOLE_EOF) {
sl@0
  1048
	    return 1;
sl@0
  1049
	}
sl@0
  1050
	
sl@0
  1051
	if (PeekConsoleInput(handle, &input, 1, &count) == FALSE) {
sl@0
  1052
            /*
sl@0
  1053
	     * Check to see if the peek failed because of EOF.
sl@0
  1054
	     */
sl@0
  1055
	    
sl@0
  1056
	    TclWinConvertError(GetLastError());
sl@0
  1057
	    
sl@0
  1058
	    if (errno == EOF) {
sl@0
  1059
		infoPtr->readFlags |= CONSOLE_EOF;
sl@0
  1060
		return 1;
sl@0
  1061
	    }
sl@0
  1062
sl@0
  1063
	    /*
sl@0
  1064
	     * Ignore errors if there is data in the buffer.
sl@0
  1065
	     */
sl@0
  1066
	    
sl@0
  1067
	    if (infoPtr->readFlags & CONSOLE_BUFFERED) {
sl@0
  1068
		return 0;
sl@0
  1069
	    } else {
sl@0
  1070
		return -1;
sl@0
  1071
	    }
sl@0
  1072
	}
sl@0
  1073
sl@0
  1074
	/*
sl@0
  1075
	 * If there is data in the buffer, the console must be
sl@0
  1076
	 * readable (since it is a line-oriented device).
sl@0
  1077
	 */
sl@0
  1078
sl@0
  1079
	if (infoPtr->readFlags & CONSOLE_BUFFERED) {
sl@0
  1080
	    return 1;
sl@0
  1081
	}
sl@0
  1082
sl@0
  1083
	
sl@0
  1084
	/*
sl@0
  1085
	 * There wasn't any data available, so reset the thread and
sl@0
  1086
	 * try again.
sl@0
  1087
	 */
sl@0
  1088
    
sl@0
  1089
	ResetEvent(infoPtr->readable);
sl@0
  1090
	SetEvent(infoPtr->startReader);
sl@0
  1091
    }
sl@0
  1092
}
sl@0
  1093

sl@0
  1094
/*
sl@0
  1095
 *----------------------------------------------------------------------
sl@0
  1096
 *
sl@0
  1097
 * ConsoleReaderThread --
sl@0
  1098
 *
sl@0
  1099
 *	This function runs in a separate thread and waits for input
sl@0
  1100
 *	to become available on a console.
sl@0
  1101
 *
sl@0
  1102
 * Results:
sl@0
  1103
 *	None.
sl@0
  1104
 *
sl@0
  1105
 * Side effects:
sl@0
  1106
 *	Signals the main thread when input become available.  May
sl@0
  1107
 *	cause the main thread to wake up by posting a message.  May
sl@0
  1108
 *	one line from the console for each wait operation.
sl@0
  1109
 *
sl@0
  1110
 *----------------------------------------------------------------------
sl@0
  1111
 */
sl@0
  1112
sl@0
  1113
static DWORD WINAPI
sl@0
  1114
ConsoleReaderThread(LPVOID arg)
sl@0
  1115
{
sl@0
  1116
    ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
sl@0
  1117
    HANDLE *handle = infoPtr->handle;
sl@0
  1118
    DWORD count, waitResult;
sl@0
  1119
    HANDLE wEvents[2];
sl@0
  1120
sl@0
  1121
    /* The first event takes precedence. */
sl@0
  1122
    wEvents[0] = infoPtr->stopReader;
sl@0
  1123
    wEvents[1] = infoPtr->startReader;
sl@0
  1124
sl@0
  1125
    for (;;) {
sl@0
  1126
	/*
sl@0
  1127
	 * Wait for the main thread to signal before attempting to wait.
sl@0
  1128
	 */
sl@0
  1129
sl@0
  1130
	waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
sl@0
  1131
sl@0
  1132
	if (waitResult != (WAIT_OBJECT_0 + 1)) {
sl@0
  1133
	    /*
sl@0
  1134
	     * The start event was not signaled.  It must be the stop event
sl@0
  1135
	     * or an error, so exit this thread.
sl@0
  1136
	     */
sl@0
  1137
sl@0
  1138
	    break;
sl@0
  1139
	}
sl@0
  1140
sl@0
  1141
	count = 0;
sl@0
  1142
sl@0
  1143
	/* 
sl@0
  1144
	 * Look for data on the console, but first ignore any events
sl@0
  1145
	 * that are not KEY_EVENTs 
sl@0
  1146
	 */
sl@0
  1147
	if (ReadConsoleA(handle, infoPtr->buffer, CONSOLE_BUFFER_SIZE,
sl@0
  1148
		(LPDWORD) &infoPtr->bytesRead, NULL) != FALSE) {
sl@0
  1149
	    /*
sl@0
  1150
	     * Data was stored in the buffer.
sl@0
  1151
	     */
sl@0
  1152
	    
sl@0
  1153
	    infoPtr->readFlags |= CONSOLE_BUFFERED;
sl@0
  1154
	} else {
sl@0
  1155
	    DWORD err;
sl@0
  1156
	    err = GetLastError();
sl@0
  1157
	    
sl@0
  1158
	    if (err == EOF) {
sl@0
  1159
		infoPtr->readFlags = CONSOLE_EOF;
sl@0
  1160
	    }
sl@0
  1161
	}
sl@0
  1162
sl@0
  1163
	/*
sl@0
  1164
	 * Signal the main thread by signalling the readable event and
sl@0
  1165
	 * then waking up the notifier thread.
sl@0
  1166
	 */
sl@0
  1167
sl@0
  1168
	SetEvent(infoPtr->readable);
sl@0
  1169
sl@0
  1170
	/*
sl@0
  1171
	 * Alert the foreground thread.  Note that we need to treat this like
sl@0
  1172
	 * a critical section so the foreground thread does not terminate
sl@0
  1173
	 * this thread while we are holding a mutex in the notifier code.
sl@0
  1174
	 */
sl@0
  1175
sl@0
  1176
	Tcl_MutexLock(&consoleMutex);
sl@0
  1177
	if (infoPtr->threadId != NULL) {
sl@0
  1178
	    /* TIP #218. When in flight ignore the event, no one will receive it anyway */
sl@0
  1179
	    Tcl_ThreadAlert(infoPtr->threadId);
sl@0
  1180
	}
sl@0
  1181
	Tcl_MutexUnlock(&consoleMutex);
sl@0
  1182
    }
sl@0
  1183
sl@0
  1184
    return 0;
sl@0
  1185
}
sl@0
  1186

sl@0
  1187
/*
sl@0
  1188
 *----------------------------------------------------------------------
sl@0
  1189
 *
sl@0
  1190
 * ConsoleWriterThread --
sl@0
  1191
 *
sl@0
  1192
 *	This function runs in a separate thread and writes data
sl@0
  1193
 *	onto a console.
sl@0
  1194
 *
sl@0
  1195
 * Results:
sl@0
  1196
 *	Always returns 0.
sl@0
  1197
 *
sl@0
  1198
 * Side effects:
sl@0
  1199
 *	Signals the main thread when an output operation is completed.
sl@0
  1200
 *	May cause the main thread to wake up by posting a message.  
sl@0
  1201
 *
sl@0
  1202
 *----------------------------------------------------------------------
sl@0
  1203
 */
sl@0
  1204
sl@0
  1205
static DWORD WINAPI
sl@0
  1206
ConsoleWriterThread(LPVOID arg)
sl@0
  1207
{
sl@0
  1208
sl@0
  1209
    ConsoleInfo *infoPtr = (ConsoleInfo *)arg;
sl@0
  1210
    HANDLE *handle = infoPtr->handle;
sl@0
  1211
    DWORD count, toWrite, waitResult;
sl@0
  1212
    char *buf;
sl@0
  1213
    HANDLE wEvents[2];
sl@0
  1214
sl@0
  1215
    /* The first event takes precedence. */
sl@0
  1216
    wEvents[0] = infoPtr->stopWriter;
sl@0
  1217
    wEvents[1] = infoPtr->startWriter;
sl@0
  1218
sl@0
  1219
    for (;;) {
sl@0
  1220
	/*
sl@0
  1221
	 * Wait for the main thread to signal before attempting to write.
sl@0
  1222
	 */
sl@0
  1223
sl@0
  1224
	waitResult = WaitForMultipleObjects(2, wEvents, FALSE, INFINITE);
sl@0
  1225
sl@0
  1226
	if (waitResult != (WAIT_OBJECT_0 + 1)) {
sl@0
  1227
	    /*
sl@0
  1228
	     * The start event was not signaled.  It must be the stop event
sl@0
  1229
	     * or an error, so exit this thread.
sl@0
  1230
	     */
sl@0
  1231
sl@0
  1232
	    break;
sl@0
  1233
	}
sl@0
  1234
sl@0
  1235
	buf = infoPtr->writeBuf;
sl@0
  1236
	toWrite = infoPtr->toWrite;
sl@0
  1237
sl@0
  1238
	/*
sl@0
  1239
	 * Loop until all of the bytes are written or an error occurs.
sl@0
  1240
	 */
sl@0
  1241
sl@0
  1242
	while (toWrite > 0) {
sl@0
  1243
	    if (WriteConsoleA(handle, buf, toWrite, &count, NULL) == FALSE) {
sl@0
  1244
		infoPtr->writeError = GetLastError();
sl@0
  1245
		break;
sl@0
  1246
	    } else {
sl@0
  1247
		toWrite -= count;
sl@0
  1248
		buf += count;
sl@0
  1249
	    }
sl@0
  1250
	}
sl@0
  1251
sl@0
  1252
	/*
sl@0
  1253
	 * Signal the main thread by signalling the writable event and
sl@0
  1254
	 * then waking up the notifier thread.
sl@0
  1255
	 */
sl@0
  1256
	
sl@0
  1257
	SetEvent(infoPtr->writable);
sl@0
  1258
sl@0
  1259
	/*
sl@0
  1260
	 * Alert the foreground thread.  Note that we need to treat this like
sl@0
  1261
	 * a critical section so the foreground thread does not terminate
sl@0
  1262
	 * this thread while we are holding a mutex in the notifier code.
sl@0
  1263
	 */
sl@0
  1264
sl@0
  1265
	Tcl_MutexLock(&consoleMutex);
sl@0
  1266
	if (infoPtr->threadId != NULL) {
sl@0
  1267
	    /* TIP #218. When in flight ignore the event, no one will receive it anyway */
sl@0
  1268
	    Tcl_ThreadAlert(infoPtr->threadId);
sl@0
  1269
	}
sl@0
  1270
	Tcl_MutexUnlock(&consoleMutex);
sl@0
  1271
    }
sl@0
  1272
sl@0
  1273
    return 0;
sl@0
  1274
}
sl@0
  1275
sl@0
  1276
sl@0
  1277

sl@0
  1278
/*
sl@0
  1279
 *----------------------------------------------------------------------
sl@0
  1280
 *
sl@0
  1281
 * TclWinOpenConsoleChannel --
sl@0
  1282
 *
sl@0
  1283
 *	Constructs a Console channel for the specified standard OS handle.
sl@0
  1284
 *      This is a helper function to break up the construction of 
sl@0
  1285
 *      channels into File, Console, or Serial.
sl@0
  1286
 *
sl@0
  1287
 * Results:
sl@0
  1288
 *	Returns the new channel, or NULL.
sl@0
  1289
 *
sl@0
  1290
 * Side effects:
sl@0
  1291
 *	May open the channel
sl@0
  1292
 *
sl@0
  1293
 *----------------------------------------------------------------------
sl@0
  1294
 */
sl@0
  1295
sl@0
  1296
Tcl_Channel
sl@0
  1297
TclWinOpenConsoleChannel(handle, channelName, permissions)
sl@0
  1298
    HANDLE handle;
sl@0
  1299
    char *channelName;
sl@0
  1300
    int permissions;
sl@0
  1301
{
sl@0
  1302
    char encoding[4 + TCL_INTEGER_SPACE];
sl@0
  1303
    ConsoleInfo *infoPtr;
sl@0
  1304
    DWORD id, modes;
sl@0
  1305
sl@0
  1306
    ConsoleInit();
sl@0
  1307
sl@0
  1308
    /*
sl@0
  1309
     * See if a channel with this handle already exists.
sl@0
  1310
     */
sl@0
  1311
    
sl@0
  1312
    infoPtr = (ConsoleInfo *) ckalloc((unsigned) sizeof(ConsoleInfo));
sl@0
  1313
    memset(infoPtr, 0, sizeof(ConsoleInfo));
sl@0
  1314
sl@0
  1315
    infoPtr->validMask = permissions;
sl@0
  1316
    infoPtr->handle = handle;
sl@0
  1317
    infoPtr->channel = (Tcl_Channel) NULL;
sl@0
  1318
sl@0
  1319
    wsprintfA(encoding, "cp%d", GetConsoleCP());
sl@0
  1320
sl@0
  1321
    infoPtr->threadId = Tcl_GetCurrentThread();
sl@0
  1322
sl@0
  1323
    /*
sl@0
  1324
     * Use the pointer for the name of the result channel.
sl@0
  1325
     * This keeps the channel names unique, since some may share
sl@0
  1326
     * handles (stdin/stdout/stderr for instance).
sl@0
  1327
     */
sl@0
  1328
sl@0
  1329
    wsprintfA(channelName, "file%lx", (int) infoPtr);
sl@0
  1330
    
sl@0
  1331
    infoPtr->channel = Tcl_CreateChannel(&consoleChannelType, channelName,
sl@0
  1332
            (ClientData) infoPtr, permissions);
sl@0
  1333
sl@0
  1334
    if (permissions & TCL_READABLE) {
sl@0
  1335
	/*
sl@0
  1336
	 * Make sure the console input buffer is ready for only character
sl@0
  1337
	 * input notifications and the buffer is set for line buffering.
sl@0
  1338
	 * IOW, we only want to catch when complete lines are ready for
sl@0
  1339
	 * reading.
sl@0
  1340
	 */
sl@0
  1341
	GetConsoleMode(infoPtr->handle, &modes);
sl@0
  1342
	modes &= ~(ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
sl@0
  1343
	modes |= ENABLE_LINE_INPUT;
sl@0
  1344
	SetConsoleMode(infoPtr->handle, modes);
sl@0
  1345
sl@0
  1346
	infoPtr->readable = CreateEvent(NULL, TRUE, TRUE, NULL);
sl@0
  1347
	infoPtr->startReader = CreateEvent(NULL, FALSE, FALSE, NULL);
sl@0
  1348
	infoPtr->stopReader = CreateEvent(NULL, FALSE, FALSE, NULL);
sl@0
  1349
	infoPtr->readThread = CreateThread(NULL, 256, ConsoleReaderThread,
sl@0
  1350
	        infoPtr, 0, &id);
sl@0
  1351
	SetThreadPriority(infoPtr->readThread, THREAD_PRIORITY_HIGHEST);
sl@0
  1352
    }
sl@0
  1353
sl@0
  1354
    if (permissions & TCL_WRITABLE) {
sl@0
  1355
	infoPtr->writable = CreateEvent(NULL, TRUE, TRUE, NULL);
sl@0
  1356
	infoPtr->startWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
sl@0
  1357
	infoPtr->stopWriter = CreateEvent(NULL, FALSE, FALSE, NULL);
sl@0
  1358
	infoPtr->writeThread = CreateThread(NULL, 256, ConsoleWriterThread,
sl@0
  1359
	        infoPtr, 0, &id);
sl@0
  1360
	SetThreadPriority(infoPtr->writeThread, THREAD_PRIORITY_HIGHEST);
sl@0
  1361
    }
sl@0
  1362
sl@0
  1363
    /*
sl@0
  1364
     * Files have default translation of AUTO and ^Z eof char, which
sl@0
  1365
     * means that a ^Z will be accepted as EOF when reading.
sl@0
  1366
     */
sl@0
  1367
    
sl@0
  1368
    Tcl_SetChannelOption(NULL, infoPtr->channel, "-translation", "auto");
sl@0
  1369
    Tcl_SetChannelOption(NULL, infoPtr->channel, "-eofchar", "\032 {}");
sl@0
  1370
    Tcl_SetChannelOption(NULL, infoPtr->channel, "-encoding", encoding);
sl@0
  1371
sl@0
  1372
    return infoPtr->channel;
sl@0
  1373
}
sl@0
  1374

sl@0
  1375
/*
sl@0
  1376
 *----------------------------------------------------------------------
sl@0
  1377
 *
sl@0
  1378
 * ConsoleThreadActionProc --
sl@0
  1379
 *
sl@0
  1380
 *	Insert or remove any thread local refs to this channel.
sl@0
  1381
 *
sl@0
  1382
 * Results:
sl@0
  1383
 *	None.
sl@0
  1384
 *
sl@0
  1385
 * Side effects:
sl@0
  1386
 *	Changes thread local list of valid channels.
sl@0
  1387
 *
sl@0
  1388
 *----------------------------------------------------------------------
sl@0
  1389
 */
sl@0
  1390
sl@0
  1391
static void
sl@0
  1392
ConsoleThreadActionProc (instanceData, action)
sl@0
  1393
     ClientData instanceData;
sl@0
  1394
     int action;
sl@0
  1395
{
sl@0
  1396
    ConsoleInfo *infoPtr = (ConsoleInfo *) instanceData;
sl@0
  1397
sl@0
  1398
    /* We do not access firstConsolePtr in the thread structures. This is
sl@0
  1399
     * not for all serials managed by the thread, but only those we are
sl@0
  1400
     * watching. Removal of the filevent handlers before transfer thus
sl@0
  1401
     * takes care of this structure.
sl@0
  1402
     */
sl@0
  1403
sl@0
  1404
    Tcl_MutexLock(&consoleMutex);
sl@0
  1405
    if (action == TCL_CHANNEL_THREAD_INSERT) {
sl@0
  1406
        /* We can't copy the thread information from the channel when
sl@0
  1407
	 * the channel is created. At this time the channel back
sl@0
  1408
	 * pointer has not been set yet. However in that case the
sl@0
  1409
	 * threadId has already been set by TclpCreateCommandChannel
sl@0
  1410
	 * itself, so the structure is still good.
sl@0
  1411
	 */
sl@0
  1412
sl@0
  1413
        ConsoleInit ();
sl@0
  1414
        if (infoPtr->channel != NULL) {
sl@0
  1415
	    infoPtr->threadId = Tcl_GetChannelThread (infoPtr->channel);
sl@0
  1416
	}
sl@0
  1417
    } else {
sl@0
  1418
	infoPtr->threadId = NULL;
sl@0
  1419
    }
sl@0
  1420
    Tcl_MutexUnlock(&consoleMutex);
sl@0
  1421
}