home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 3 Comm / 03-Comm.zip / COMTALK.ZIP / THREADS.C < prev    next >
C/C++ Source or Header  |  1989-02-08  |  10KB  |  372 lines

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