Cooperative scheduler
[Process schedulers (kernels)]

A simple co-operative mutithreading kernel. More...

Data Structures

struct  BZ_MESG
 Message packet structure. More...
struct  BZ_INIT
 Kernel initialisation structure. More...

Modules

 Initialisation functions
 

Before using, you must initialise the kernel.


 Process management functions
 Event (signal) functions
 

Event (signal) handling.


 Mutual exclusivity lock functions
 

Locks are objects that can be used to guarantee exclusive access to some resource.


 Counting semaphore
 

Semaphores are objects that can be used to manage a finite pool os resources.


 Message port functions
 

The kernel provides a mechanism for sending messages from a thread or interrupt routine to some other thread.


 Timer functions
 

The kernel contains a simple timer subsystem.


 Interrupt control
 

Interrupt control.


Defines

#define BZ_PID_BCAST   255
 Broadcast PID.

Enumerations

enum  { BZ_EVENT_ALWAYS = 0x80000000, BZ_EVENT_LOCK = 0x40000000, BZ_EVENT_SEMA = 0x20000000, BZ_EVENT_MAX = 29 }
 

Pre-defined signals (events).

More...
enum  {
  BZ_ILLEGAL_PID = -1, BZ_IN_USE = -2, BZ_NOT_OWNER = -3, BZ_ILLEGAL_PORT = -4,
  BZ_SIGNAL = -5, BZ_NOT_OPEN = -6, BZ_OUT_OF_MESG = -7
}
 

Error codes.

More...

Detailed Description

A simple co-operative mutithreading kernel.

The library package contains a pre-emptive kernel, see Pre-emptive, soft real-time scheduler. However, that kernel is fairly large. In addition, in pre-emptive systems a lot of attention as well as code should be spent in guaranteeing resource integrity and coherence. When building simpler systems it would often be good to have most of the advantages of a multi-threading kernel available, but in a small footprint and preferably without the hassle associated with pre-emption.

The co-operative kernel is a possible solution. Cooperative mutitasking means that once a process (or thread, in our case it's the same) gets the processor, no other process will run until the current thread voluntarily gives up control. It means that once you started working on a shared object, you can finish working on it as long as you do not call a kernel function that would cause a scheduler run. That eliminates a significant amount of resource locking. Furthermore, the kernel's scheduler is much simpler since context switches can not occur at any time; only when the running process calls specific kernel functions. Of course, it comes at a price. When an external event happens, the process responsible to react to it will not be woken up until the current thread gives up the processor. If a thread never calls a function that would suspend it, then it can hog the processor indefinitely, even if more important processes are waiting for the CPU.

Since this library is intended to be used in embedded systems where you write every thread running on the processor, one can assume that you know what you are doing and you will not create rogue threads to kill the system. If you can live with the lack of pre-emption and all the associated process latency issues, then you can use the co-operative kernel. It is fairly small (it is suitable even for systems with only 16K of FLASH or even less), has little overhead and low interrupt latency.

Each thread has its own stack that you have to provide. Threads are identified by small integers. In fact, these numbers are just indices into the process table, for which you must also provide the RAM but must not touch otherwise. You can choose between round-robin or priority based scheduling. Whenever the current thread relinguishes the processor, the kernel needs to select the next thread to run. It is done by going through the process table and start the first thread that is ready to run. If you choose round-robin scheduling policy, then the kernel will scan the process table starting at the process with the process identifier (PID, i.e. the index to the process table) one less than the process that gave up the processor. It will then continue to go down to 0, wrapping around to the highest PID and counting down again. If you selected priority scheduling, then the scan will always start at the highest PID, thus the kernel will find the highest runnable PID. If the kernel finds no runnable process at all, that is, the system is idle, then it will call a function that you have to provide. In that function you can stop the processor to conserve power, assuming that you have hardware support to wake up when an external event (i.e. an interrupt) needs servicing.

In order to keep the code small, the kernel does very little error checking. It assumes that you know what you're doing. Please read the warnings in the documentation in particular about calling kernel functions from interrupt routines.


Enumeration Type Documentation

anonymous enum

Pre-defined signals (events).

Enumerator:
BZ_EVENT_ALWAYS 

Event that is always pending.

BZ_EVENT_LOCK 

Event used internally by the kernel.

BZ_EVENT_SEMA 

Event used internally by the kernel.

BZ_EVENT_MAX 

Number of available events.

anonymous enum

Error codes.

Enumerator:
BZ_ILLEGAL_PID 

The specified PID is not valid.

BZ_IN_USE 

The resource is in use.

BZ_NOT_OWNER 

The caller does not own the resource.

BZ_ILLEGAL_PORT 

The port specified is not valid.

BZ_SIGNAL 

The specified signal is not valid.

BZ_NOT_OPEN 

The port is not open.

BZ_OUT_OF_MESG 

The kernel is out of message buffers.

Generated on Fri Aug 13 12:02:25 2010 by  doxygen 1.6.3