gint-with-thread/include/gint/thread.h

673 lines
24 KiB
C

#ifndef GINT_THREAD
# define GINT_THREAD
#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>
/* define the jmp_buf type */
#include <gint/std/setjmp.h>
/* Define the default context switching frequency */
#ifndef THREAD_SCHEDULER_FREQUENCY
# define THREAD_SCHEDULER_FREQUENCY 16
#endif
/* Define the default thread stack size */
#ifndef THREAD_STACK_SIZE
# define THREAD_STACK_SIZE (4 * 1024)
#endif
/* Define the default kernel idle thread's stack size */
#ifndef THREAD_IDLE_STACK_SIZE
# define THREAD_IDLE_STACK_SIZE 32
#endif
//---
// Thread attribute interface
//---
/* define the thread wartermark, used to check if the mutex is valid */
#define THREAD_ATTR_WATERMARK (~0xdeadbeef)
/* define the fundamental data type and alias for thread attribute */
struct thread_attr_s {
struct {
enum {
THREAD_ATTR_JOINABLE = 0,
THREAD_ATTR_DETACHED = 1,
} detach;
enum {
THREAD_ATTR_LONGJMP_DISABLE = 0,
THREAD_ATTR_LONGJMP_ENABLE = 1,
} longjmp;
} state;
struct {
uint32_t watermark;
} private;
};
typedef struct thread_attr_s thread_attr_t;
/* thread_attr_init(): Initialize thread attribute object
This function initializes the thread attributes object pointed to by ATTR
with default attribute values. After this call, individual attributes of the
object can be set using various related functions formatted like
"thread_attr_*()" and then the object can be used in one or more
"thread_create()" calls that create threads.
@return
* negative value if ATTR is NULL
* 0 if success */
extern int thread_attr_init(thread_attr_t *attr);
/* thread_attr_setdetachstate() and thread_attr_getdetachstate()
The "thread_attr_setdetachstate()" function sets the detach state attribute
of the thread attributes object referred to by attr to the value specified
in detachstate.
The detach state attribute determines whether a thread created using the
thread attributes object attr will be created in a joinable or a detached
state.
The following values may be specified in detachstate:
- THREAD_ATTR_DETACHED
Threads that are created using ATTR will be created in a detached state.
- THREAD_ATTR_JOINABLE
Threads that are created using attr will be created in a joinable state.
The default setting of the detach state attribute in a newly initialized
thread attributes object is THREAD_ATTR_JOINABLE.
The "thread_attr_getdetachstate()" returns the detach state attribute of
the thread attributes object attr in the buffer pointed to by detachstate.
@return
* negative value if ATTR is NULL or uninitialized
* 0 if success */
extern int thread_attr_setdetachstate(thread_attr_t *attr, int detachstate);
extern int thread_attr_getdetachstate(thread_attr_t *attr, int *detachstate);
/* thread_attr_enablelongjmp() thread_attr_getlongjmpstatus()
This feature is CUSTOM to the Gint kernel. It will allow, when a thread with
this attribute die, instead of release its memories and removed it from the
scheduler, allow it to perform a "longjmp()" and return ("rewinds") to the
"thread_create()" function involved to create the current thread. The
original "thread_create()" will return the special value:
THREAD_CREATE_LONGJMP_RETURN.
In the case of Gint, this feature is very useful because the "main" function
can be threaded and we can return the to "dark-side" of the kernel to manage
destructors or to recall the main function again. We cann allow this while
continuing running (potential) threaded drivers that can continue working
while being isolated from the Gint "bootstrap" part.
The following values may be specified in "status":
- THREAD_ATTR_LONGJMP_ENABLE
Enable the longjmp() when the thread come to the end.
- THREAD_ATTR_LONGJMP_DISABLE
Disable the longjmp() when the thread come to the end.
@return
* negative value if ATTR is NULL or uninitialized
* 0 if success */
extern int thread_attr_enablelongjmp(thread_attr_t *attr, int status);
extern int thread_attr_getlongjmpstatus(thread_attr_t *attr, int *status);
/* thread_attr_destroy(): Destroy thread attribute object
When a thread attributes object is no longer required, it should be destroyed
using the "pthread_attr_destroy()" function. Destroying a thread attributes
object has no effect on threads that were created using that object.
Once a thread attributes object has been destroyed, it can be reinitialized
using "thread_attr_init()". Any other use of a destroyed thread attributes
object has undefined results.
@return
* negative value if ATTR is NULL or uninitialized
* 0 if success */
extern int thread_attr_destroy(thread_attr_t *attr);
//---
// Signals interface
//
// A “signal” is a software interrupt delivered to a process. The operating
// system uses signals to report exceptional situations to an executing
// program. Some signals report errors such as references to invalid
// memory addresses; others report asynchronous events, such as
// disconnection of a phone line.
//
// If you anticipate an event that causes signals, you can define a handler
// function and tell the operating system to run it when that particular
// type of signal arrives.
//
// Finally, one thread can send a signal to another process; this allows a
// parent thread to abort a child, or two related thread to communicate
// and synchronize.
//
//---
/* Define thread signal set type */
typedef uint32_t sigset_t;
/* Define signals handler type */
typedef void (*sighandler_t)(int);
/* Define fake signal functions. */
#define SIG_ERR ((sighandler_t) -1) /* Error return. */
#define SIG_DFL ((sighandler_t) 0) /* Default action. */
#define SIG_IGN ((sighandler_t) 1) /* Ignore signal. */
/* Define the number of signals availbable */
#define NSIG 19
/* Define all signum
(note: All signals are not currently supported, but will be in near future)*/
#define SIGKILL 0 /* (unblockable) Killed */
#define SIGSTOP 1 /* (unblockable) Stop */
#define SIGTERM 2 /* (unblockable) Termination request. */
#define SIGCONT 3 /* Continue. */
#define SIGTRAP 4 /* Trace/breakpoint trap. */
#define SIGILL 5 /* Illegal instruction. */
#define SIGTSTP 6 /* Keyboard stop. */
#define SIGABRT 7 /* Abnormal termination. */
#define SIGCHLD 8 /* Child terminated or stopped. */
#define SIGPOLL 9 /* Pollable event occurred (System V). */
#define SIGVTALRM 10 /* Virtual timer expired. */
#define SIGPROF 11 /* Profiling timer expired. */
#define SIGUSR1 12 /* User-defined signal 1. */
#define SIGUSR2 13 /* User-defined signal 2. */
#define SIGHUP 14 /* hang up. */
#define SIGINT 15 /* interruption */
#define SIGBUS 16 /* bus error */
#define SIGFPE 17 /* fata aritmetic error */
#define SIGSEGV 18 /* segmentation violation */
//---
// User thread interface
//---
/* define special return value with the thread_create() function */
#define THREAD_CREATE_LONGJMP_RETURN (0xd1ceca5e)
/* Define thread ID alias */
typedef uint32_t thread_t;
/* cpu_ctx: whole SH3-based CPU hardware context definition */
struct cpu_ctx {
uint32_t reg[16];
uint32_t gbr;
uint32_t macl;
uint32_t mach;
uint32_t ssr;
uint32_t spc;
uint32_t pr;
};
/* struct thread: Thread structure definition */
struct thread {
/* hardware context */
struct cpu_ctx context;
/* thread configuration */
struct thread_attr_s attr;
/* signals information */
struct {
sighandler_t handler[NSIG];
sigset_t pending;
sigset_t blocking;
} signals;
/* thread scheduler information */
struct {
/* thread status */
enum {
THREAD_STATUS_PAUSED = 0,
THREAD_STATUS_RUNNING = 1,
THREAD_STATUS_ZOMBIE = 2,
THREAD_STATUS_DEAD = 3
} status;
/* thread identifier */
thread_t id;
/* hierarchical information */
struct thread *next;
} scheduler;
/* private information */
struct {
uintptr_t stack; /* original stack address */
uintptr_t ret; /* saved exit value */
jmp_buf jmpbuf; /* longjmp feature */
struct thread *sibling; /* sibling thread list */
struct thread *child; /* child thread list */
struct thread *parent; /* thread parent address */
} private;
};
/* thread_create(): Create a new thread
This function creates a new thread which starts execution by invoking
"start_routine()"; You can passe many argument as you want and
all args is passed as argument of start_routine().
The new thread terminates in one of the following ways:
- It calls "thread_exit()", specifying an exit status value that is
available to another thread in the same process that calls
"thread_join()"
- It returns from start_routine(). This is equivalent to calling
"thread_exit()" with the value supplied in the return statement.
- It is canceled (see "thread_cancel()").
- If one thread with the special THREAD_ATTR_MAIN_THREAD die. In this case,
all thread created with this special (custom) attribute will be killed
The "attr" argument points to a pthread_attr_t structure whose contents
are used at thread creation time to determine attributes for the new thread;
this structure is initialized using "thread_attr_init()" and related
functions. If "attr" is NULL, then the thread is created with default
attributes.
Before returning, a successful call to "thread_create()" stores the ID of
the new thread in the buffer pointed to by "thread"; this identifier is used
to refer to the thread in subsequent calls to other "thread_*" functions.
@return:
* negative value if error occurs
* 0 if success
*/
extern int thread_create(thread_t *thread,
thread_attr_t *attr, void *start_routine, ...);
/* Makes sure an argument is always provided, for va_arg() and for definite the
last arguments sended by the user */
#define THREAD_CREATE_ARG_END_WATERMARK (0xdeb0cad0)
#define thread_create(...) thread_create(__VA_ARGS__,\
THREAD_CREATE_ARG_END_WATERMARK)
/* thread_join(): Waits for the thread specified by thread to terminate
This function waits for the thread specified by thread to terminate.
If that thread has already terminated, then "thread_join()" returns
immediately. The thread specified by THREAD must be joinable.
If RETVAL is not NULL, then "thread_join()" copies the exit status of the
target thread (i.e., the value that the target thread supplied to
"thread_exit()") into the location pointed to by RETVAL. If the target thread
was canceled, then THREAD_CANCELED is placed in the location pointed to by
RETVAL.
If multiple threads simultaneously try to join with the same thread, the
results are undefined. If the thread calling "thread_join()" is canceled,
then the target thread will remain joinable (i.e., it will not be detached).
The "thread_tryjoin()" function have the same behaviour that the
"thread_join()" excet that it will not block the current thread and returns
directly if the wanted thread is busy or unjoinable.
@return:
* negative value if error occurs
* 0 if success */
extern int thread_join(thread_t thread, void **retvel);
extern int thread_tryjoin(thread_t thread, void **retval);
/* thread_exit(): Terminate the calling thread
This function will terminates the calling thread and returns a value via
retval that (if the thread is joinable) is available to another thread in the
same process that calls "thread_join()".
Any clean-up handlers established by pthread_cleanup_push(3) that have not
yet been popped, are popped (in the reverse of the order in which they were
pushed) and executed. If the thread has any thread-specific data, then, after
the clean-up handlers have been executed, the corresponding destructor
functions are called, in an unspecified order.
When a thread terminates, process-shared resources (e.g., mutexes, condition
variables, semaphores, and file descriptors) are not released. */
extern void thread_exit(void *retval);
/* thread_kill(): Send signal to a thread
This fucntion will raise any signals to any thread. The value of the thread
can have special value:
* if thread is positive then signal is sent to the thread with the ID
specified by thread.
* if thread is equals 0, then signal is sent to threads which have the
calling has parent.
@return:
* negative value if error occurs
* 0 if success */
extern int thread_kill(thread_t thread, int sig);
/* thread_signal(): sets the disposition of the signal signum to handler, which
is either SIG_IGN, SIG_DFL, or the address of a
programmer-defined function (a "signal handler").
This part provides a simple interface for establishing an action for a
particular signal.
If the signal signum is delivered to the process, then one of the following
happens:
- If the disposition is set to SIG_IGN, then the signal is ignored.
- If the disposition is set to SIG_DFL, then the default action associated
with the signal
- If the disposition is set to a function, then first either the disposition
is reset to SIG_DFL, or the signal is blocked, and then handler is called
with argument signum. If invocation of the handler caused the signal to
be blocked, then the signal is unblocked upon return from the handler.
The signals SIGKILL and SIGSTOP cannot be caught or ignored.
@return
the previous value of the signal handler, or SIG_ERR on error. */
extern void (*thread_signal(int signum, void (*handler)(int)))(int);
/* thread_terminate(): KERNEL-ONLY: destroy a thread
This function will destroy a thread regardless of its attributes, scheduler
status, ... This function is used by the kernel to remove definitively a
thread. */
extern int thread_terminate(struct thread *thread);
//---
// Thread mutex interface
//
// To have better control of resources and how threads access them, Gint
// implements a "mutex" object, which can help avoir race conditions and
// other concurrency issues. The term mutex refers to mutual exclusion.
//---
/* define the thread wartermark, used to check if the mutex is valid */
#define THREAD_MUTEX_WATERMARK 0xdeadbeef
/* mutex returnable value */
enum
{
thread_mutex_retval_success = 0,
thread_mutex_retval_busy = 1,
thread_mutex_retval_error = 2,
thread_mutex_retval_nomem = 3,
thread_mutex_retval_timeout = 4
};
/* mutex type */
enum
{
thread_mutex_type_plain = (1 << 1), /* non-recursive */
thread_mutex_type_recursive = (1 << 2), /* support recursive */
thread_mutex_type_timed = (2 << 3) /* support timeout */
};
/* define the fundamental data type and alias for thread mutex */
struct thread_mutex_s
{
uint32_t watermark;
uint32_t lock;
uint8_t type;
thread_t owner;
struct {
int id;
int abord;
} timer;
};
typedef struct thread_mutex_s thread_mutex_t;
/* thread_mutex_init(): Creates a new mutex object with type TYPE.
This function will initialize the mutex MUTEX using the type TYPE. The type
define the behaviour of the mutex, basically, you have:
- thread_mutex_type_plain
A mutex that does not support timeout, or test and return
- thread_mutex_type_recursive
A mutex that support recursive locking, which mean that the the owning
thread can lock it more than once without causing deadlock
- thread_mutex_type_timed
A mutex that supports timeout
Not all combinations of mutex types are valid for the TYPE argument.
Valid uses of mutex types for the TYPE argument are:
- thread_mutex_type_plain
A non-recursive mutex that does not support timeout
- thread_mutex_type_timed
A non-recursive mutex that does support timeout
- thread_mutex_type_plain | thread_mutex_type_timed
A recursive mutex that does not support timeout
- thread_mutex_type_timed | thread_mutex_type_timed
A recursive mutex that does support timeout
@return
* thread_mutex_retval_success If successful initialized
* thread_mutex_retval_error If error occur */
extern int thread_mutex_init(thread_mutex_t *mutex, int type);
/* thread_mutex_lock(): Block the current thread until the mutex is unlocked.
This function will bock the calling thread until the mutex is locked.
The behaviour is undefined if the current thread has already locked the
mutex and the mutex is not recursive.
Prior calls to "thread_mutex_unlock()" to the same mutex syncronize-with
this operations (if this operation success), and all lock/unlock operations
on any given mutex form a single total order (similar to the modification
order of an atomic).
@return:
* thread_mutex_retval_success If lock was obtained
* thread_mutex_retval_error If error occur */
extern int thread_mutex_lock(thread_mutex_t *mutex);
/* thread_mutex_timedlock(): Block the current thread until the mutex is
locked or until the time TIME has been reached.
This function will bock the calling thread until the mutex is locked or if
the time TIME (millisecond) has been reached. If the current has been
already locked the mutex and the mutex is not recursive, or if the mutex
does not support timeout an error will be returned.
Prior calls to "thread_mutex_unlock()" to the same mutex syncronize-with
this operations (if this operation success), and all lock/unlock operations
on any given mutex form a single total order (similar to the modification
order of an atomic).
@return:
* thread_mutex_retval_success if the lock was obtained
* thread_mutex_retval_error if error occur */
extern int thread_mutex_timedlock(thread_mutex_t *mutex, uint64_t delay_us);
/* thread_mutex_trylock(): Try to lock the mutex without blocking.
This function tries to lock the mutex without blocking. It return
immediately if the mutex is already locked.
Prior calls to "thread_mutex_unlock()" to the same mutex syncronize-with
this operations (if this operation success), and all lock/unlock operations
on any given mutex form a single total order (similar to the modification
order of an atomic).
@return:
* thread_mutex_retval_success if the lock was obtained
* thread_mutex_retval_busy if the mutex is already locked
* thread_mutex_retval_error if error occur */
extern int thread_mutex_trylock(thread_mutex_t *mutex);
/* thread_mutex_unlock(): Unlocks the mutex
This function synchronize-with subsequent "thread_mutex_lock()",
"thread_mutext_trylock()" and "thread_mutex_timedlock()" calls on the same
mutex. All lock/unlock operations on any given mutex form a signle total
order (similar to the modification order of an atomic).
@return:
* thread_mutex_retval_success if unlocked
* thread_mutex_retval_busy if not locked but need other unlock call
* thread_mutex_retval_error if error occur */
extern int thread_mutex_unlock(thread_mutex_t *mutex);
/* thread_mutex_destroy(): Destroy the mutex.
This function will destroy the mutex. If there are any thread waiting on the
mutex, the behaviour is undefined. */
extern void thread_mutex_destroy(thread_mutex_t *mutex);
//---
// Thread atomic operation
//---
/* thread_atomic_start(): Start atomic operation
This function will block interruptions and exception until
"thread_atomic_stop()" is called. This is really useful when you need to
secure some tricky part of code (like driver kernel-level implementation).
But be carefull: your code executed after this function SHOULD be
EXCEPTION-SAFE ! Otherwise, a crash will occur and Gint can do nothing to
avoid it because is hardware specific. If you need to secure shared data,
use mutex instead.
This implementation is recursive-safe and will return:
* SR value when you enter in "atomic" operation (first call)
* 0 if you are already in a "atomic" operation (x call)
To return to the "normal" operation, you should call "thread_atomic_stop()"
as many time as you have involved with "thread_atomic_start()". */
extern uint32_t thread_atomic_start(void);
/* thread_atomic_stop(): Stop atomic opration
This function will try to return to the "normal" mode and will return:
* negative value If error occur
* 0 If you are alwayrs in "atomic" mode
* the restored SR value If you are returned to the "clasic" mode */
extern uint32_t thread_atomic_stop(void);
//---
// Signals management (kernel-level)
//---
enum {
thread_signals_deliver_retval_running = 0,
thread_signals_deliver_retval_stopped = 1,
thread_signals_deliver_retval_dead = 2,
};
/* Macros for constructing status values. */
#define __W_EXITCODE(ret, sig) ((ret) << 8 | (sig))
#define __W_STOPCODE(sig) ((sig) << 8 | 0x7f)
/* thread_signals_raise(): Raise a signal
This function will raise a signal for a given thread. This function is used
to raise kernel-based signals like SIGKILL. */
extern int thread_signals_raise(struct thread *thread, int sig);
/* thread_signals_deliver_pending(): Deliver all pending signals
This function is KERNEL-ONLY and SHOULD NEVER be called because it is
exclusively reserved for the internal thread scheduler. (see
<gint/thread/scheduler.c> for more information). */
extern int thread_signals_pending_deliver(struct thread *thread);
/* thread_signals_replace(): Replace current signal handler
This function will replace the signum signal handler with the new handler.
This part is used by the "thread_kill()" function. */
extern void (*thread_signals_replace(struct thread *thread,
int signum, void (*handler)(int)))(int);
/* thread_signals_sigreturn(): KERNEL-ONLY: Signals return handler
This function is involved when an custom signals handler come to the end. It
will restore previous context and stack then it will invalidate the current
thread to force the scheudler to not save the current thread context; this
mecanism will restore the previous context. */
extern void thread_signals_sigreturn(void);
//---
// Idle thread interface
//---
/* thread_idle_initialize(): Initialize the idle thread */
extern void thread_idle_init(void);
extern void thread_idle_uninit(void);
/* thread_idle_get(): Return the idle thread address */
extern struct thread *thread_idle_get(void);
//---
// Scheduler interface (kernel-only)
//---
/* thread_sched_initialize(): Initialize the scheduler */
extern void thread_sched_init(void);
extern void thread_sched_uninit(void);
/* thread_sched_start() / thread_sched_stop: Control the scheduler timer */
extern void thread_sched_start(void);
extern void thread_sched_stop(void);
/* thread_sched_add(): thread_sched_remove(): handle internal thread queue */
extern int thread_sched_add(struct thread *thread);
extern int thread_sched_remove(struct thread *thread);
/* thread_sched_find(): Find a thread strcuture using his ID */
extern struct thread *thread_sched_find(thread_t thread);
/* thread_sched_get_current(): Get the current thread context */
extern struct thread *thread_sched_get_current(void);
/* thread_sched_get_counter(): Return the number of thread in the queue */
extern int thread_sched_get_counter(void);
/* thread_sched_invalidate(): Invalidate the current thread.
This function will invalidate the current thread, it means that the current
thread will not be saved on the next schedule. This is really useful for the
signals' management to restore previously saved thread context.
You SHOULD never use it. */
extern void thread_sched_invalidate(void);
//---
// Kernel thread interface
//---
/* thread_kernel_terminate_trampoline(): Termination trampoline code.
This function is automatically involved when a thread return from his main
procedure and will invoke the "thread_exit()" function with the returned
value.
You SHOULD never use it. */
extern void thread_kernel_terminate_trampoline(void);
/* thread_kernel_yield(): Cause the calling thread to relinquish the CPU */
extern void thread_kernel_yield(void);
/* thread_kernel_exit(): Terminate the calling thread */
extern void thread_kernel_exit(void *retval);
#endif /* GINT_THREAD */