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

566 lines
19 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 part is CUSTOM to the Gint kernel, it will allow, when the thread with
this attibute die, instead of release its memories and been removed from the
scheduler, to perform a "longjmp()" and returns ("rewinds") to the
"thread_create()" function which will return the special THREAD_ATTR_REWIND
and all child thread of the thread will be killed (using signals).
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
the destructors or to recall the main function again. All this while allowing
(potential) threaded driver 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. */
extern int thread_attr_destroy(thread_attr_t *attr);
//---
// Signals interface
//---
/* 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 signal are not currently supported but will be in near futur) */
#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 specrial 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 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 */
//TODO: all hardware context !
//TODO: signals context !
//TODO: signals mask !
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; /* potential next process */
} 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 */
extern int thread_create(thread_t *thread,
thread_attr_t *attr, void *function, ...);
/* 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).
*/
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(): Kill a thread
This function will destroy a thread without care about its attributes. The
thread SHOUL NEVER be used anymore after this function.
This function is used by the scheduler to destroy thread from its queue
before removing it. */
extern int thread_kill(thread_t thread, int sig);
/* TODO doc */
extern void (*thread_signal(int signum, void (*handler)(int)))(int);
/* KERNEL-ONLY TODO Doc */
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)
//---
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 SOULD 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(): TODO */
extern void (*thread_signals_replace(struct thread *thread,
int signum, void (*handler)(int)))(int);
/* thread_signals_sigreturn(): TODO */
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
//---
extern void thread_sched_init(void);
extern void thread_sched_uninit(void);
/* thread_sched_start(): */
extern void thread_sched_start(void);
extern void thread_sched_stop(void);
extern int thread_sched_add(struct thread *thread);
extern int thread_sched_remove(struct thread *thread);
extern struct thread *thread_sched_find(thread_t thread);
extern struct thread *thread_sched_get_current(void);
extern int thread_sched_get_counter(void);
extern void thread_sched_invalidate(void);
//---
// Kernel thread interface
//---
/* thread_kernel_terminate_trampoline(): Termination trampiline code.
This function will invoke the "thread_exit()" function with 0. This function
is automatically involved when a thread return from his main procedure and
SHOULD NOT be called. */
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 */