Article View: comp.os.os2
Article #1028Re: Problem with multiple threads ...
From: kulokari
Date: Sat, 06 Jan 1990 20:43
Date: Sat, 06 Jan 1990 20:43
422 lines
10930 bytes
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!kulokariReferences:
<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