forked from Lephenixnoir/gint
Thread:
* allow exception when atomic operation is used ! * Add documentation
This commit is contained in:
parent
6399ed6f40
commit
413c2b57f2
|
@ -92,17 +92,18 @@ 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).
|
||||
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
|
||||
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.
|
||||
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
|
||||
|
@ -119,12 +120,16 @@ 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
|
||||
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. */
|
||||
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);
|
||||
|
||||
|
||||
|
@ -132,6 +137,21 @@ 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;
|
||||
|
@ -148,7 +168,7 @@ typedef void (*sighandler_t)(int);
|
|||
#define NSIG 19
|
||||
|
||||
/* Define all signum
|
||||
(note: all signal are not currently supported but will be in near futur) */
|
||||
(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. */
|
||||
|
@ -175,15 +195,14 @@ typedef void (*sighandler_t)(int);
|
|||
//---
|
||||
// User thread interface
|
||||
//---
|
||||
/* define specrial return value with the thread_create() function */
|
||||
/* 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 context definition */
|
||||
struct cpu_ctx
|
||||
{
|
||||
/* cpu_ctx: whole SH3-based CPU hardware context definition */
|
||||
struct cpu_ctx {
|
||||
uint32_t reg[16];
|
||||
uint32_t gbr;
|
||||
uint32_t macl;
|
||||
|
@ -194,9 +213,6 @@ struct cpu_ctx
|
|||
};
|
||||
|
||||
/* struct thread: Thread structure definition */
|
||||
//TODO: all hardware context !
|
||||
//TODO: signals context !
|
||||
//TODO: signals mask !
|
||||
struct thread {
|
||||
/* hardware context */
|
||||
struct cpu_ctx context;
|
||||
|
@ -225,7 +241,7 @@ struct thread {
|
|||
thread_t id;
|
||||
|
||||
/* hierarchical information */
|
||||
struct thread *next; /* potential next process */
|
||||
struct thread *next;
|
||||
} scheduler;
|
||||
|
||||
/* private information */
|
||||
|
@ -239,9 +255,38 @@ struct thread {
|
|||
} private;
|
||||
};
|
||||
|
||||
/* thread_create(): Create a new thread */
|
||||
/* 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 *function, ...);
|
||||
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 */
|
||||
|
@ -264,7 +309,14 @@ extern int thread_create(thread_t *thread,
|
|||
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);
|
||||
|
||||
|
@ -284,23 +336,53 @@ extern int thread_tryjoin(thread_t thread, void **retval);
|
|||
variables, semaphores, and file descriptors) are not released. */
|
||||
extern void thread_exit(void *retval);
|
||||
|
||||
/* thread_kill(): Kill a thread
|
||||
/* thread_kill(): Send signal to a thread
|
||||
|
||||
This function will destroy a thread without care about its attributes. The
|
||||
thread SHOUL NEVER be used anymore after this function.
|
||||
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.
|
||||
|
||||
This function is used by the scheduler to destroy thread from its queue
|
||||
before removing it. */
|
||||
@return:
|
||||
* negative value if error occurs
|
||||
* 0 if success */
|
||||
extern int thread_kill(thread_t thread, int sig);
|
||||
|
||||
/* TODO doc */
|
||||
/* 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);
|
||||
|
||||
/* KERNEL-ONLY TODO Doc */
|
||||
/* 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
|
||||
//
|
||||
|
@ -477,7 +559,7 @@ extern uint32_t thread_atomic_stop(void);
|
|||
|
||||
|
||||
//---
|
||||
// Signals management (kernel)
|
||||
// Signals management (kernel-level)
|
||||
//---
|
||||
enum {
|
||||
thread_signals_deliver_retval_running = 0,
|
||||
|
@ -497,16 +579,24 @@ 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
|
||||
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(): TODO */
|
||||
/* 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(): TODO */
|
||||
/* 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);
|
||||
|
||||
|
||||
|
@ -525,21 +615,36 @@ extern struct thread *thread_idle_get(void);
|
|||
|
||||
|
||||
//---
|
||||
// Scheduler interface
|
||||
// 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_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);
|
||||
|
||||
|
||||
|
@ -549,11 +654,13 @@ extern void thread_sched_invalidate(void);
|
|||
// Kernel thread interface
|
||||
//---
|
||||
|
||||
/* thread_kernel_terminate_trampoline(): Termination trampiline code.
|
||||
/* thread_kernel_terminate_trampoline(): Termination trampoline 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. */
|
||||
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 */
|
||||
|
|
|
@ -187,7 +187,7 @@ int start(int isappli, int optnum)
|
|||
use the "classic" way to do the job.
|
||||
|
||||
@note
|
||||
When we return from the thread_create() function with the longjmp()
|
||||
When we return from the "thread_create()" function with the longjmp()
|
||||
feature, we are always in a thread execution (the scheduler always
|
||||
performs context switch) */
|
||||
int rc = -1;
|
||||
|
@ -199,13 +199,8 @@ int start(int isappli, int optnum)
|
|||
int retval = thread_create(&thread, &attr, &main, isappli, optnum);
|
||||
if (retval == 0)
|
||||
thread_join(thread, (void**)&rc);
|
||||
//if (retval != 0 && (uint32_t)retval != THREAD_CREATE_LONGJMP_RETURN)
|
||||
// rc = main(isappli, optnum);
|
||||
|
||||
dclear(C_BLACK);
|
||||
dtext(0, 0, C_WHITE, "longjmp, success !");
|
||||
dupdate();
|
||||
while (1);
|
||||
if (retval != 0 && (uint32_t)retval != THREAD_CREATE_LONGJMP_RETURN)
|
||||
rc = main(isappli, optnum);
|
||||
|
||||
/* main loop */
|
||||
while (gint_restart == 0) {
|
||||
|
|
|
@ -70,7 +70,7 @@ atomic_end_exit:
|
|||
.align 4
|
||||
thread_atomic_counter: .long _thread_atomic_counter
|
||||
thread_atomic_sr_save: .long _thread_atomic_sr_save
|
||||
sr_mask: .long 0x100000f0
|
||||
sr_mask: .long 0x000000f0
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <gint/thread.h>
|
||||
#include <gint/std/string.h>
|
||||
|
||||
/* define private sombols */
|
||||
/* define private symbols */
|
||||
static struct thread thread_idle;
|
||||
static uint32_t thread_idle_stack[THREAD_IDLE_STACK_SIZE];
|
||||
|
||||
|
@ -29,12 +29,11 @@ static void thread_idle_code(void)
|
|||
void thread_idle_init(void)
|
||||
{
|
||||
memset(&thread_idle, 0x00, sizeof(struct thread));
|
||||
thread_idle.context.reg[15] = (uintptr_t)&thread_idle_stack;
|
||||
thread_idle.context.reg[15] = (uintptr_t)thread_idle_stack;
|
||||
thread_idle.context.reg[15] += (THREAD_IDLE_STACK_SIZE << 2);
|
||||
thread_idle.context.spc = (uintptr_t)&thread_idle_code;
|
||||
thread_idle.context.pr = (uintptr_t)0xa0000000;
|
||||
}
|
||||
|
||||
void thread_idle_uninit(void)
|
||||
{
|
||||
return;
|
||||
|
@ -46,6 +45,7 @@ void thread_idle_uninit(void)
|
|||
//---
|
||||
// User interface
|
||||
//---
|
||||
/* thread_idle_get(): Return the idle thread */
|
||||
struct thread *thread_idle_get(void)
|
||||
{
|
||||
return (&thread_idle);
|
||||
|
|
|
@ -29,11 +29,11 @@
|
|||
a common subroutine, PR register has been saved into the R0 register and the
|
||||
R15 register has been saved into the R1 register.
|
||||
|
||||
We are always with the "interrupt" bank register configuration. So, we only
|
||||
We are always with the "privileged" bank register configuration. So, we only
|
||||
can use r0~r7 register until the current thread context has been saved.
|
||||
|
||||
When involved, R0 content the PR register snapshot when interrupts occur,
|
||||
same for the R1 register which content the stack snapshot. (Yes, I knewn that
|
||||
same for the R1 register which content the stack snapshot. (Yes, I know that
|
||||
the SH4-based MPU provide the SGR register which saves the stack snapshot
|
||||
when interrupts / exceptions occur, but we need to be SH3 compatible) */
|
||||
_thread_kernel_sched_interrupt_procedure:
|
||||
|
@ -277,6 +277,7 @@ intevt_register: .long 0xff000028
|
|||
|
||||
This function will move the calling thread to the end of the queue for its
|
||||
static priority and new thread gets on run. */
|
||||
/* TODO preemption ? */
|
||||
_thread_kernel_yield:
|
||||
/* start atomic operation + bank switch*/
|
||||
stc sr, r0
|
||||
|
@ -317,13 +318,8 @@ pouet:
|
|||
.global _thread_kernel_terminate_trampoline
|
||||
.type _thread_kernel_terminate_trampoline, @function
|
||||
|
||||
.global _thread_kernel_exit
|
||||
.type _thread_kernel_exit, @function
|
||||
|
||||
|
||||
/*
|
||||
r0 - SR backup
|
||||
*/
|
||||
/* thread_kernel_terminate_trampoline()
|
||||
call the thread_exit function with the returned value.*/
|
||||
_thread_kernel_terminate_trampoline:
|
||||
mov.l 2f, r1
|
||||
jmp @r1
|
||||
|
|
|
@ -102,7 +102,7 @@ int thread_mutex_lock(thread_mutex_t *mutex)
|
|||
break;
|
||||
};
|
||||
|
||||
/* Wait next schedule */
|
||||
/* Force schedule */
|
||||
thread_kernel_yield();
|
||||
}
|
||||
|
||||
|
@ -159,7 +159,7 @@ int thread_mutex_timedlock(thread_mutex_t *mutex, uint64_t delay_us)
|
|||
break;
|
||||
};
|
||||
|
||||
/* Wait next schedule */
|
||||
/* Force schedule */
|
||||
thread_kernel_yield();
|
||||
}
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ int thread_sched_remove(struct thread *thread)
|
|||
break;
|
||||
parent = &(*parent)->scheduler.next;
|
||||
}
|
||||
if (parent == NULL) {
|
||||
if (*parent == NULL) {
|
||||
thread_atomic_stop();
|
||||
return (-1);
|
||||
}
|
||||
|
|
|
@ -72,13 +72,7 @@ static int thread_signals_deliver(struct thread *thread, int sig)
|
|||
//---
|
||||
// Kernel interface
|
||||
//---
|
||||
/* thread_signals_raise(): Tricky part to handle signals
|
||||
|
||||
@return:
|
||||
* 1 Cannot be scheduled
|
||||
* 0 Can be scheduled
|
||||
* -1 The thread SHOULD be removed !
|
||||
*/
|
||||
/* thread_signals_raise():Raise a signal */
|
||||
int thread_signals_raise(struct thread *thread, int sig)
|
||||
{
|
||||
if (sig >= NSIG)
|
||||
|
@ -95,13 +89,7 @@ int thread_signals_raise(struct thread *thread, int sig)
|
|||
return (0);
|
||||
}
|
||||
|
||||
/* thread_signals_deliver_pending(): Deliver all pending signals
|
||||
|
||||
@return
|
||||
* 0 - can be scheduled
|
||||
* negative value - should not be scheduled
|
||||
*/
|
||||
#include <gint/display.h>
|
||||
/* thread_signals_deliver_pending(): Deliver pending signals */
|
||||
int thread_signals_pending_deliver(struct thread *thread)
|
||||
{
|
||||
sigset_t sig;
|
||||
|
@ -125,13 +113,13 @@ int thread_signals_pending_deliver(struct thread *thread)
|
|||
return (retval);
|
||||
}
|
||||
|
||||
/* thread_signals_sigreturn(): TODO */
|
||||
/* thread_signals_sigreturn(): Signals return handler */
|
||||
void thread_signals_sigreturn(void)
|
||||
{
|
||||
struct thread *thread;
|
||||
void *stack;
|
||||
|
||||
/* thread */
|
||||
/* get the current thread */
|
||||
thread_atomic_start();
|
||||
thread = thread_sched_get_current();
|
||||
if (thread == NULL) {
|
||||
|
@ -145,16 +133,19 @@ void thread_signals_sigreturn(void)
|
|||
|
||||
/* restore stack */
|
||||
thread->context.reg[15] += (sizeof(struct cpu_ctx) + 3) >> 2 << 2;
|
||||
|
||||
/* Invalidate the thread to force the scheduler to not save the current
|
||||
context otherwise, the restored context will be overwritten. */
|
||||
thread_sched_invalidate();
|
||||
|
||||
/* force schedule */
|
||||
/* force schedule, on the next schedule, we will return to the job */
|
||||
thread_atomic_stop();
|
||||
while (1) {
|
||||
thread_kernel_yield();
|
||||
}
|
||||
}
|
||||
|
||||
/* thread_signals_replace(): TODO */
|
||||
/* thread_signals_replace(): Replace current signal handler */
|
||||
void (*thread_signals_replace(struct thread *thread,
|
||||
int signum, void (*handler)(int)))(int)
|
||||
{
|
||||
|
|
|
@ -19,11 +19,10 @@
|
|||
#if 0
|
||||
/* thread_kill_child()
|
||||
|
||||
If the THREAD_ATTR_LONGJMP_ENABLE is set, this function is involved when the
|
||||
thread is created. If the "longjmp()" feature is set then the "setjmp()"
|
||||
function will return with an retval that differ from 0. In this case, it's
|
||||
signifiacte that the created thread just died. So, we should kill all child
|
||||
thread generated by him */
|
||||
If the THREAD_ATTR_MAIN_THREAD is set, this function is involved when the
|
||||
thread die. This function will kill all child thread generated by the parent
|
||||
*/
|
||||
/* TODO */
|
||||
static void thread_kill_child(struct thread **thread)
|
||||
{
|
||||
/* walking */
|
||||
|
@ -205,7 +204,8 @@ void thread_exit(void *retval)
|
|||
}
|
||||
}
|
||||
|
||||
/* thread_kill(): Destroy the thread */
|
||||
/* thread_kill(): Send signals */
|
||||
/* TODO: hierarchical handling ! */
|
||||
int thread_kill(thread_t id, int sig)
|
||||
{
|
||||
struct thread *thread;
|
||||
|
@ -242,6 +242,7 @@ void (*thread_signal(int signum, void (*handler)(int)))(int)
|
|||
//---
|
||||
// Kernel interface
|
||||
//---
|
||||
|
||||
/* thread_terminate(): Terminate a thread */
|
||||
int thread_terminate(struct thread *thread)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue