File:  [OS/2 SDKs] / pmsdk / samples / comtalk / threads.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Thu Aug 9 12:28:17 2018 UTC (7 years, 9 months ago) by root
Branches: msft, MAIN
CVS tags: pmsdk-1989, HEAD
Microsoft OS/2 SDK PM 02-24-1989

/*
    threads.c -- Supplementary threads module
    Created by Microsoft Corporation, 1989
*/
#define 	INCL_DOSSEMAPHORES
#define		INCL_DOSMEMMGR
#define		INCL_DOSPROCESS
#define		INCL_WINMESSAGEMGR
#define		INCL_WINTRACKRECT
#include	<os2.h>
#include	"global.h"
#include	"avio.h"
#include	"circleq.h"
#include	"comport.h"
#include	"threads.h"
#include	"malloc.h"

int		rc;
HWND		hWndMaster;
BOOL		fNoUpdate = TRUE;
BOOL		fWrap;
/*
    Declare pointers to stacks
*/
PINT		pStackWPT,
		pStackRPT,
		pStackWST;
/*
    ...Selectors
*/
SEL		selStackWPT,
		selStackRPT,
		selStackWST;
/*
    ...Thread ID numbers
*/
TID		tidWPT,
		tidRPT,
		tidWST;
/*
    ...Control booleans, semaphores
*/
BOOL		fBreak,			/* Break active */
		fAlive;			/* Should the threads be killed? */
LONG		lSemLock,		/* TypeAhead buffer locks... */
		lSemEmpty,
		lSemFull,
		lSemOverflow;		/* Buffer overflow semaphore */
CHAR		TypeAhead[BUFSIZE];	/* TypeAhead buffer and controls */
int		nBufLoc, nChars;
LineInfo	aliReadAhead[RASIZE];
int		cliReadAhead;
LONG		lSemRALock;
LONG		lSemRAFull;
LONG		lSemRAEmpty;
/*
    Messages....
*/
char aszMessage[MBE_NUMMSGS][MAXLINELEN] = {
    "Error opening port",
    "Error writing port",
    "Error reading port",
    "Circular buffer overflowing"
};
/*
    Macros
*/
#define	NOUPDATE		(MRESULT) FALSE
#define	UPDATE			(MRESULT) TRUE
#define	ThdBufNextLoc(n)	(n = ((n + 1) % BUFSIZE))
#define	ThdBufLastLoc(n)	(n = (n > 0) ? (n - 1) : (BUFSIZE - 1))
#define	ThdNextRALoc(n)		(n = ((n + 1) % RASIZE))
#define MessageBox(s, v)	WinPostMsg(hWndMaster, WM_MSGBOX, s, v)
/*
    Local routines
*/
void far WritePortThread(void);
void far ReadPortThread(void);
void far WriteScreenThread(void);
void Process(Line, Line, int far *);

int ThdCreate(PFNTHREAD Routine, PBYTE *pStack, SEL *selStack, TID *tidThread) {
/*
    Initialize the thread
*/
    if (rc = DosAllocSeg(sizeof(int) * STACKSIZE, selStack, 0)) return rc;
    *pStack = (PBYTE) MAKEP(*selStack, 0) + STACKSIZE;
    return (rc = DosCreateThread(Routine, tidThread, *pStack));
}

void ThdInitialize(HWND hWnd, COM Term) {
/*
    Initialize Booleans, Master Window
*/
    fBreak = FALSE;
    fAlive = TRUE;
    hWndMaster = hWnd;
/*
    Initialize TypeAhead buffer
*/
    nBufLoc = -1;
    nChars = 0;
/*
    Initialize ReadAhead buffer
*/
    cliReadAhead = 0;
/*
    Spawn off the threads
*/
    rc = ThdCreate(WritePortThread,
		   (PBYTE *) &pStackWPT, &selStackWPT, &tidWPT);
    rc = ThdCreate(ReadPortThread,
		   (PBYTE *) &pStackRPT, &selStackRPT, &tidRPT);
    rc = ThdCreate(WriteScreenThread,
		   (PBYTE *) &pStackWST, &selStackWST, &tidWST);
/*
    Open up the COM port and the circular queue
*/
    if (rc = ComInit(Term))	{ /* Initialize terminal */
	MessageBox(MBE_OPENPORT, NOUPDATE);
    } 
    fWrap = Term.fWrap;
}

void ThdTerminate(void) {
/*
    Kill the threads (maybe time delayed) and clean up.
    Yes, I throw away the return codes.

    Problem:  The ReadPort thread might not die, because it blocks
    waiting for a character to be read.
*/
    if (fAlive) {
	fAlive = FALSE;			/* Kill thread loops		*/
	if (fBreak) rc = ComUnbreak();	/* Remove break signal		*/
	rc = ComClose();		/* Close the port 		*/
	DosSemClear(&lSemEmpty);	/* Make WritePort unblock	*/
	DosSemClear(&lSemRAFull);	/* Make ReadPort unblock	*/
	DosSemClear(&lSemRAEmpty);	/* Make WriteScreen unblock	*/
    }
}

void ThdDoBreak(void) {
/*
    Try to send break for a second
*/
    ComBreak();
    DosSleep(1000L);
    ComUnbreak();
}

int ThdPutChar(char ch) {
/*
    Perhaps we will enter an entire key packet
    But we can't really block, because we gotta respond.
    Solution:  This time, we time out.
*/
/*
    Manipulate the typeahead buffer (circular queue)
*/
    if (nChars >= BUFSIZE) {		/* Block if buffer full */
	DosSemSet(&lSemFull);
	/*
	    Timeout possibility; probably want TIMEOUT < 1 second
	*/
	if (rc = DosSemWait(&lSemFull, TIMEOUT)) return rc;
    }
    ThdBufNextLoc(nBufLoc);		/* Increments to next location */
    /*
	Be really impatient...
	This protects the queue, but we should never read/write
	the same queue location.  Maybe I'll move it out later.
    */
    if (rc = DosSemRequest(&lSemLock, TIMEOUT)) { /* Another quick timeout */
	ThdBufLastLoc(nBufLoc);
	return rc;
    }
    TypeAhead[nBufLoc] = ch; 
    nChars++;
    DosSemClear(&lSemEmpty);
    DosSemClear(&lSemLock);
    return 0;
}

void far WritePortThread(void) {
/*
    The routine which writes your WM_CHARS to the port,
    one at a time, nice and easy.  We can even wait all
    day (and will, at the rate a user types....)

    The typeahead buffer is protected with semaphores,
    although a move is probably an atomic operation;
    also, it should be secure to read the element, without
    having to protect the buffer.
*/
    int MyLoc = -1;
    char ch;

    while (fAlive) {
	if (nChars < 1) {		/* Wait if the queue is empty */
	    DosSemSet(&lSemEmpty);
	    DosSemWait(&lSemEmpty, MAXTIMEOUT);
	} else if (!(rc = DosSemRequest(&lSemLock, MAXTIMEOUT))) {
	    ThdBufNextLoc(MyLoc);
	    ch = TypeAhead[MyLoc];	/* writing to the port is slow, and */
	    nChars--;			/* we want to release the semaphore */
	    DosSemClear(&lSemFull);
	    DosSemClear(&lSemLock);
	    if (rc = ComWrite(TypeAhead[MyLoc])) {
		/* Post the message... */
		MessageBox(MBE_WRITEPORT, NOUPDATE);
	    }
	}
    }
    DosExit(EXIT_THREAD, 0);
}

void far ReadPortThread(void) {
/*
    Read from the port, a line at a time
    The semaphores force private queue access
*/
    int iLine = 0;

    while (fAlive) {
	if (cliReadAhead < RASIZE) {
	    if (ComRead(&aliReadAhead[iLine]))
		MessageBox(MBE_COMREAD, (MPARAM) ComError());
	    else if (&aliReadAhead[iLine].cch) {
		DosSemRequest(&lSemRALock, MAXTIMEOUT);
		cliReadAhead++;
		DosSemClear(&lSemRALock);
		DosSemClear(&lSemRAEmpty);
		ThdNextRALoc(iLine);
	    }
	} else {
	    DosSemSet(&lSemRAFull);
	    DosSemWait(&lSemRAFull, TIMEOUT);
	}
    }
    DosExit(EXIT_THREAD, 0);
}

int ThdPutString(char a[], int n) {
    int i, rc = 0;

    for (i = 0; i < n; i++)
	if (!rc) rc = ThdPutChar(a[i]);
    return rc;
}

void Process(Line lCmd, Line lOutput, int far *pi) {
/*
    This routine filters characters from the port, before we
    display them to the screen.

    To make this a full blown terminal emulator application,
    all that's needed is to trap cursor movement sequences
    here, and then to retrieve the appropriate queue line.
*/
    USHORT usTemp;
    int i;

    while ((lCmd->szText[*pi] != '\n') && ((*pi) < lCmd->cch)) {
	switch (lCmd->szText[*pi]) {
	    case '\b':
		if (lOutput->cch > 0) lOutput->cch--;
		break;
	    case '\r':
	    case '\0':
		break;
	    case '\007':	/* Ctrl G */
		WinAlarm(HWND_DESKTOP, WA_NOTE);
		break;
	    case '\t':
		if ((usTemp = (((lOutput->cch >> 3) + 1) << 3)) < MAXLINELEN) {
		    for (i = lOutput->cch; i < usTemp; i++)
			lOutput->szText[i] = ' ';
		    lOutput->cch = usTemp;
		}
		break;
	    default:
		if (fWrap) {
		    if (lOutput->cch >= MAXLINELEN) {
			lOutput->fComplete = TRUE;
			lOutput->fDrawn = FALSE;
			return;
		    } else lOutput->szText[lOutput->cch++] = lCmd->szText[*pi];
		} else {
		    if (lOutput->cch < MAXLINELEN) /* Straight nowrap */
			lOutput->szText[lOutput->cch++] = lCmd->szText[*pi];
		}
		break;
	}
	(*pi)++;
    }
    /*
	Complete line if it doesn't exhaust the command string, and
	ends with a newline (\n).  If so, increment string length, but
	don't write (potentially) out of the array bounds.
    */
    if (lOutput->fComplete =
		(((*pi) < lCmd->cch) && (lCmd->szText[*pi] == '\n')))
	(*pi)++;

    lOutput->fDrawn = FALSE;
}

void ThdReset(void) {
    DosSemClear(&lSemOverflow);
}

void far WriteScreenThread(void) {
    int		iLine = 0;
    LineInfo	liQueueEntry;
    int		iLinePos = 0;
    BOOL	fMore = FALSE;

    liQueueEntry.cch = 0;
    while (fAlive) {
	if ((cliReadAhead > 0) || fMore) {
	    Process(&aliReadAhead[iLine], &liQueueEntry, &iLinePos);
	    /*
		Add the entry to the queue if it's:
		    1) A complete line
		    2) There's nothing more to read
	    */
	    if (liQueueEntry.fComplete || (cliReadAhead <= 1)) {
		while (!QueInsertLine(&liQueueEntry)) {
		    MessageBox(MBE_QUEUEFULL, UPDATE);
		    DosSemSet(&lSemOverflow);
		    DosSemWait(&lSemOverflow, MAXTIMEOUT);
		}
		/*
		    If complete line is read, reset line pointer
		*/
		if (liQueueEntry.fComplete) {
		    liQueueEntry.cch = 0;
		    /*
			If done moving, move cursor to the right place.
			(the beginning of the next line), so that the
			user knows when they've hit <Enter>.
		    */
		    if (cliReadAhead <= 1) {
			liQueueEntry.fComplete = liQueueEntry.fDrawn = FALSE;
			while (!QueInsertLine(&liQueueEntry)) {
			    MessageBox(MBE_QUEUEFULL, UPDATE);
			    DosSemSet(&lSemOverflow);
			    DosSemWait(&lSemOverflow, MAXTIMEOUT);
			}
		    }
		}
	    }
	    /*
		If there's no more to process, bump the read pointer
		and possibly unblock the Read Port thread
	    */
	    if (!(fMore = (iLinePos < aliReadAhead[iLine].cch))) {
		ThdNextRALoc(iLine);
		iLinePos = 0;
		DosSemRequest(&lSemRALock, MAXTIMEOUT);
		cliReadAhead--;
		DosSemClear(&lSemRALock);
		DosSemClear(&lSemRAFull);
	    }
	} else {
	    if (fNoUpdate) {
		fNoUpdate = WinPostMsg(hWndMaster, WM_AVIOUPDATE, NULL, NULL);
	    }
	    DosSemSet(&lSemRAEmpty);
	    DosSemWait(&lSemRAEmpty, TIMEOUT);
	}
    }
    DosExit(EXIT_THREAD, 0);
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.