* allow exception when atomic operation is used !
* Add documentation
This commit is contained in:
Yatis 2021-01-03 20:58:49 +01:00
parent 6399ed6f40
commit 413c2b57f2
9 changed files with 177 additions and 87 deletions

View File

@ -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 */

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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)
{

View File

@ -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)
{