Data Structures | Modules | Defines | Enumerations

# 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.