Path: 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 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 #include #include #define INCL_BASE #define INCL_NOPM #include #include #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); }