# KIO's Thread Dispatcher - Documentation

## Threads

### Overview

While a classical application has only one thread, one line of program execution, with a thread dispatcher you get a multitude of threads which all seem to execute in parallel. They seem, but they aren't, because you have only one CPU in your computer. [And even if you have more: this library currently does not exploit them.]

The advantage of having threads is, that a handler for a certain problem can stay in it's context as opposed to solving the same problem with interrupts or regular calls of a dedicated procedure, which must reload and save it's state every time it is called. A frequently used idea is to assign a thread to a window, so everything going on in this window is managed within one thread, not disturbed by the action in other windows.

You create a thread with Split(), run it a while hopefully calling Disp() frequently and then it terminates, either because the procedured called by Split() returns or by calling Termi(). Meanwhile it can shedule itself for interrupts or shedule itself for system time, request and lock semaphors and do it's work.

### Class Twsp

    class Twsp
    {
            size_t      StackFree( );

            long        State( );
            ulong       Prio( );
            long        Timeout( );
            const char* Name( );
            long&       Ptime( );

            void        Termi( );
            void        Suspend( );
            void        Resume( );
            void        Wait ( long duration );
            void        Shedule ( long systemtime );
            void        ChangePrio ( ulong newprio );
    };

    const   size_t      minStackFree;

#### long Twsp::State()

Returns the state of this thread. This is either running, suspended|[:irptshed:semashed] or timeshed|[:irptshed:semashed]. These are constants enumerated in disp.h.

running | This thread is willing and able to run. The Tid of the really currently serviced thread is returned by MyTid(). All other threads are only enqueued to get process time.
suspended | The thread is suspended (sleeping).
suspended|irptshed | The thread is sheduled for an interrupt without timeout.
suspended|semashed | The thread is waiting for a semaphor without timeout.
timeshed | The thread is currently suspended but sheduled to resume some time later.
timeshed|irptshed | The thread is sheduled for an interrupt with timeout.
timeshed|semashed | The thread is waiting for a semaphor with timeout.

#### ulong Twsp::Prio()

Query the a thread's priority. You get the logical priority of a thread which may be different from the currently used one if the thread is boosted by a semaphor. This happens, if a low-priority thread blocks a semaphor while a high-priority thread requests it.

All threads have a priority. This does not define how much cpu time they are granted but whether they are granted cpu time at all, because this dispatcher is purely priority driven. This ensures that you never waste time in idle waiting loops. Whenever a thread has time to spend, it must release the cpu by calling Wait() or whatever is more appropriate. The most important task, serviced by the highest priority thread, which is willing and able to run, is executed. If you choose highest priority for a thread which eats up all cpu time you have made a cardinal design error in your application.

#### Choosing the proper thread priority

When creating a new thread with Split() or by calling ChangePrio() you assign a thread a certain importance, it's priority for execution.

Choosing the right priority for all your threads is essential. If a longer running job was given higher priority than a short shot, the short shot would be delayed without need.

e.g. if you have a job which will run 1 hour and another which only takes one second, it would be idiotic to give the one-second-job a lower priority than the long-run: you would have to wait one hour for both jobs to complete.

In general it is best to give jobs which take longer time to complete lower priorities. In fact, this seems to be the major criterium for sorting thread priorities: how long will they run before they yield their result?

Because different developers could adjust their priority base differently, this thread dispatcher defines the following rule for converting duration to priority:

The suggested priority for a thread is the estimated run time until they finish or the allowed maximum latency for handling an event expressed in milli seconds [msec].

e.g.

- a command line shell should have approx. priority 1000/20, because the echo for pressed keys should be visible after max. 1/20 second.
- a compiler should have approx. priority 5*1000, because you expect it to run for a few seconds.

The highest possible priority is 0, which is reserved for serving absulutely low-level system interrupts.

The lowest possible priority is 0xFFFFFFFFu. This corresponds to approx. 7 weeks. In general you'll probably never need any priority lower than 1000*60*60*24 which is 1 day.

You can use the handly constants mssec, msmin, mshour and msday which are meant for expressing durations for various dispatcher functions to define priorities too.

#### What to do if a fast thread needs to perform a longish job?

A typical example for this is an event dispatcher for Mac OS events. Normally you expect it to be very fast, but it may also encounter an event which will open a document or redraw a complex drawing. The thread can't be both at the same time: fast and longish.

The solution is create a new thread on demand with appropriate priority and hand it over the event. This way you solve the low/high priority problem and get your hands free to serve any further fast events, e.g. you can process mouse clicks and key strokes while you still redraw the screen or you can drag a window while the action inside goes on!

#### Conclusion

> The priority of a thread should be the time in msec within which it is expected to handle it's task. This may be a single task, so that the priority is the expected total running time; e.g. a compiler; or it may be an event driven thread which may be running infinitely waiting for it's events, where the priority is the maximum allowed event handling latency; e.g. for a user interface.

#### long Twsp::Timeout()

This returns the system time, when a time sheduled thread is resumed or when it times out waiting for a semaphor or interrupt. In all other cases the returned time is meaningless. You can test for a valid timeout value by inspecting the thread's state:

            if (theThread->State()&timeshed) { theTimeout = theThread->Timeout(); }

Always be aware that you may be switched out for some time if you call a dispatcher method or Disp() directly and always think very precisely whether any result is guaranteed to remain valid across thread switching. Remember Murphy's Law (what he really meant): What can happen will happen. And if do not trap unlikely possibilities wherever they can occure, they'll haunt you forever!

#### const char* Twsp::Name()

Query the name of a thread.

Every thread is asigned a name when you create it. You should always use telling names for your threads, so you can include it in your debugging output.

#### long& Twsp::Ptime()

Query the process time consumed by this thread up to now.

The system timer updates a counter in the currently serviced thread every milli second. This cell can be read and optionally cleared by a thread which monitors process load or may be used as a termination criterium for a malfunctioning thread.

Because the system timer also triggers time sheduled action, there is a cause for a systematic error: If a thread is time sheduled and runs only very short, it has a good chance to be never seen by the system ticker; and in an extreme example where it is sheduled once per msec, this thread may even eat up nearly all process time. Normally you won't design your threads that way.

#### The thread identifier Tid

            typedef Twsp* Tid;

The thread identifier is required to identify the thread on which you want to perform a dispatcher method.

#### Tid Twsp::MyTid()

This returns the Tid of the currently executed thread itself. If you get a Tid from some kind of list you can compare it to MyTid() to see if it addresses yourself. Most dispatcher methods have additional versions which automatically work on the currently running thread, so that you do not need MyTid() to perform all the common actions like sheduling or suspending on yourself:

        e.g.:
            Twsp::Suspend()  ->  MyTid()->Suspen()     works on 'this' thread
            Suspend(Tid)     ->  Suspend(MyTid())      works on the passed thread
            Suspend()        ->                        works on the current thread

#### Tid Twsp::RootTid()

Query the Tid of the #root thread. This is the thread running inside main() which should execute the event polling loop. The #root thread's stack is the original stack of the application, all other threads get their stack allocated with new. The #root thread can do everything an unthreaded application can do, for other threads there may be some (yet unknown) limitations.

#### Tid Twsp::GetTid(const char* name)

Query the Tid of a thread which you know by name. If there is no such thread, GetTid() returns NULL. If there are multiple threads with this name, you can't know which one will be found. You should avoid this.

#### bool Twsp::IsValidTid()

When you got somehow a Tid and cannot guarantee by other means that it is still valid, you can validate the Tid with this test. The functionality of the test is limited, it is slow if you have lot's of threads and Tids can be recycled. You should avoid circumstances where you do not know for shure whether a Tid is still valid or not. This test is provided for security reasons, mainly while working on a project.

#### Tid Twsp::Split ( ... )

        Tid Split ( ThreadProc, void* data, ulong prio, ulong size, const char* name )

The only way to create a thread is with this function. It creates a new thread according to the passed parameters and returns it's Thread identifier or NULL if it failed due to lack of memory. The parameters are:

ThreadProc: is the address of the procedure for the new thread to call. The new thread starts executing this procedure according to it's priority and when it returns, the thread is terminated, that is: deleted.

void* data: You can pass data to your new thread. Set up your data and pass it's address to Split(). Because the splitted thread can be of higher or lower priority than your's, it can read the data imediately or later. Because semaphors could have raised your priority or for other fancy reasons it may be hard to make shure that your child thread reads the data before you destroy the data. So it is best practice to create a copy of the data with new() and pass this address to Split() and the splitted thread can read and use the data and delete() it when it's done. You should always proceed like this! Most time howether, you do not need to pass any data and pass NULL instead.

ulong prio: This is the desired priority for your child thread. Chose a proper priority in accordance to the priority guidelines above! The priority can be lowered or raised later by ChangePrio() if really required, and semaphors can temporarily raise a threads effective priority.

ulong size: Estimate how much stack your thread will require for all procedures called one in another with all their local variables. Split() will add the size for system tasks, for logging and checking methods and so on, so you need not care about that. If you are unshure you can use the StackFree() function within your thread to show you how much is left.

const char* name: Give your thread a name! This is handy if you want to trace it's action, want to know who is logging a message and you can find it by it's name if you like, though storing the Tid somewhere is faster.

#### typedef void (*ThreadProc)(void*)

Thread main procedure. When you create a new thread, you must pass Split() the address of a procedure which the new thread shall execute. This procedure must be of type ThreadProc.

The new thread starts executing this procedure according to it's priority and when it returns, the thread is terminated, that is: deleted.

            void MyThreadProc ( void* myData )
            {
            	MyStruct& data = *(MyStruct*)myData;
            	/* ... do your work ... */
            	delete myData;
            }

Parameters can be passed using the void* data parameter. Make a copy of your data using new() and pass that address to Split() and the splitted thread can read and use the data and delete() it when it's done. You should always proceed like this if you must pass parameters!

#### void Termi()

            void Twsp::Termi ( )            terminate this thread
            void Termi ( )                  terminate self
            void Termi ( Tid t )            terminate that thread
            void Termi ( const char* name ) terminate thread by name

Terminating a thread, even terminating self should be avoided! The only way to properly terminate a thread is by returning from it's ThreadProc! Neverthelesse sometimes it's save and handy to terminate this or that thread.

Why is it so precious to terminate a thread this way? Normally you can't say for shure where it is currently stuck. It is probably some nesting levels deep in some procedure execution and has piled up some local variables. Probably some of these variables have destructor methods to be called when they are deleted. If you terminate a thread then all these variables are not deleted but simply forgotten. This will result in some memory leakage, in the better case, but more likely in some dangling pointers, waiting to kill the entire Mac. So only terminate threads which are designed to be terminatable, that is, which do all their dispatcher calls at locations where they only own simple variables with no destructor needs!

#### void Suspend()

            void Twsp::Suspend ( )            suspend this thread
            void Suspend ( )                  suspend self
            void Suspend ( Tid t )            suspend that thread
            void Suspend ( const char* name ) suspend thread by name

You can suspend execution of any thread using these calls. But the precise effect depends on their current state, which is, in case you suspend yourself, of course running:

running | -> suspended
suspended | -> suspended
time sheduled | -> suspended
waiting for interrupt | -> suspended
waiting for semaphor without timeout | -> suspended; semaphor will be requested again when the thread is resumed.
waiting for semaphor with timeout | -> suspended; semaphor will be requested again if the thread is resumed before the timeout expires.

#### void Resume()

            void Twsp::Resume ( )            resume this thread
            void Resume ( Tid t )            resume that thread
            void Resume ( const char* name ) resume thread by name

You can resume execution of any thread using this call. But the precise effect depends on their current state:

running | -> running
suspended | -> running
time sheduled | -> running
sheduled for interrupt | -> running
waiting for semaphor without timeout | -> running; semaphor will be requested again.
waiting for semaphor with timeout | -> running; semaphor will be requested again if the timeout has not yet expired.

#### void Wait()

            void Wait ( long duration )         shedule self to wait some time
            void Twsp::Wait ( long duration )   shedule this thread
            void Wait ( Tid t, long duration )  shedule that thread

You can suspend and time-shedule any thread using this call. But the precise effect depends on their current state, which is, in case you shedule yourself, of course running:

running | -> time sheduled
suspended | -> time sheduled
time sheduled | -> time sheduled for the new time
sheduled for interrupt | -> time sheduled
waiting for semaphor without timeout | -> time sheduled; semaphor will be requested again when the thread resumes.
waiting for semaphor with timeout | -> time sheduled for the new time; semaphor will be requested again if the thread resumes (for whatever reason) before the timeout given in the Sema::Request() expired!

#### void Shedule()

            void Shedule (long clock )          shedule self for certain system time
            void Twsp::Shedule (long clock )    shedule this thread
            void Shedule ( Tid t, long clock )  shedule that thread

You can suspend and time-shedule any thread using this call. It is merely identical to Wait(), except that it takes a pre-calculated system time to shedule for. All said for Wait() applies here too.

#### void ChangePrio()

            void ChangePrio ( ulong n )         change own prio
            void Twsp::ChangePrio ( ulong n )   change prio of this thread
            void ChangePrio ( Tid t, ulong n )  change prio of that thread

Changes the priority of this thread. The priority boosting done by semaphors is invisible (opaque) to this call. Changing the priority of a thread is not recommended.

#### size_t Twsp::StackFree()

Returns the amount of free space on a thread's stack. StackFree() can be called for the currently running thread or any other thread. This must never fall below 1000 bytes and should never be less than minStackFree.

#### const size_t minStackFree

Returns the suggested amount of minimum free space on a thread's stack. This free space is required for error checking (~2k), logging (~8k), requesters (~20k), or a debugger (~20k).