/*
 * thread.h
 *
 * [Generated from thread, 25 September 1996]
 */

#if !defined(__CC_NORCROFT) || !defined(__arm)
  #error You must use the Norcroft ARM Compiler for Sapphire programs
#endif

#pragma include_only_once
#pragma force_top_level

#ifndef __thread_h
#define __thread_h

#ifndef __sapphire_h
  #include "sapphire.h"
#endif

/*----- Overview ----------------------------------------------------------*
 *
 * Functions provided:
 *
 *  thread_create
 *  thread_setPriority
 *  thread_setTimeSlice
 *  thread_destroy
 *  thread_suspend
 *  thread_resume
 *  thread_yield
 *  thread_createSem
 *  thread_destroySem
 *  thread_threaded
 *  thread_wait
 *  thread_signal
 *  thread_init
 *  thread_enterCrit
 *  thread_leaveCrit
 *  thread_errorHandler
 */

/* --- thread_create --- *
 *
 * On entry:	R0 == size of stack to allocate, or 0 for a default
 *		R1 == pointer to thread routine
 *		R2 == workspace pointer to pass in R10
 *		R3 == workspace pointer to pass in R12
 *
 * On exit:	R0 == thread handle for the thread
 *		May return an error
 *
 * Use:		Creates a new thread running `in the background' (i.e. over
 *		idle events).
 *
 *		The thread is passed control with the registers R10 and R12
 *		set up from R1 and R2 passed to this routine and R13 pointing
 *		to the top of a stack chunk.  R0 on entry contains the
 *		thread's handle.  The thread is passed the scratchpad
 *		address (in R11).  The values of other registers are
 *		indeterminate and must not be relied upon.
 *
 *		The default stack size for a new thread is 1K, although this
 *		may change in future.
 *
 *		The thread may exit by calling thread_destroy or by
 *		returning in the normal way.
 */

extern routine thread_create;

/* --- thread_setPriority --- *
 *
 * On entry:	R0 == thread handle
 *		R1 == new priority to set
 *
 * On exit:	--
 *
 * Use:		Changes the priority of a thread.  The priority if a thread
 *		is a signed integer.  The highest priority thread is the one
 *		which runs.  If more than one thread has maximum priority,
 *		they are run in a cyclical order.
 */

extern routine thread_setPriority;

/* --- thread_setTimeSlice --- *
 *
 * On entry:	R0 == thread handle
 *		R1 == new timeslice size, in centiseconds
 *
 * On exit:	--
 *
 * Use:		Changes a thread's timeslice size.  Specify 0 to indicate
 *		that thread shouldn't be pre-empted.
 */

extern routine thread_setTimeSlice;

/* --- thread_destroy --- *
 *
 * On entry:	R0 == thread handle to destroy, if not executing a thread
 *
 * On exit:	--
 *
 * Use:		Destroys either the current thread or a thread with the
 *		the given handle if no thread is executing currently.  You
 *		can't destroy an arbitrary thread while running in one.
 *
 *		If a thread is waiting for a semaphore, it is removed from
 *		the waiting list.
 */

extern routine thread_destroy;

/* --- thread_suspend --- *
 *
 * On entry:	R0 == thread handle, or 0 for the current thread
 *
 * On exit:	--
 *
 * Use:		Suspends a thread's execution.  If a thread is currently
 *		running, that thread is suspended.  Otherwise, any thread
 *		may be suspended.
 *
 *		If the thread is currently claiming semaphores, the
 *		semaphores are not released, because we don't whether the
 *		system is in a fit state for this.
 *
 *		Thread suspensions are counted.  i.e. if you suspend a thread
 *		5 times, you have to resume it 5 times for it to become
 *		active again.
 */

extern routine thread_suspend;

/* --- thread_resume --- *
 *
 * On entry:	R0 == thread handle
 *
 * On exit:	--
 *
 * Use:		Allows a suspended thread to continue operations.  If you
 *		resume a thread more times than it has been suspended,
 *		any excess resumes are ignored.  You can't resume a thread
 *		to stop it being blocked by a semaphore.
 */

extern routine thread_resume;

/* --- thread_yield --- *
 *
 * On entry:	--
 *
 * On exit:	--
 *
 * Use:		Pauses the thread for a while.  You only need to use this
 *		call if you have stopped the current thread from being
 *		timesliced.
 */

extern routine thread_yield;

/* --- thread_createSem --- *
 *
 * On entry:	R0 == initial value for semaphore (0 for counter, 1 for
 *		      mutex)
 *
 * On exit:	R0 == semaphore handle and V clear if all went well
 *		R0 == pointer to error and V set if something went wrong
 *
 * Use:		Creates a semaphore with the given initial counter value.
 *
 *		The semaphore can be used to provide serialised access to
 *		a resource by initialising its value to 1 and performing the
 *		following:
 *
 *		thread_wait(mySemaphore)
 *		//
 *		// Do things with the resource
 *		//
 *		thread_signal(mySemaphore)
 *
 *		Or you can inform a thread that it has items in its input
 *		queue by having the following in the thread code:
 *
 *		while true
 *		  thread_wait(theSemaphore)
 *		  getFromQueue(myQueue,item)
 *		  process(item)
 *		endWhile
 *
 *		and when inserting queue items:
 *
 *		addToQueue(item)
 *		thread_signal(theSemaphore)
 *
 *		It is distinctly possible that input queue management will
 *		be introduced in a separate Sapphire module.
 */

extern routine thread_createSem;

/* --- thread_destroySem --- *
 *
 * On entry:	R0 == semaphore handle
 *
 * On exit:	--
 *
 * Use:		Destroys a semaphore when it's no use any more.  If threads
 *		are waiting for	it, an error is generated.
 */

extern routine thread_destroySem;

/* --- thread_threaded --- *
 *
 * On entry:	--
 *
 * On exit:	CS if currently running a thread, CC otherwise
 *
 * Use:		Informs the caller whether a thread is currently executing.
 */

extern routine thread_threaded;

/* --- thread_wait --- *
 *
 * On entry:	R0 == semaphore handle
 *
 * On exit:	If successful, R0 preserved and V clear.
 *		If failed, R0 == pointer to error block and V set
 *
 * Use:		Waits on a sempahore.  The algorithm actually is as follows:
 *
 *		if semaphore.counter == 0 then
 *		  addToWaitingList(semaphore,currentThread)
 *		  suspend(currentThread)
 *		else
 *		  semaphore.counter -= 1
 *		endIf
 *
 *		See thread_createSem for suggestions on how to make use of
 *		semaphores.
 */

extern routine thread_wait;

/* --- thread_signal --- *
 *
 * On entry:	R0 == semaphore handle
 *
 * On exit:	--
 *
 * Use:		Increments a semaphore's counter if no threads are waiting
 *		for it, or releases a thread waiting for the semaphore.
 *
 *		The actual algorithm is shown below:
 *
 *		if semaphore.waitingList != 0 then
 *		  thread = removeFromWaitingList(semaphore)
 *		  unSuspend(thread)
 *		else
 *		  semaphore.counter += 1;
 *		endif
 *
 *		See thread_createSem for suggestions on how to make use of
 *		semaphores.
 */

extern routine thread_signal;

/* --- thread_init --- *
 *
 * On entry:	--
 *
 * On exit:	--
 *
 * Use:		Initialises the thread system for use.
 */

extern routine thread_init;

/* --- thread_enterCrit --- *
 *
 * On entry:	--
 *
 * On exit:	--
 *
 * Use:		Declares that the current thread is about to enter a
 *		critical section and must not be interrupted.
 */

extern routine thread_enterCrit;

/* --- thread_leaveCrit --- *
 *
 * On entry:	--
 *
 * On exit:	--
 *
 * Use:		Declares that the current thread has left the critical
 *		section and can be interrupted again.
 */

extern routine thread_leaveCrit;

/* --- thread_errorHandler --- *
 *
 * On entry:	R0 == pointer to routine to call
 *		R1 == R12 value to call with
 *		R2 == R13 value to call with
 *
 * On exit:	--
 *
 * Use:		Sets up the error handler for a thread.
 */

extern routine thread_errorHandler;

/*----- That's all, folks -------------------------------------------------*/

#endif
