kulokari@cc.helsinki.fi (01/04/90)
In article <1020@tuminfo1.lan.informatik.tu-muenchen.dbp.de>, rommel@lan.informatik.tu-muenchen.dbp.de (Kai-Uwe Rommel) writes: > I know about the busy loop problem. But I already tried to use to > separate threads for keyboard and mouse input. I was surprised to see > that the MouReadEventQueue() call apears to use busy wait too when the > MOUSE_WAIT mode is used. When my mouse thread waited for an event, my > other thread waiting for keyboard input was blocked !?! Any hints ? > Currently I use the MOUSE_NOWAIT mode and make some DosSleep() calls > between the MouReadEventQueue() calls. > Mouse and keyboard handling and the use of threads therein, with special reference to uEmacs, does seem to interest many of us. I have had no problems in getting the system to work. Stopping it is a lot harder, as will be demonstrated below. This message is a bit lengthy, sorry. The problem is, how to use OS/2 threads to read keyboard and mouse input and merge them into one message queue (fifo), if possible without busy loop polling. This much for the background. The basic solution, written in somewhat stylized and abridged C, is the following: -------------- int stop, threads; unsigned long something; kbd_thread() { threads++; while (!stop) { /* read a key, translate to character string and put it in FIFO */ ... /* indicate there is something in FIFO */ DosSemClear(&something); } threads--; DosExit(0,0); } mou_thread() { /* analogous to kbd_thread */ } get_char() { if (somethinginfifo()) return (getfromfifo()); DosSemSetWait(&something,-1L); /* wait until there is something again... */ return (getfromfifo()); } main() { char c; ... stop = 0; threads=0; /* start threads */ ... /* main loop */ while (!stop) c = get_char(); ... /* something */ ... } /* wait for threads to kill themselves */ while (threads); ... } ----------------- This is simple and beautiful, and works well until you try to exit. Because both threads are always waiting for the "next" keyin/mouse event, you must both press a key and click the mouse before the threads get the change to notice that they are expected to die. Of course, if you just brutally exit the program, the threads will be gone too, but in many cases this is not what you want. For example, you must be able to deactivate keyboard and mouse handlers when you spawn subprocesses, and restore them afterwards. So we must eliminate the read-ahead. Let us introduce another semaphore, 'proceed', and modify the code: --------------- int stop, threads; unsigned long something, proceed; kbd_thread() { threads++; while (1) { DosSemWait(&proceed,-1L); /* wait for permission to continue */ if (stop) { threads--; DosExit(0,0); } /* read a key, translate to character string and put it in FIFO */ ... /* indicate there is something in FIFO */ DosSemClear(&something); DosSemSet(&proceed); } } mou_thread() { /* analogous to kbd_thread */ } get_char() { if (somethinginfifo()) return (getfromfifo()); DosSemSet(&something); /* indicate fifo is empty */ DosSemClear(&proceed); /* allow threads to continue */ DosSemWait(&something,-1L); /* wait until there is something again ... */ return (getfromfifo()); } main() { ... stop = 0; threads=0; DosSemClear(&proceed); /* start threads */ ... /* main loop */ while (!stop) ... /* do something */ ... } DosSemClear(&proceed); /* wait for threads to kill themselves */ while (threads); ... } --------------------- But this is still not good enough. If the quit command comes from keyboard, you must still click the mouse, and vice versa. I know no *good* solution. What you need is the ability to kill threads from the outside, and OS/2 does not allow that as far as I know. DosSuspendThread is useless here. Another passable solution would be to have a timeout in MouReadEventQue() and KbdCharIn(). No luck there, either. What you *can* do is to simulate timeouts. This brings back polling, tempered with judicious use of DosSleep(). If you know that the quit command always comes from keyboard (as in uEmacs), then you need to poll only the mouse. This is fortunate, because response time is not so critical with mouse, and because mouse has its own event queue, events do not go unnoticed while we sleep, so we can sleep longer. Here is the "final" version of mou_thread(): #define AWHILE 333L /* third of a second */ mou_thread() { int nEvents; threads++; while (1) { DosSemWait(&proceed,-1L); /* wait for permission to continue */ if (stop) { threads--; DosExit(0,0); } MouGetNumQueEl(...); /* set nEvents to number of events in mouse queue */ if (nEvents==0) { DosSleep(AWHILE); continue; /* restart loop */ } /* translate all mouse events in queue to character strings, store them in FIFO */ ... /* indicate there is something in FIFO */ DosSemClear(&something); DosSemSet(&proceed); } threads--; DosExit(0,0); } Although this works well, THIS IS NOT A GOOD SOLUTION! OS/2 should provide a combined keyboard / mouse event queue for text-mode applications, too. PM has it, of course. And there should be a way to kill threads, as there is a way to kill processes (sessions). Perhaps in a future release... Or perhaps somebody knows how to do it better with the present tools? --- Hannu Kulokari CC, U of Helsinki kulokari@cc.helsinki.fi
ballard@cheddar.cc.ubc.ca (Alan Ballard) (01/05/90)
In article <1688.25a364a4@cc.helsinki.fi> kulokari@cc.helsinki.fi writes: > ... >This is simple and beautiful, and works well until you try to exit. Because >both threads are always waiting for the "next" keyin/mouse event, you must both >press a key and click the mouse before the threads get the change to notice >that they are expected to die. Of course, if you just brutally exit the >program, the threads will be gone too, but in many cases this is not what you >want. You might try closing the mouse/keyboard handles from another thread, which *may* have the effect of causing the waits in the key/mouse threads to abort. That works in a similar situation for device monitors. At least, it works with OS/2 1.1; I've had some preliminary indication maybe it doesn't always work with 1.2. An alternative, of course, is to spawn a separate process to contain the threads that wait on mouse and keyboard, and to have them communicate back to the main thread via IPC or shared memory. Then you can just kill this process and restart it when you want to wait again. >... And there should be a way to >kill threads, as there is a way to kill processes (sessions). Perhaps >in a future release... I agree. The lack of anyway to kill a thread or abort a thread's wait is one of the more glaring omissions in the OS/2 API. Alan Ballard | Internet: Alan_Ballard@mtsg.ubc.ca University Computing Services | Bitnet: USERAB1@UBCMTSG University of British Columbia | Phone: 604-228-3074 Vancouver B.C. Canada V6R 1W5 | Fax: 604-228-5116
kulokari@cc.helsinki.fi (01/06/90)
In article <6155@ubc-cs.UUCP>, ballard@cheddar.cc.ubc.ca (Alan Ballard) writes: > You might try closing the mouse/keyboard handles from another thread, which > *may* have the effect of causing the waits in the key/mouse threads to abort. > That works in a similar situation for device monitors. At least, it works > with OS/2 1.1; I've had some preliminary indication maybe it doesn't always > work with 1.2. I tried. Does not work. > > An alternative, of course, is to spawn a separate process to contain the > threads that wait on mouse and keyboard, and to have them communicate back > to the main thread via IPC or shared memory. Then you can just kill this > process and restart it when you want to wait again. I thought that. Somehow I feel it is overkill for such a simple and basic task. Separate exe file and all that. Hannu Kulokari CC, U of Helsinki kulokari@cc.helsinki.fi (Internet) kulokari@finuh (Bitnet)