home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / os2sdk / os2sdk11 / tk3 / comtalk / threads.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-02-20  |  9.6 KB  |  376 lines

  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. }
  376.