🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.os.os2
Article #1020

Problem with multiple threads ...

#1020
From: Kai-Uwe Rommel
Date: Thu, 04 Jan 1990 17:41
255 lines
7153 bytes
Some time ago I posted my version of MicroEMACS 3.10 to this news group.
Of course it's not the best way to do the input from keyboard and mouse
with busy waiting. Now I tried to use two separate threads which wait
for keyboard and mouse input and write the characters to a global buffer
from which the main thread now reads his input.

For testing, I extracted all code related to this problem to a stand
alone program. The source is appended to this article. I do not have
much experience with multiple threads. Can someone please tell me if
something is wrong ?

The test program works but I have the following problems:

1. In checkmouse() you will find a call to MouReadEventQue() with
   MOUSE_NOWAIT! When I use MOUSE_WAIT instead, I expect the thread to
   sleep inside the call when no mouse input is available. But actually
   THE OTHER (keyboard) thread is blocked and does not accept any input or
   the main thread is blocked and cannot write to the screen. Why ?
   As a workaround I use MOUSE_NOWAIT but only 10 times per second (the
   DosSleep(100L) call !).

2. The KbdGetStatus() call (commented out) blocks the mouse thread when
   the keyboard thread is waiting for input. Why ? I need the shift state
   to create the mouse event characters correctly.

Except these two problems the serialization with the two semaphores
works well. But when I include the code into MicroEMACS other problems
arise which do no occur with the stand alone model (yes, the correct
compiling and linking options and libraries were used):

3. The whole editor blocks until I insert a DosSleepCall(10L) into the
   keyboard thread loop too. When I trace the program with the debugger,
   everything works well even without the DosSleep(10L).

4. When the mouse thread got an event he is blocked before he could
   insert the event into the global buffer. Again, when I trace the threads
   with the debugger, everything works well.

I already tried DosSetPrty(PRTYS_THREAD, PRTYC_IDLETIME, 0, threadid)
calls to reduce the priority of the waiting threads - no change.

What am I doing wrong ?

Chris Adie suggested another solution using a semaphore for the mouse
thread and another for the keyboard thread and a DosMuxSemWait() in the
main thread. But I want the kbd and mouse threads to run independently
and writing their input to the global buffer because this buffer then
serves as a typeahead buffer.

Kai Uwe Rommel
Munich
rommel@lan.informatik.tu-muenchen.dbp.de

------------------------------------------------------------

#include <stdio.h>
#include <process.h>
#include <stdlib.h>


#define INCL_BASE
#define INCL_NOPM
#include <os2.h>
#include <conio.h>

#define KEYBUFSIZE 64
#define THREADSTACK 4096

static int mouseflag = 0;
static HMOU hmouse;
static USHORT mousemask;

static int buttonmask[3] = {MOUSE_BN1_DOWN, MOUSE_BN3_DOWN, MOUSE_BN2_DOWN};

static USHORT in_buf[KEYBUFSIZE], in_next, in_last;
static void *kbd_stack, *mouse_stack;

static ULONG semAccess = 0L, semEmpty = 0L;

void in_init(void)       /* initialize the input buffer */
{
  DosSemRequest(&semAccess, SEM_INDEFINITE_WAIT);

  DosSemSet(&semEmpty);
  in_next = in_last = 0;

  DosSemClear(&semAccess);
}

int in_check(void)      /* is the input buffer non-empty? */
{
  int empty;

  DosSemRequest(&semAccess, SEM_INDEFINITE_WAIT);

  empty = (in_next == in_last);

  DosSemClear(&semAccess);

  return !empty;
}

void in_put(int event)
{
  DosSemRequest(&semAccess, SEM_INDEFINITE_WAIT);

  in_buf[in_last++] = event;
  in_last &= (KEYBUFSIZE - 1);
  DosSemClear(&semEmpty);

  DosSemClear(&semAccess);
}

int in_get(void)    /* get an event from the input buffer */
{
  int event;     /* event to return */

  DosSemWait(&semEmpty, SEM_INDEFINITE_WAIT);

  DosSemRequest(&semAccess, SEM_INDEFINITE_WAIT);

  event = in_buf[in_next++];
  in_next &= (KEYBUFSIZE - 1);

  if (in_next == in_last)
    DosSemSet(&semEmpty);

  DosSemClear(&semAccess);

  return(event);
}

void checkmouse(void *arg)
{
  MOUEVENTINFO mouInfo;
  KBDINFO kbdInfo;
  USHORT wait, old, new, cnt, chr;

  while (1)
  {
    wait = MOU_NOWAIT;
    MouReadEventQue(&mouInfo, &wait, hmouse);

    if ( mouInfo.time != 0L )
    {
      kbdInfo.cb = sizeof(kbdInfo);
      /* KbdGetStatus(&kbdInfo, 0); */

      chr = 'a';
      if ( kbdInfo.fsState & (LEFTSHIFT | RIGHTSHIFT) )
        chr = 'A';
      if ( kbdInfo.fsState & CONTROL )
        chr = 'A' - '@';

      for ( cnt = 0; cnt < 3; cnt++ )
      {
        old = mousemask & buttonmask[cnt];
        new = mouInfo.fs & buttonmask[cnt];

        if ( old != new )
        {
          in_put(0);
          in_put(0x8000);
          in_put(mouInfo.col);
          in_put(mouInfo.row);
          in_put(new ? chr + 2 * cnt : chr + 2 * cnt + 1);
        }
      }

      mousemask = mouInfo.fs;
    }

    DosSleep(100L);
  }
}

void checkkbd(void *arg)
{
  KBDKEYINFO keyInfo;
  USHORT nextc;

  while (1)
  {
    KbdCharIn(&keyInfo, IO_WAIT, 0);

    if (keyInfo.chChar == 0 || keyInfo.chChar == 0xE0)
    {
      nextc = keyInfo.chScan;
      in_put(0);
      in_put(nextc >> 8);         /* prefix byte */
      in_put(nextc & 255);        /* event code byte */
    }
    else
      in_put(keyInfo.chChar);

    /* DosSleep(10L); */
  }
}

int get(void)
{
  NOPTRRECT rect;
  USHORT res;

  if ( in_check() )
    return in_get();

  if ( mouseflag )
    MouDrawPtr(hmouse);

  res = in_get();

  if ( mouseflag )
  {
    rect.row = rect.col = 0;
    rect.cRow = 24;
    rect.cCol = 79;
    MouRemovePtr(&rect, hmouse);
  }

  return res;
}

void main(void)
{
  USHORT mask, key;

  in_init();

  if ( (kbd_stack = malloc(THREADSTACK)) == NULL ||
       (mouse_stack = malloc(THREADSTACK)) == NULL )
  {
    puts("Cannot allocate thread stacks.");
    exit(-1);
  }

  MouOpen(NULL, &hmouse);
  mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN;
  MouSetEventMask(&mask, hmouse);
  mask = 0;
  MouSetDevStatus(&mask, hmouse);

  _beginthread(checkmouse, mouse_stack, THREADSTACK, NULL);
  _beginthread(checkkbd, kbd_stack, THREADSTACK, NULL);

  do
  {
    key = get();
    printf("Empty=%08lx Access=%08lx in_next=%d in_last=%d %04x\n",
           semEmpty, semAccess, in_next, in_last, key);
  }
  while (key != 27);

  exit(0);
}
Technical Details
Message-ID: <1028@tuminfo1.lan.informatik.tu-muenchen.dbp.de>
Path: arc1-ca.pugleaf.net!utzoo!utgpu!jarvis.csri.toronto.edu!clyde.concordia.ca!uunet!aplcen!uakari.primate.wisc.edu!zaphod.mps.ohio-state.edu!mips!apple!snorkelwacker!bloom-beacon!eru!luth!sunic!mcsun!unido!fauern!tumuc!lan!rommel

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!jarvis.csri.toronto.edu!clyde.concordia.ca!uunet!aplcen!uakari.primate.wisc.edu!zaphod.mps.ohio-state.edu!mips!apple!snorkelwacker!bloom-beacon!eru!luth!sunic!mcsun!unido!fauern!tumuc!lan!rommel
From: rommel@lan.informatik.tu-muenchen.dbp.de (Kai-Uwe Rommel)
Newsgroups: comp.os.os2
Subject: Problem with multiple threads ...
Message-ID: <1028@tuminfo1.lan.informatik.tu-muenchen.dbp.de>
Date: 4 Jan 90 17:41:48 GMT
Sender: news@lan.informatik.tu-muenchen.dbp.de
Reply-To: rommel@lan.informatik.tu-muenchen.dbp.de (Kai-Uwe Rommel)
Distribution: comp.os.os2
Organization: Inst. fuer Informatik, TU Muenchen, W. Germany
Lines: 255