Path: 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 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 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 #if HRK==0 #include #endif #include #define INCL_BASE #define INCL_NOPM #include #include #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); }