Main Page | Class Hierarchy | Class List | File List | Class Members

MTasker Documentation

0.4

Simple system for implementing cooperative multitasking of functions, with support for waiting on events which can return values.

Copyright and License

MTasker is (c) 2004 by bert hubert. It is licensed to you under the terms of the GPL version 2.

High level overview

MTasker is designed to support very simple cooperative multitasking to facilitate writing code that would ordinarily require a statemachine, for which the author does not consider himself smart enough.

This class does not perform any magic it only makes calls to makecontext() and swapcontext(). Getting the details right however is complicated and MTasker does that for you.

If preemptive multitasking or more advanced concepts such as semaphores, locks or mutexes are required, the use of POSIX threads is advised.

MTasker is designed to offer the performance of statemachines while maintaining simple thread semantics. It is not a replacement for a full threading system.

Compatability

MTasker is only guaranteed to work on Linux with glibc 2.2.5 and higher. It does not work on FreeBSD 4.x and notably, not on Red Hat 6.0. It may work on Solaris, please test.

Concepts

There are two important concepts, the 'kernel' and the 'thread'. Each thread starts out as a function, which is passed to MTasker::makeThread(), together with a possible argument. The kernel is written by the developer, but can be quite short.

This function is now free to do whatever it wants, but realise that MTasker implements cooperative multitasking, which means that the coder has the responsiblilty of not taking the CPU overly long. Other threads can only get the CPU if MTasker::yield() is called or if a thread sleeps to wait for an event, using the MTasker::waitEvent() method.

The Kernel

The Kernel consists of functions that do housekeeping, but also of code that the client coder can call to report events. A minimal kernel loop looks like this:
    for(;;) {
       MT.schedule();
       if(MT.noProcesses())  // exit if no processes are left
          break;
    }

The kernel typically starts from the main() function of your program. New threads are also created from the kernel. This can also happen before entering the main loop. To start a thread, the method MTasker::makeThread is provided.

Events

By default, Events are recognized by an int and their value is also an int. This can be overriden by specifying the EventKey and EventVal template parameters.

An event can be a keypress, but also a UDP packet, or a bit of data from a TCP socket. The sample code provided works with keypresses, but that is just a not very useful example.

A thread can also wait for an event only for a limited time, and receive a timeout of that event did not occur within the specified timeframe.

A simple menu system

MTasker<> MT;

void menuHandler(void *p)
{
  int num=(int)p;
  cout<<"Key handler for key "<<num<<" launched"<<endl;
 
  MT.waitEvent(num);
  cout<<"Key "<<num<<" was pressed!"<<endl;
}


int main()
{
  char line[10];

  for(int i=0;i<10;++i) 
    MT.makeThread(menuHandler,(void *)i);
  
  for(;;) {
    while(MT.schedule()); // do everything we can do
    if(MT.noProcesses())  // exit if no processes are left
      break;

    if(!fgets(line,sizeof(line),stdin))
      break;
    
    MT.sendEvent(*line-'0');
  }
}

Canonical multitasking example

This implements the canonical multitasking example, alternately printing an 'A' and a 'B'. The Linux kernel started this way too.
void printer(void *p)
{
  char c=(char)p;
  for(;;) {
    cout<<c<<endl;
    MT.yield();
  }

}

int main()
{
  MT.makeThread(printer,(void*)'a');
  MT.makeThread(printer,(void*)'b');

  for(;;) {
    while(MT.schedule()); // do everything we can do
    if(MT.noProcesses())  // exit if no processes are left
      break;
  }
}

Generated on Sun Feb 8 12:07:52 2004 for MTasker by doxygen 1.3.5