🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.os.os2
Article #1028

Re: Problem with multiple threads ...

#1028
From: kulokari
Date: Sat, 06 Jan 1990 20:43
422 lines
10930 bytes
In article <1028@tuminfo1.lan.informatik.tu-muenchen.dbp.de>, rommel@lan.informatik.tu-muenchen.dbp.de (Kai-Uwe Rommel) writes:
> 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 ?

A corrected version of your program is appended. My patches can
be identified by the defined variable HRK (my initials).

Some notes:
                                
1) When I first tried to compile your program, it did not
compile. You use some symbols (like MOU_WAIT) which are not
defined in the standard header files of the IBM toolkit v. 1.1.
Which toolkit do you use?

2) When the program did compile (after I had checked from manuals
what those undefined symbols probably did mean), it did not link.
_beginthread was not found in the standard C library. Closer
inspection revealed, that you have been using the multithreaded
version of the MSC 5.1 library. This is usually (and in this
case) quite unnecessary, and to be avoided of at any cost.

3) Then I found that you have installed your C header files in a
nonstandard fashion. The header files of the multithreaded
library are supposed to be in the MT subdirectory of the default
INCLUDE directory, and the #include statement for
process.h is usually

#include <mt\process.h>

4) A make file with the C flags and libraries documented would
also have been helpful.

FLAME ON:

When you post your code here for others to scrutiny, please
make sure that all deviations from standard assumptions are
documented.

Thank you.

FLAME OFF.

5) I replaced the call to _beginthread() with DosCreateThread()
and declared far all that should be far. (The thread functions at
least.) The corrected code is not dependent of memory model (I
used small), and it compiles, links and runs in standard
environment.

6) Your code was mostly okay, although I did quite a lot of
cleanup here and there. The basic fault, and probably the cause
for all your sorrows, was quite simple: Mou and Kbd subsystems
are not re-entrant. Only one thread/process at a time can have a
call pending. If other processes need to do something to keyboard
or mouse (note: keyboard != Kbd, mouse != Mou), they have to
use device control calls. I have substituted those where needed.

7) If you need to de-activate the keyboard/mouse handler
temporarily, as you will if you are doing uEmacs, then you have
additional trouble as documented in my note posted here a couple
of days back.

The non-re-entrancy of the Mou and Kbd subsystems is not well
documented, if at all. It can be read from between the lines,
however, when you remember, that all the processes in a screen
group have ONE keyboard and ONE mouse, and only ONE process at a
time has the input focus (not necessarily the same process for
keyboard and mouse). That process can use the Mou a/or Kbd calls.
Virtual keyboards may complicate the picture, but that is a
subject with which I am not familiar enough yet.

---

This is the day and time of the week when all the industrious Finns
take a break and go to sauna. So will I. Hope the rest of the world
will find something to amuse itself with in the meantime.

Hannu Kulokari
CC, U of Helsinki

kulokari@cc.helsinki.fi (Internet)
kulokari@finuh (Bitnet)

------------------------------------------------------------------------
#define HRK 1

#include <stdio.h>
#if HRK==0
#include <process.h>
#endif
#include <stdlib.h>


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

#define KEYBUFSIZE 64
#define THREADSTACK 4096

#if HRK

/* these constants are NOT defined in the IBM toolkit! */

#define MOU_WAIT 1
#define MOU_NOWAIT 0
#define MOUSE_BN1_DOWN 4
#define MOUSE_BN2_DOWN 16
#define MOUSE_BN3_DOWN 64

#define LEFTSHIFT 2
#define RIGHTSHIFT 1
#define CONTROL 4

#endif

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;
#if HRK
/* 
   These pointers must have type, if we are to increment them in
   the call of DosCreateThread 
*/   

static char *kbd_stack, *mouse_stack;
#else
static void *kbd_stack, *mouse_stack;
#endif

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

void in_init(void)       /* initialize the input buffer */
{
#if HRK==0
  /* not needed here */
  DosSemRequest(&semAccess, SEM_INDEFINITE_WAIT);
  DosSemSet(&semEmpty);
#endif  

  in_next = in_last = 0;

  DosSemClear(&semAccess);
}

#if HRK==0    /* see get() */
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;
}
#endif

void in_put(int event)
{
#if HRK==0    
  DosSemRequest(&semAccess, SEM_INDEFINITE_WAIT);
#endif  

  in_buf[in_last++] = event;
  in_last &= (KEYBUFSIZE - 1);
#if HRK==0  
  DosSemClear(&semEmpty);
  DosSemClear(&semAccess);
#endif  
}

#if HRK==0     /* see get() */   

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);
}
#endif


#if HRK
/* must be explicitly declared far when using small model */
void far checkmouse()
#else
void checkmouse(void *arg)
#endif
{
  MOUEVENTINFO mouInfo;
#if HRK
  struct {
    USHORT fsState;
    BYTE fNLS;
  } kbdInfo;
#else  
  KBDINFO kbdInfo;
#endif  
  USHORT wait, old, new, cnt, chr;
    

  while (1)
  {
#if HRK
    wait = MOU_WAIT;
#else
    wait = MOU_NOWAIT;
#endif    
    MouReadEventQue(&mouInfo, &wait, hmouse);

    if ( mouInfo.time != 0L )
    {
#if HRK
      /* Get keyboard shift status. 
         must use device control call, because Kbd subsystem
         is not re-entrant. Only one thread can have a call
         pending. */  
      DosDevIOCtl(&kbdInfo,0L,0x0073,0x0004,0);
#else        
      kbdInfo.cb = sizeof(kbdInfo);
      /* KbdGetStatus(&kbdInfo, 0); */
#endif      

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

#if HRK
      /* You want this to be saved in unbroken sequence */  

      DosSemRequest(&semAccess,-1L);
#endif      

      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);
        }
      }
#if HRK      
      DosSemClear(&semAccess);
      if (in_next != in_last)DosSemClear(&semEmpty);
#endif      

      mousemask = mouInfo.fs;
    }

#if HRK==0
    DosSleep(100L);
#endif    
  }
}

#if HRK
/* must be explicitly declared far when using small model */
void far checkkbd()
#else
void checkkbd(void *arg)
#endif
{
  KBDKEYINFO keyInfo;
  USHORT nextc;

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

#if HRK
    DosSemRequest(&semAccess,-1L);
#endif    

    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);
#if HRK      
    DosSemClear(&semAccess);
    DosSemClear(&semEmpty);
#endif    

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


int get(void)
{
  USHORT res;
  NOPTRRECT rect;
#if HRK
  if (in_next == in_last) {
    /* queue empty. */
    /* Show mouse while waiting. Must use device control call,
       because Mou subsystem is not re-entrant (only one thread
       can have a call pending).
       MouseDrawPtr() would hang. */

    if (mouseflag) DosDevIOCtl(0L,0L,0x0057,0x0007,hmouse);
    DosSemSetWait(&semEmpty,-1L);
  }  
  res = in_buf[in_next++];
  in_next &= (KEYBUFSIZE - 1);
#else  

  if ( in_check() )
    return in_get();

  if ( mouseflag )
    MouDrawPtr(hmouse);

  res = in_get();
#endif

  if ( mouseflag )
  {
    rect.row = rect.col = 0;
    rect.cRow = 24;
    rect.cCol = 79;
#if HRK
    /* must use device control call */    
    DosDevIOCtl(0L,&rect,0x0058,0x0007,hmouse);
#else    
    MouRemovePtr(&rect, hmouse);
#endif    
  }
  return res;
}

#if HRK
TID kbdid, mouid; 
#endif

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);
  }

#if HRK
  mouseflag = 0 == MouOpen(NULL,&hmouse);
#else  
  MouOpen(NULL, &hmouse);
#endif
#if HRK
  if (mouseflag) {
#endif    
  mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN;
  MouSetEventMask(&mask, hmouse);
  mask = 0;
  MouSetDevStatus(&mask, hmouse);

#if HRK
  MouFlushQue(hmouse);  
  }
  /* _beginthread is not in the *standard* MSC 5.1 library! */  

  if (mouseflag)
    DosCreateThread(checkmouse,&mouid,mouse_stack+THREADSTACK);
  DosCreateThread(checkkbd,&kbdid,kbd_stack+THREADSTACK);
#else  
  _beginthread(checkmouse, mouse_stack, THREADSTACK, NULL);
  _beginthread(checkkbd, kbd_stack, THREADSTACK, NULL);
#endif  

  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: <1702.25a6586a@cc.helsinki.fi>
Path: arc1-ca.pugleaf.net!utzoo!utgpu!jarvis.csri.toronto.edu!rutgers!tut.cis.ohio-state.edu!snorkelwacker!bloom-beacon!eru!luth!sunic!tut!hydra!hylka!kulokari
References: <1028@tuminfo1.lan.informatik.tu-muenchen.dbp.de>

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!jarvis.csri.toronto.edu!rutgers!tut.cis.ohio-state.edu!snorkelwacker!bloom-beacon!eru!luth!sunic!tut!hydra!hylka!kulokari
From: kulokari@cc.helsinki.fi
Newsgroups: comp.os.os2
Subject: Re: Problem with multiple threads ...
Message-ID: <1702.25a6586a@cc.helsinki.fi>
Date: 6 Jan 90 20:43:21 GMT
References: <1028@tuminfo1.lan.informatik.tu-muenchen.dbp.de>
Distribution: comp.os.os2
Organization: University of Helsinki
Lines: 422