| TIP: Click on subject to list as thread! | ANSI |
| echo: | |
|---|---|
| to: | |
| from: | |
| date: | |
| subject: | Timeslices |
TS>
> Can I give timeslices to OS/2 in OS/2 prog? Or how should I
> do this kind of program:
>
> repeat
> if keypressed then
> begin
> c:=readkey;
> {...}
> end else
> begin
> give_timeslice; { Any way to do this in OS/2 prog? }
> end;
> until quit;
TS>
We were talking about the design of the BOXER text editor here in the
FIDONET OS2PROG echo back in October 1994, and Phil Crown asked
roughly the same question. Here is the response that I posted then :
------------------------------------
Conference: Os2prog
To: Phil Crown Date: 10/22/1994
From: YOU Time: 9:38 am
The reasons why polling the keyboard has such demerits are the latency
of the DosSleep() call, and the fact that even when there is no actual
work to do, the thread is using CPU time.
Ignoring the clock and mouse for now, consider the loop with a
blocking KbdCharIn().
| for (;;) {
| blocking_read_keyboard () ;
| process_character () ;
| }
It spends most of its time blocked in the the upper half of the KBD$
device driver. When a keystroke arrives, the lower half of the KBD$
device driver unblocks the thread, making it ready to run. When the
thread is next dispatched, it returns from the upper half of the KBD$
device driver and from the KbdCharIn() call.
In other words, receipt of a character makes the thread ready to run
almost immediately, and thus the character is processed as soon as
possible (subject to thread priority) after it arrives in the keyboard
device driver. When there is no work to do, the thread is blocked,
using no CPU time.
Now consider a non-blocking loop interspersed with DosSleep() calls.
| for (;;) {
| if ( poll_keyboard() ) { // KbdCharIn(... IO_NOWAIT ...)
| process_character() ;
|
| } else if ( poll_mouse() ) {
| process_mouse() ;
|
| } else {
| DosSleep ( 1 ) ; // Yield this time slice
| update_clock() ;
| }
| }
In theory the thread spends most of its time blocked in the internals
of DosSleep(). When a keystroke arrives, the lower half of the KBD$
device driver enqueues the keystroke, but since no thread is blocked
in the upper half, it doesn't do anything more.
So the thread doesn't actually see anything until DosSleep() returns
and it calls KbdCharIn() once more. If the character arrives in the
keyboard device driver just as DosSleep() is being entered, it isn't
processed until DosSleep() is over. What's worse is that DosSleep()
only guarantees a minimum sleep time. So it could be quite a while
before KbdCharIn() is even called to retrieve the character.
In practice, the overhead of calling KbdCharIn() is quite significant.
So the thread spends (relatively) more time in KbdCharIn(). But of
course of that time spent, it only tests the queue once, so this
doesn't imply that there's a larger window of opportunity for a
character to be immediately recognised.
Since polling the mouse has significant overhead too, one iteration
around the loop ends up using quite a lot of processor time. When
there is no actual work to do, this is a Mortal Sin for a program in a
multitasking environment.
Also in practice, the "dead time" when a keystroke remains
unrecognised in the keyboard driver is further added to by the calls
to do mouse and clock processing.
One final thing to note is that the scheduler lowers the priority of
CPU bound processes, and raises the priority of I/O bound processes
(i.e. processes that spend most of their time blocked). The blocking
version of the loop is totally I/O bound, so when a character arrives,
the thread will have a relatively high priority, and will be more
likely to be the next thread dispatched, giving good response to user
input.
On the other hand, the non-blocking version of the loop uses CPU time,
and will be penalised for it by the scheduler by having its priority
proportionately lowered. When the character arrives, it will be less
likely to be the next thread dispatched, making the application less
responsive to user input.
> JdeBP <
___ Maximus/2 2.01wb
------------------------------------
Conference: Os2prog
To: Phil Crown Date: 10/22/1994
From: YOU Time: 9:51 am
PC>
> while( 1 ) {
> rc = KbdCharIn( &ki, 1, 0 );
> DosSleep( 5 ); // give up time slices this drops the CPU usage from
> // %100 to %5 in this example
do_mouse_and_clock_stuff() ;
> }
PC>
Since you asked how else a program would be able to know when a key on
the keyboard is hit, here's my multithreaded improvement on the above
single-threaded design.
Main thread :
Initialise event_queue and start secondary threads ;
for (;;) {
event = event_queue.blocking_read () ;
switch (event) {
case MOUSE : do_mouse(event) ; break ;
case KEYBOARD: do_keyboard(event) ; break ;
}
}
So when there is no work to do this thread spends its time
blocked reading the event queue.
Secondary thread 1 :
for (;;) {
mouse_event = blocking_read_of_mouse () ;
event_queue.post(mouse_event) ;
}
So when there is no work to do this thread spends its time
blocked in the upper half of the mouse device driver.
Secondary thread 2 :
for (;;) {
keyboard_event = blocking_read_of_keyboard () ;
event_queue.post(keyboard_event) ;
}
So when there is no work to do this thread spends its time
blocked in the upper half of the keyboard device driver.
Secondary thread 3 :
for (;;) {
DosSleep(1000) ;
update_clock_display () ;
}
This thread spends all of its idle time blocked within DosSleep() .
Total CPU usage when there is no actual work to do ? Well, since all
threads are blocked, it must be zero.
( Incidentally, Peter Fitzsimmons will tell you that you can also page
tune the program by putting the inner loops of the threads as close
together as possible, reducing the working set of an idle process.
He'll also tell you that the clock thread is a stupid idea when it's
just as easy for the user to start up the supplied clock on the
desktop. )
The only (slight) difficulty for the programmer is the event queue.
This must support blocking reads, and all calls must be thread-safe.
Although there is a message queue IPC facility provided by OS/2, it's
possibly more expensive than rolling your own, since it has to use
shared memory to ensure communication between processes. For multiple
threads within a single process, a global data structure can be used,
and the only problem is synchronisation. Fortunately, this happens to
be the standard producer/consumer problem from most textbooks, and is
easily solved using semaphores.
Do you think that David Hamel is listening ? (-:
> JdeBP <
___ Maximus/2 2.01wb
------------------------------------
> JdeBP <
___
X MegaMail 2.10 #0:
--- Maximus/2 3.00
* Origin: DoNoR/2,Woking UK (0483-725167) (2:440/4)* Origin: DoNoR/2,Woking UK (0483-725167) (2:440/4) * Origin: DoNoR/2,Woking UK (44-1483-725167) (2:440/4) SEEN-BY: 270/101 620/243 711/401 409 410 413 430 808 809 934 955 712/407 515 SEEN-BY: 712/517 628 713/888 800/1 7877/2809 @PATH: 440/4 141/209 270/101 712/515 711/808 809 934 |
|
| SOURCE: echomail via fidonet.ozzmosis.com | |
Email questions or comments to sysop@ipingthereforeiam.com
All parts of this website painstakingly hand-crafted in the U.S.A.!
IPTIA BBS/MUD/Terminal/Game Server List, © 2025 IPTIA Consulting™.