From: alane AT wozzle DOT linet DOT org Subject: Re: General questions.... To: imageek!innovus.com!morteza (Morteza Ansari) Date: Sun, 31 Oct 1993 14:47:31 -0500 (EST) Cc: djgpp AT sun DOT soe DOT clarkson DOT edu > Is there any document for the task switcher (aetsk101.zip) > somewhere? I realy like to use it, but don't know C++ and can't > figure out what the code is doing. Any help in this one would be > *greatly* appreciated. Hi, 'ae' here. No, there really isn't a document for that beast. I can give you the basic idea, though. I've been thinking about rewriting some parts of that thing (I don't like the way the CPipes work, but I don't know how to do it any better ... yet). If I do, I'll probably produce a manual of sorts. I'm leaning to TeX format 'cause it's probably the most portable; if I don't use TeX, it'll be Word for Wastebaskets. In the meantime, here's the basics of it (I'm not going to try and explain C++ - that is left as a very worthwhile exercise for the reader - but I will explain the practical use of the library): First, everything is based on semaphores. If you don't know what a semaphore is, well, .... it's an access control device. Basically, it is a counter. It usually starts at 0 or 1. Let's look at something simple: I have a printer device, and two tasks in my current process can print. Now, we can't have them both writing indiscriminately or else, as they say, hijinks ensue. So, a semaphore is used to control who "owns" the printer. Now, C++ lets me overload operators, so I might have something like: Sema lpsema(1); // printer semaphore, init to 1 (printer is available) void print1() { lpsema--; // decrement sema, waiting for it to become > 0 // if it isn't already (it would be < 1 if another // task has already taken the printer) // can also say: lpsema.wait(); PrintMyStuff(); lpsema++; // I'm done, so increment it again (this will wake // up anybody else who has done a wait) // can also say: lpsema.signal(); } Now let's say I wanted to wait for the printer for a maximum of five minutes and then give up if I can't have it: void print2() { if (!lpsema.wait(5l * 60l * 1000l)) { TellUser("Somebody's hogging the printer! Sorry!"); return; } PrintMyStuff(); lpsema.signal(); // for consistency's sake, I use the call // sema.signal() when I previously did a // sema.wait(), and I use sema++ when I // previously did a sema--. } Now we get the idea of how to synchronize things between tasks. So, what's a task? It's an entity that executes until it blocks waiting on something (that something is ALWAYS a semaphore, even though it may not look like it). A task has its own stack. It can have its own variables, by defining them in the derived task class. There is no limit to how many instances of a given task may be running (other than core, of course). A task can just give up control, too, by calling TaskSuspend(). This is useful for a low-priority background task. A task does not ever get preempted; it must voluntarily relenquish the CPU, either by waiting on something or because it is feeling altruistic. Let's look at a simple example, using the provided task to read the keyboard. #include "task.h" #include "keybdtsk.h" class Echo: public Task { public: Echo() : Task("Echo") { } void TaskMain(); }; // this defines a task called Echo; each task is derived from class // Task and must define an member called TaskMain() - that's where // execution starts // define the main function for the Echo task void Echo::TaskMain() { char c; for (;;) { KbdPipe.recv(c); // get a char if (c == 0x1b) { // escape kills us scheduler(0); // stop the world } putchar(c); // echo to stdout } } // Now declare vars, and crank it up Echo echo; int main(void) { scheduler(); // start up the world // if you type ESC, then the Echo task will terminate // the task scheduler and you'll end up here. return 0; } // end of simple example Now to see how this works, look at keybdtsk.cpp (or .cc, I guess, for GNU). The keyboard task polls the keyboard, putting characers on a pipe called KbdPipe. The Echo task reads from the pipe, which blocks until a character is available, then it writes it out. Note that because this is C++, a lot of housekeeping is hidden; for example, I only had to define the variable 'echo' in order to create the echo task. There is no reason it can't be done in C, I just chose not to. -- J. Alan Eldridge (alane AT wozzle DOT linet DOT org)