Tcl Source Code

Check-in [0df14240cc]
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:Fixed serial port communication, no longer blocks on gets in non-blocking mode.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | core-8-1-branch-old
Files: files | file ages | folders
SHA1: 0df14240cc7d6b9b66ef6f49f86fbc964b3b5615
User & Date: redman 1999-03-24 02:37:14
Context
1999-03-24
02:38
Fixed serial port check-in: 762e3896db user: redman tags: core-8-1-branch-old
02:37
Fixed serial port communication, no longer blocks on gets in non-blocking mode. check-in: 0df14240cc user: redman tags: core-8-1-branch-old
01:42
Fixed initialization bug [Bug ID: 647] check-in: 1aebc74e85 user: surles tags: core-8-1-branch-old
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to win/tclWinSerial.c.

     5      5    *	and the "serial" channel driver.
     6      6    *
     7      7    * Copyright (c) 1999 by Scriptics Corp.
     8      8    *
     9      9    * See the file "license.terms" for information on usage and redistribution
    10     10    * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
    11     11    *
    12         - * RCS: @(#) $Id: tclWinSerial.c,v 1.1.2.2 1999/03/14 18:55:12 stanton Exp $
           12  + * RCS: @(#) $Id: tclWinSerial.c,v 1.1.2.3 1999/03/24 02:37:14 redman Exp $
    13     13    */
    14     14   
    15     15   #include "tclWinInt.h"
    16     16   
    17     17   #include <dos.h>
    18     18   #include <fcntl.h>
    19     19   #include <io.h>
................................................................................
    34     34   #define SERIAL_PENDING	(1<<0)	/* Message is pending in the queue. */
    35     35   #define SERIAL_ASYNC	(1<<1)	/* Channel is non-blocking. */
    36     36   
    37     37   /*
    38     38    * Bit masks used in the sharedFlags field of the SerialInfo structure below.
    39     39    */
    40     40   
    41         -#define SERIAL_EOF	(1<<2)	/* Serial has reached EOF. */
    42         -
           41  +#define SERIAL_EOF	 (1<<2)  /* Serial has reached EOF. */
           42  +#define SERIAL_EXTRABYTE (1<<3)  /* Extra byte consumed while waiting for data */
    43     43   /*
    44     44    * This structure describes per-instance data for a serial based channel.
    45     45    */
    46     46   
    47     47   typedef struct SerialInfo {
    48     48       HANDLE handle;
    49     49       struct SerialInfo *nextPtr;	/* Pointer to next registered serial. */
................................................................................
    89     89       int readFlags;		/* Flags that are shared with the reader
    90     90   				 * thread.  Access is synchronized with the
    91     91   				 * readable object.  */
    92     92       int writeFlags;		/* Flags that are shared with the writer
    93     93   				 * thread.  Access is synchronized with the
    94     94   				 * readable object.  */
    95     95       int readyMask;              /* Events that need to be checked on. */
           96  +    char extraByte;
           97  +    
    96     98   } SerialInfo;
    97     99   
    98    100   typedef struct ThreadSpecificData {
    99    101       /*
   100    102        * The following pointer refers to the head of the list of serials
   101    103        * that are being watched for file events.
   102    104        */
................................................................................
   464    466   	CloseHandle(serialPtr->startReader);
   465    467   	serialPtr->readThread = NULL;
   466    468       }
   467    469       serialPtr->validMask &= ~TCL_READABLE;
   468    470   
   469    471       if (serialPtr->writeThread) {
   470    472   	WaitForSingleObject(serialPtr->writable, INFINITE);
          473  +	TerminateThread(serialPtr->writeThread, 0);
   471    474   
   472    475   	/*
   473    476   	 * Wait for the thread to terminate.  This ensures that we are
   474    477   	 * completely cleaned up before we leave this function. 
   475    478   	 */
   476    479   
   477    480   	WaitForSingleObject(serialPtr->writeThread, INFINITE);
   478         -	TerminateThread(serialPtr->writeThread, 0);
   479    481   	CloseHandle(serialPtr->writeThread);
   480    482   	CloseHandle(serialPtr->writable);
   481    483   	CloseHandle(serialPtr->startWriter);
   482    484   	serialPtr->writeThread = NULL;
   483    485       }
   484    486       serialPtr->validMask &= ~TCL_WRITABLE;
   485    487   
................................................................................
   546    548       int bufSize,			/* How much space is available
   547    549                                            * in the buffer? */
   548    550       int *errorCode)			/* Where to store error code. */
   549    551   {
   550    552       SerialInfo *infoPtr = (SerialInfo *) instanceData;
   551    553       DWORD bytesRead = 0;
   552    554       int result;
   553         -    DWORD errors, err;
   554         -    DWORD mask = 0;
   555         -    COMSTAT stat;
          555  +    DWORD err;
   556    556   
   557    557       *errorCode = 0;
   558    558   
   559    559       /*
   560    560        * Synchronize with the reader thread.
   561    561        */
   562    562       
................................................................................
   567    567        */
   568    568       
   569    569       if (result == -1) {
   570    570   	*errorCode = errno;
   571    571   	return -1;
   572    572       }
   573    573   
   574         -    infoPtr->readyMask &= ~TCL_READABLE;
   575         -    
   576         -    if (ClearCommError(infoPtr->handle, &errors, &stat)) {
   577         -
   578         -	/*
   579         -	 * If there are errors, then signal an I/O error.
   580         -	 */
   581         -
   582         -	if (errors != 0) {
   583         -	    *errorCode = EIO;
   584         -	    return -1;
   585         -	}
   586         -
   587         -	/*
   588         -	 * Otherwise, it's not that fatal, so check the queue
   589         -	 */
   590         -
   591         -	if (stat.cbInQue != 0) {
   592         -	    
   593         -	    /* 
   594         -	     * If the buffer is bigger than the data in the queue,
   595         -	     * shrink the buffer size to match.
   596         -	     */
   597         -	    
   598         -	    if ((DWORD) bufSize > stat.cbInQue) {
   599         -		bufSize = stat.cbInQue;
   600         -	    }
   601         -
   602         -	} else {
   603         -	    if (infoPtr->flags & SERIAL_ASYNC) {
   604         -		errno = *errorCode = EAGAIN;
   605         -		return -1;
   606         -	    } else {
   607         -		bufSize = 1;
   608         -	    }
   609         -	}
   610         -    }
   611         -    
          574  +    if (infoPtr->readFlags & SERIAL_EXTRABYTE) {
          575  +
          576  +	/*
          577  +	 * If a byte was consumed waiting, then put it in the buffer.
          578  +	 */
          579  +
          580  +	*buf = infoPtr->extraByte;
          581  +	infoPtr->readFlags &= ~SERIAL_EXTRABYTE;
          582  +	buf++;
          583  +	bufSize--;
          584  +	bytesRead = 1;
          585  +
          586  +	if (result == 0) {
          587  +	    return bytesRead;
          588  +	}
          589  +    }
          590  +
   612    591       if (ReadFile(infoPtr->handle, (LPVOID) buf, (DWORD) bufSize, &bytesRead,
   613    592   		 NULL) == FALSE) {
   614    593   	err = GetLastError();
   615    594   	if (err != ERROR_IO_PENDING) {
   616    595   	    goto error;
   617    596   	}
   618    597       }
................................................................................
   923    902   
   924    903   static int
   925    904   WaitForRead(
   926    905       SerialInfo *infoPtr,	/* Serial state. */
   927    906       int blocking)		/* Indicates whether call should be
   928    907   				 * blocking or not. */
   929    908   {
   930         -    DWORD timeout, err, mask;
          909  +    DWORD timeout, errors;
   931    910       HANDLE *handle = infoPtr->handle;
          911  +    COMSTAT stat;
          912  +    
          913  +    while (1) {
          914  +	/*
          915  +	 * Synchronize with the reader thread.
          916  +	 */
          917  +	
          918  +	timeout = blocking ? INFINITE : 0;
          919  +	if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) {
          920  +	    /*
          921  +	     * The reader thread is blocked waiting for data and the channel
          922  +	     * is in non-blocking mode.
          923  +	     */
          924  +	    
          925  +	    errno = EAGAIN;
          926  +	    return -1;
          927  +	}
          928  +	
          929  +	/*
          930  +	 * At this point, the two threads are synchronized, so it is safe
          931  +	 * to access shared state.  This code is not called in the ReaderThread
          932  +	 * in blocking mode, so it needs to be checked here.
          933  +	 */
   932    934   
   933         -    /*
   934         -     * Synchronize with the reader thread.
   935         -     */
          935  +	/*
          936  +	 * If the serial has hit EOF, it is always readable.
          937  +	 */
   936    938       
   937         -    timeout = blocking ? INFINITE : 0;
   938         -    if (WaitForSingleObject(infoPtr->readable, timeout) == WAIT_TIMEOUT) {
          939  +	if (infoPtr->readFlags & SERIAL_EOF) {
          940  +	    return 1;
          941  +	}
          942  +	
          943  +	if (ClearCommError(infoPtr->handle, &errors, &stat)) {
          944  +	    /*
          945  +	     * If there are errors, then signal an I/O error.
          946  +	     */
          947  +
          948  +	    if (errors != 0) {
          949  +		errno = EIO;
          950  +		return -1;
          951  +	    }
          952  +	}
          953  +	
          954  +	/*
          955  +	 * If data is in the queue return 1
          956  +	 */
          957  +	
          958  +	if (stat.cbInQue != 0) {
          959  +	    return 1;
          960  +	}
          961  +
   939    962   	/*
   940         -	 * The reader thread is blocked waiting for data and the channel
   941         -	 * is in non-blocking mode.
          963  +	 * if there is an extra byte that was consumed while
          964  +	 * waiting, but no data in the queue, return 0
   942    965   	 */
   943    966   	
   944         -	errno = EAGAIN;
   945         -	return -1;
   946         -    }
   947         -    
   948         -    /*
   949         -     * At this point, the two threads are synchronized, so it is safe
   950         -     * to access shared state.  This code is not called in the ReaderThread
   951         -     * in blocking mode, so it needs to be checked here.
   952         -     */
   953         -
   954         -    while (1) {
   955         -	if (WaitCommEvent(handle, &mask, NULL) == FALSE) {
   956         -	    err = GetLastError();
   957         -	    infoPtr->readFlags |= SERIAL_EOF;
   958         -	    break;
          967  +	if (infoPtr->readFlags & SERIAL_EXTRABYTE) {
          968  +	    return 0;
   959    969   	}
   960         -	if (mask & EV_RXCHAR) {
   961         -	    break;
   962         -	}
          970  +	    
   963    971   	ResetEvent(infoPtr->readable);
   964    972   	SetEvent(infoPtr->startReader);
   965    973       }
   966         -    
   967         -    /*
   968         -     * If the serial has hit EOF, it is always readable.
   969         -     */
   970         -    
   971         -    if (infoPtr->readFlags & SERIAL_EOF) {
   972         -	return 1;
   973         -    }
   974         -    
   975         -    return 1;
   976    974   }
   977    975   
   978    976   /*
   979    977    *----------------------------------------------------------------------
   980    978    *
   981    979    * SerialReaderThread --
   982    980    *
................................................................................
   996    994   
   997    995   static DWORD WINAPI
   998    996   SerialReaderThread(LPVOID arg)
   999    997   {
  1000    998       SerialInfo *infoPtr = (SerialInfo *)arg;
  1001    999       HANDLE *handle = infoPtr->handle;
  1002   1000       DWORD mask = EV_RXCHAR;
  1003         -
         1001  +    DWORD count;
         1002  +    
  1004   1003       for (;;) {
  1005   1004   	/*
  1006   1005   	 * Wait for the main thread to signal before attempting to wait.
  1007   1006   	 */
  1008   1007   
  1009   1008   	WaitForSingleObject(infoPtr->startReader, INFINITE);
  1010   1009   
  1011   1010   	/*
  1012   1011   	 * Try waiting for a Comm event.
  1013   1012   	 */
         1013  +	
         1014  +	WaitCommEvent(handle, NULL, NULL);
         1015  +	
  1014   1016   
         1017  +	/*
         1018  +	 * try to read one byte
         1019  +	 */
  1015   1020   	
  1016         -	while (1) {
  1017         -	    if (WaitCommEvent(handle, &mask, NULL) == FALSE) {
  1018         -		/*
  1019         -		 * There is an error, signal an EOF.
  1020         -		 */
  1021         -
  1022         -		infoPtr->readFlags |= SERIAL_EOF;
  1023         -		break;
  1024         -	    }
         1021  +	if (ReadFile(handle, &(infoPtr->extraByte), 1, &count, NULL)
         1022  +		!= FALSE) {
  1025   1023   
  1026   1024   	    /*
  1027         -	     * Signal the main thread by signalling the readable event and
  1028         -	     * then waking up the notifier thread.
         1025  +	     * one byte was consumed while waiting to read, keep it
  1029   1026   	     */
  1030   1027   
  1031         -	    if (mask & EV_RXCHAR) {
  1032         -		SetEvent(infoPtr->readable);
  1033         -		Tcl_ThreadAlert(infoPtr->threadId);
  1034         -		break;
         1028  +	    if (count != 0) {
         1029  +		infoPtr->readFlags |= SERIAL_EXTRABYTE;
  1035   1030   	    }
         1031  +
         1032  +	} else {
         1033  +            /*
         1034  +	     * There is an error, signal an EOF.
         1035  +	     */
         1036  +	    
         1037  +	    infoPtr->readFlags |= SERIAL_EOF;
  1036   1038   	}
         1039  +
         1040  +	/*
         1041  +	 * Signal the main thread by signalling the readable event and
         1042  +	 * then waking up the notifier thread.
         1043  +	 */
         1044  +
         1045  +	SetEvent(infoPtr->readable);
         1046  +	Tcl_ThreadAlert(infoPtr->threadId);
  1037   1047       }
  1038   1048       return 0;			/* NOT REACHED */
  1039   1049   }
  1040   1050   
  1041   1051   /*
  1042   1052    *----------------------------------------------------------------------
  1043   1053    *
................................................................................
  1139   1149       tsdPtr = SerialInit();
  1140   1150   
  1141   1151       SetCommMask(handle, EV_RXCHAR);
  1142   1152       SetupComm(handle, 4096, 4096);
  1143   1153       PurgeComm(handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR
  1144   1154   	      | PURGE_RXCLEAR);
  1145   1155       cto.ReadIntervalTimeout = MAXDWORD;
  1146         -    cto.ReadTotalTimeoutMultiplier = 0;
  1147         -    cto.ReadTotalTimeoutConstant = 0;
         1156  +    cto.ReadTotalTimeoutMultiplier = MAXDWORD;
         1157  +    cto.ReadTotalTimeoutConstant = 1;
  1148   1158       cto.WriteTotalTimeoutMultiplier = 0;
  1149   1159       cto.WriteTotalTimeoutConstant = 0;
  1150   1160       SetCommTimeouts(handle, &cto);
  1151   1161       
  1152   1162       infoPtr = (SerialInfo *) ckalloc((unsigned) sizeof(SerialInfo));
  1153   1163       memset(infoPtr, 0, sizeof(SerialInfo));
  1154   1164