forked from Lephenixnoir/gint
566 lines
19 KiB
C
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 */
|