delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/1993/10/31/18:24:26

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)


- Raw text -


  webmaster     delorie software   privacy  
  Copyright 2019   by DJ Delorie     Updated Jul 2019