USched is a preemptive scheduler for x86 using user land POSIX signal API calls
to switch tasks on regular intervals using sigaction(3p)
and
timer_create(3p)
. It not particularly efficient or useful, and not intended
to be a working replacement for pthreads(7)
. Unfortunately the method used to
preempt threads interrupts system calls, resulting in EINTR
errors. Thus any
program using USched must either tediously retry system calls if they are
interrupted, or refrain from using any at all. It does however demonstrate
the basics of software multitasking on x86 without the complexity of a bare
metal kernel. It has only been tested on GNU/Linux
, no guarantees that it
will work on any other *nix system, though, I believe it should.
Management of threads including preemption, creation, and destruction.
USCHED_MAX_THREADS
: the maximum number of possible threads (including the main thread) that USched can handle, attempting to create more threads than this is undefined.USCHED_STACK_NPAGES
: the number of_SC_PAGESIZE
pages (defined inunistd.h
) that will be allocated for a new threads stack.USCHED_STACK_SIZE
: the size in bytes that will be allocated for a new threads stack (defined as(_SC_PAGESIZE * USCHED_STACK_NPAGES)
).
usched_thread_t
(akastruct usched_thread_struct
)- The structure representing a thread.
unsigned long tid
: the unique thread ID.void *stack_end
: the lowest address of the stack, the one returned bymalloc(3)
.void *stack
: the stack pointer at last task switch.void **result
: a pointer to the result as returned by thestart_routine
passed tousched_thread_create
; will beNULL
until the thread exits.
int usched_init ()
- Initializes structures and starts generating timer "interrupts" (by
SIGALRM
) every 20ms. These timer "interrupts" are what preempt threads. - Generates an entry for the "main thread", the one that will receive the first timer interrupt.
- Must be called before any other function and can only be called once.
- Initializes structures and starts generating timer "interrupts" (by
void usched_yeild ()
- Yield the running thread and switch tasks to the next in the queue (which could possibly be the executing thread.
void usched_create (usched_thread_t **thread_ptr, void *(*start_routine) (void *), void *p)
- Allocates and initializes a new thread and stores it in
*thread_ptr
. - Prepares the thread for the first task switch and places in into the
threads
task queue. p
will be passed tostart_routine
when it is called.
- Allocates and initializes a new thread and stores it in
void usched_thread_release (usched_thread_t *thread)
- Deallocates the threads stack, result, and the thread structure itself.
- Should be called after the thread is completed.
atomic_ulong next_tid
- Next thread ID to be assigned to a new
usched_thread_t
upon creation.
- Next thread ID to be assigned to a new
usched_thread_t *threads[]
,size_t cur_thread_index
,usched_thread_t *cur_thread
threads
: array of running threads. ANULL
element represents the lack of a thread, the array in traversed cyclicly by thecur_thread_index
on each task switch.cur_thread_index
the index of the executing thread, incremented byvoid progress_thread_index ()
.cur_thread
the currently executing thread, not progressed byprogress_thread_index
, but instead byusched_yeild
.
void usched_unblock_alarm ()
- Unblocks the
SIGALRM
signal, this normally doesn't need to be done explicitly by the signal handler but is done transparently bysigreturn(2)
(on Linux) or similar.
- Unblocks the
void usched_alarm_action_handler (int signal)
- Signal handler for
SIGALRM
, callsusched_yeild
and unblocksSIGALRM
signals before returning. - Prints
[Caught Alarm clock -> task switch]
tostderr
on every signal handled.
- Signal handler for
void progress_thread_index ()
- Refer to
cur_thread_index
for details.
- Refer to
void *push_n (void *stack, void *src, size_t n)
- Pushes
n
bytes fromsrc
onto thestack
pointer, and returns(stack - n)
.
- Pushes
void usched_thread_init (usched_thread_t *thread)
,void usched_thread_fin (usched_thread_t *thread, void *result)
.usched_thread_init
: Allocates a new thread and stack, initializes all values but does not prepare the stack for task switching.usched_thread_fin
: Allocates and assigns theresult
, removes the thread from thethreads
task queue. Does not deallocate the stack or the thread itself.
__attribute__((noreturn)) void usched_bootstrap (usched_thread_t *thread, void *(*start_routine) (void *), void *p)
- Bootstraps a new thread, this function is called after first task switch.
It unblocks
SIGALRM
which is normally done byusched_yeild
, but not in this case because a new thread wasn't previously preempted.
- Bootstraps a new thread, this function is called after first task switch.
It unblocks
Low-level task switch code for 32-bit x86 on the SysV ABI.
void usched_switch (void **cur_stack, void *next_stack)
- Switch tasks, store the current tasks state in
*cur_stack
and load the next tasks state fromnext_stack
. - The stacks specified by
*cur_stack
andnext_stack
may be the same.
- Switch tasks, store the current tasks state in
void *usched_switch_stack_init (void *stack)
- Prepare the specified stack and return a stack pointer which can be passed
to
usched_switch
asnext_stack
.
- Prepare the specified stack and return a stack pointer which can be passed
to
An example program using USched, demonstrating it's functionality and some of its downsides.