Horus Thread Interface

This document is part of the online Horus Documentation, under Horus Utilities.


In this page we specify the interface to threads (lightweight processes). The interface is designed so that the existing threads implementation (if any) in the underlying operating system can slide easily into this interface, i.e. this interface provides a thin (portable) veneer to underlying threads implementations. On operating systems that do not provide threads, the threads implementation should be designed to fit this interface.

For portability reasons we have not added a mechanism for inter-thread signaling. A complication is that threads may live in multiple address spaces (see also the section on messages). We have added the concept of thread priority, which may not necessarily be supported by the underlying thread implementation.

Threads are first declared, and then created. During their declaration as much as possible is initialized so that creation may be as efficient as possible. A thread identifier of type thread is returned that may be passed between address spaces (user, kernel) on the same site.

A thread is invoked with two void pointer arguments. The first is usually specified by the declarator of the thread, and often points to a structure with global information that the thread needs during its execution. The second is usually specified by the creator of the thread, and often points to arguments that are passed to the thread.

When a thread is created, it does not necessarily run independently of the invoking thread. If possible, the invoking thread will just execute the task itself. This should only be done for threads that run for only a short time, and can be done only for threads that live in the same address space and have enough stack space available. The thread declarator specifies this using a negative priority. The thread, if just executed by the invoking thread, will use the same priority as the invoking thread. If not, if will get a low, default priority.

There is a special thread descriptor, T_NOTHREAD, that can be used for the null thread. This thread does nothing (exits immediately), and can be used in any place where a thread descriptor is required, but no thread is necessary.

SOURCES

machdep/*/thread.c

INCLUDE

#include "muts.h"

INTERFACE

error_t t_declare(
	void (*proc)(void *env, void *param),
	char *name,
	int stacksize,
	int priority,
	thread *tdesc
	);
}
t_declare declares a new thread. This takes care of any bookkeeping (initialization, etc.) necessary before a thread is created.

proc is a procedure that gets two void pointer arguments by the creator of the thread. The first, env, is usually provided by the declarator of the thread, while the second param usually points to parameters provided by the creator of the thread. The supplied name is mainly used for debugging and monitoring purposes. The stacksize is the size of stack space (in units of char) that the procedure will maximally use.

The priority is a number between 0 and 255. Threads of higher priority will normally be scheduled before threads of lower priority. It is also possible to specify a negative priority. In this case, the procedure may be invoked directly, instead of by having a thread of control allocated.

A thread descriptor (tdesc) of type thread is returned. This descriptor is needed to be able to create an instance of this thread. Threads are declared in the address space current at the time of the call to t_declare. Threads run in the address space in which they are declared, even if created in a different address space. These two address spaces must, however, live on the same machine (or, rather, --MUTS site).

error_t t_create(
	thread tdesc,
	void *env,
	void *param
);
Create a thread using the specified thread descriptor. The env and param arguments are passed to the new thread. A thread terminates by returning. t_exit is no longer supported. Use setjmp/longjmp to bail out of the middle of a thread.

EXAMPLE

The following example declares a thread of priority zero, and subsequently creates two instances of this thread. The stack size is chosen to be 4096, which should be ample for most computer architectures.
void example(void *env, void *param){
	char *s1 = env, *s2 = param;

	printf("created with env %s and param %s", s1, s2);
}
...
{	 
	thread t;

	(void) t_declare(example, "example", 4096, 0, &t);
	(void) t_create(t, "env", "example 1");
	(void) t_create(t, "env", "example 2");
	...
}								 

FUTURE WORK

We are currently getting more and more convinced that threads are a bad idea to start with. Thread interfaces have been available for a long time now, and nobody knows how to use them. Thread priorities are a joke. We are therefore swithing to an automaton model. To prepare for this switch, we discourage that you use semaphores or blocking system calls like read() or recvfrom(). Locks are ok, though, since they can be implemented as spin locks.


This document is part of the online Horus Documentation, under Horus Utilities.