|
|
1.1 root 1: /*
2: threads.c -- Supplementary threads module
3: Created by Microsoft Corporation, 1989
4: */
5: #define INCL_DOSSEMAPHORES
6: #define INCL_DOSMEMMGR
7: #define INCL_DOSPROCESS
8: #define INCL_WINMESSAGEMGR
9: #define INCL_WINTRACKRECT
10: #include <os2.h>
11: #include "global.h"
12: #include "avio.h"
13: #include "circleq.h"
14: #include "comport.h"
15: #include "threads.h"
16: #include "malloc.h"
17:
18: int rc;
19: HWND hWndMaster;
20: BOOL fNoUpdate = TRUE;
21: BOOL fWrap;
22: /*
23: Declare pointers to stacks
24: */
25: PINT pStackWPT,
26: pStackRPT,
27: pStackWST;
28: /*
29: ...Selectors
30: */
31: SEL selStackWPT,
32: selStackRPT,
33: selStackWST;
34: /*
35: ...Thread ID numbers
36: */
37: TID tidWPT,
38: tidRPT,
39: tidWST;
40: /*
41: ...Control booleans, semaphores
42: */
43: BOOL fBreak, /* Break active */
44: fAlive; /* Should the threads be killed? */
45: LONG lSemLock, /* TypeAhead buffer locks... */
46: lSemEmpty,
47: lSemFull,
48: lSemOverflow; /* Buffer overflow semaphore */
49: CHAR TypeAhead[BUFSIZE]; /* TypeAhead buffer and controls */
50: int nBufLoc, nChars;
51: LineInfo aliReadAhead[RASIZE];
52: int cliReadAhead;
53: LONG lSemRALock;
54: LONG lSemRAFull;
55: LONG lSemRAEmpty;
56: /*
57: Messages....
58: */
59: char aszMessage[MBE_NUMMSGS][MAXLINELEN] = {
60: "Error opening port",
61: "Error writing port",
62: "Error reading port",
63: "Circular buffer overflowing"
64: };
65: /*
66: Macros
67: */
68: #define NOUPDATE (MRESULT) FALSE
69: #define UPDATE (MRESULT) TRUE
70: #define ThdBufNextLoc(n) (n = ((n + 1) % BUFSIZE))
71: #define ThdBufLastLoc(n) (n = (n > 0) ? (n - 1) : (BUFSIZE - 1))
72: #define ThdNextRALoc(n) (n = ((n + 1) % RASIZE))
73: #define MessageBox(s, v) WinPostMsg(hWndMaster, WM_MSGBOX, s, v)
74: /*
75: Local routines
76: */
77: void far WritePortThread(void);
78: void far ReadPortThread(void);
79: void far WriteScreenThread(void);
80: void Process(Line, Line, int far *);
81:
82: int ThdCreate(PFNTHREAD Routine, PBYTE *pStack, SEL *selStack, TID *tidThread) {
83: /*
84: Initialize the thread
85: */
86: if (rc = DosAllocSeg(sizeof(int) * STACKSIZE, selStack, 0)) return rc;
87: *pStack = (PBYTE) MAKEP(*selStack, 0) + STACKSIZE;
88: return (rc = DosCreateThread(Routine, tidThread, *pStack));
89: }
90:
91: void ThdInitialize(HWND hWnd, COM Term) {
92: /*
93: Initialize Booleans, Master Window
94: */
95: fBreak = FALSE;
96: fAlive = TRUE;
97: hWndMaster = hWnd;
98: /*
99: Initialize TypeAhead buffer
100: */
101: nBufLoc = -1;
102: nChars = 0;
103: /*
104: Initialize ReadAhead buffer
105: */
106: cliReadAhead = 0;
107: /*
108: Spawn off the threads
109: */
110: rc = ThdCreate(WritePortThread,
111: (PBYTE *) &pStackWPT, &selStackWPT, &tidWPT);
112: rc = ThdCreate(ReadPortThread,
113: (PBYTE *) &pStackRPT, &selStackRPT, &tidRPT);
114: rc = ThdCreate(WriteScreenThread,
115: (PBYTE *) &pStackWST, &selStackWST, &tidWST);
116: /*
117: Open up the COM port and the circular queue
118: */
119: if (rc = ComInit(Term)) { /* Initialize terminal */
120: MessageBox(MBE_OPENPORT, NOUPDATE);
121: }
122: fWrap = Term.fWrap;
123: }
124:
125: void ThdTerminate(void) {
126: /*
127: Kill the threads (maybe time delayed) and clean up.
128: Yes, I throw away the return codes.
129:
130: Problem: The ReadPort thread might not die, because it blocks
131: waiting for a character to be read.
132: */
133: if (fAlive) {
134: fAlive = FALSE; /* Kill thread loops */
135: if (fBreak) rc = ComUnbreak(); /* Remove break signal */
136: rc = ComClose(); /* Close the port */
137: DosSemClear(&lSemEmpty); /* Make WritePort unblock */
138: DosSemClear(&lSemRAFull); /* Make ReadPort unblock */
139: DosSemClear(&lSemRAEmpty); /* Make WriteScreen unblock */
140: }
141: }
142:
143: void ThdDoBreak(void) {
144: /*
145: Try to send break for a second
146: */
147: ComBreak();
148: DosSleep(1000L);
149: ComUnbreak();
150: }
151:
152: int ThdPutChar(char ch) {
153: /*
154: Perhaps we will enter an entire key packet
155: But we can't really block, because we gotta respond.
156: Solution: This time, we time out.
157: */
158: /*
159: Manipulate the typeahead buffer (circular queue)
160: */
161: if (nChars >= BUFSIZE) { /* Block if buffer full */
162: DosSemSet(&lSemFull);
163: /*
164: Timeout possibility; probably want TIMEOUT < 1 second
165: */
166: if (rc = DosSemWait(&lSemFull, TIMEOUT)) return rc;
167: }
168: ThdBufNextLoc(nBufLoc); /* Increments to next location */
169: /*
170: Be really impatient...
171: This protects the queue, but we should never read/write
172: the same queue location. Maybe I'll move it out later.
173: */
174: if (rc = DosSemRequest(&lSemLock, TIMEOUT)) { /* Another quick timeout */
175: ThdBufLastLoc(nBufLoc);
176: return rc;
177: }
178: TypeAhead[nBufLoc] = ch;
179: nChars++;
180: DosSemClear(&lSemEmpty);
181: DosSemClear(&lSemLock);
182: return 0;
183: }
184:
185: void far WritePortThread(void) {
186: /*
187: The routine which writes your WM_CHARS to the port,
188: one at a time, nice and easy. We can even wait all
189: day (and will, at the rate a user types....)
190:
191: The typeahead buffer is protected with semaphores,
192: although a move is probably an atomic operation;
193: also, it should be secure to read the element, without
194: having to protect the buffer.
195: */
196: int MyLoc = -1;
197: char ch;
198:
199: while (fAlive) {
200: if (nChars < 1) { /* Wait if the queue is empty */
201: DosSemSet(&lSemEmpty);
202: DosSemWait(&lSemEmpty, MAXTIMEOUT);
203: } else if (!(rc = DosSemRequest(&lSemLock, MAXTIMEOUT))) {
204: ThdBufNextLoc(MyLoc);
205: ch = TypeAhead[MyLoc]; /* writing to the port is slow, and */
206: nChars--; /* we want to release the semaphore */
207: DosSemClear(&lSemFull);
208: DosSemClear(&lSemLock);
209: if (rc = ComWrite(TypeAhead[MyLoc])) {
210: /* Post the message... */
211: MessageBox(MBE_WRITEPORT, NOUPDATE);
212: }
213: }
214: }
215: DosExit(EXIT_THREAD, 0);
216: }
217:
218: void far ReadPortThread(void) {
219: /*
220: Read from the port, a line at a time
221: The semaphores force private queue access
222: */
223: int iLine = 0;
224:
225: while (fAlive) {
226: if (cliReadAhead < RASIZE) {
227: if (ComRead(&aliReadAhead[iLine]))
228: MessageBox(MBE_COMREAD, (MPARAM) ComError());
229: else if (&aliReadAhead[iLine].cch) {
230: DosSemRequest(&lSemRALock, MAXTIMEOUT);
231: cliReadAhead++;
232: DosSemClear(&lSemRALock);
233: DosSemClear(&lSemRAEmpty);
234: ThdNextRALoc(iLine);
235: }
236: } else {
237: DosSemSet(&lSemRAFull);
238: DosSemWait(&lSemRAFull, TIMEOUT);
239: }
240: }
241: DosExit(EXIT_THREAD, 0);
242: }
243:
244: int ThdPutString(char a[], int n) {
245: int i, rc = 0;
246:
247: for (i = 0; i < n; i++)
248: if (!rc) rc = ThdPutChar(a[i]);
249: return rc;
250: }
251:
252: void Process(Line lCmd, Line lOutput, int far *pi) {
253: /*
254: This routine filters characters from the port, before we
255: display them to the screen.
256:
257: To make this a full blown terminal emulator application,
258: all that's needed is to trap cursor movement sequences
259: here, and then to retrieve the appropriate queue line.
260: */
261: USHORT usTemp;
262: int i;
263:
264: while ((lCmd->szText[*pi] != '\n') && ((*pi) < lCmd->cch)) {
265: switch (lCmd->szText[*pi]) {
266: case '\b':
267: if (lOutput->cch > 0) lOutput->cch--;
268: break;
269: case '\r':
270: case '\0':
271: break;
272: case '\007': /* Ctrl G */
273: WinAlarm(HWND_DESKTOP, WA_NOTE);
274: break;
275: case '\t':
276: if ((usTemp = (((lOutput->cch >> 3) + 1) << 3)) < MAXLINELEN) {
277: for (i = lOutput->cch; i < usTemp; i++)
278: lOutput->szText[i] = ' ';
279: lOutput->cch = usTemp;
280: }
281: break;
282: default:
283: if (fWrap) {
284: if (lOutput->cch >= MAXLINELEN) {
285: lOutput->fComplete = TRUE;
286: lOutput->fDrawn = FALSE;
287: return;
288: } else lOutput->szText[lOutput->cch++] = lCmd->szText[*pi];
289: } else {
290: if (lOutput->cch < MAXLINELEN) /* Straight nowrap */
291: lOutput->szText[lOutput->cch++] = lCmd->szText[*pi];
292: }
293: break;
294: }
295: (*pi)++;
296: }
297: /*
298: Complete line if it doesn't exhaust the command string, and
299: ends with a newline (\n). If so, increment string length, but
300: don't write (potentially) out of the array bounds.
301: */
302: if (lOutput->fComplete =
303: (((*pi) < lCmd->cch) && (lCmd->szText[*pi] == '\n')))
304: (*pi)++;
305:
306: lOutput->fDrawn = FALSE;
307: }
308:
309: void ThdReset(void) {
310: DosSemClear(&lSemOverflow);
311: }
312:
313: void far WriteScreenThread(void) {
314: int iLine = 0;
315: LineInfo liQueueEntry;
316: int iLinePos = 0;
317: BOOL fMore = FALSE;
318:
319: liQueueEntry.cch = 0;
320: while (fAlive) {
321: if ((cliReadAhead > 0) || fMore) {
322: Process(&aliReadAhead[iLine], &liQueueEntry, &iLinePos);
323: /*
324: Add the entry to the queue if it's:
325: 1) A complete line
326: 2) There's nothing more to read
327: */
328: if (liQueueEntry.fComplete || (cliReadAhead <= 1)) {
329: while (!QueInsertLine(&liQueueEntry)) {
330: MessageBox(MBE_QUEUEFULL, UPDATE);
331: DosSemSet(&lSemOverflow);
332: DosSemWait(&lSemOverflow, MAXTIMEOUT);
333: }
334: /*
335: If complete line is read, reset line pointer
336: */
337: if (liQueueEntry.fComplete) {
338: liQueueEntry.cch = 0;
339: /*
340: If done moving, move cursor to the right place.
341: (the beginning of the next line), so that the
342: user knows when they've hit <Enter>.
343: */
344: if (cliReadAhead <= 1) {
345: liQueueEntry.fComplete = liQueueEntry.fDrawn = FALSE;
346: while (!QueInsertLine(&liQueueEntry)) {
347: MessageBox(MBE_QUEUEFULL, UPDATE);
348: DosSemSet(&lSemOverflow);
349: DosSemWait(&lSemOverflow, MAXTIMEOUT);
350: }
351: }
352: }
353: }
354: /*
355: If there's no more to process, bump the read pointer
356: and possibly unblock the Read Port thread
357: */
358: if (!(fMore = (iLinePos < aliReadAhead[iLine].cch))) {
359: ThdNextRALoc(iLine);
360: iLinePos = 0;
361: DosSemRequest(&lSemRALock, MAXTIMEOUT);
362: cliReadAhead--;
363: DosSemClear(&lSemRALock);
364: DosSemClear(&lSemRAFull);
365: }
366: } else {
367: if (fNoUpdate) {
368: fNoUpdate = WinPostMsg(hWndMaster, WM_AVIOUPDATE, NULL, NULL);
369: }
370: DosSemSet(&lSemRAEmpty);
371: DosSemWait(&lSemRAEmpty, TIMEOUT);
372: }
373: }
374: DosExit(EXIT_THREAD, 0);
375: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.