tinyboy-emulator/co/task.c
2024-07-16 16:47:13 +09:00

254 lines
3.9 KiB
C

#include "taskimpl.h"
#include <stdio.h>
int taskcount;
int taskidgen;
int tasknswitch;
int taskexitval;
Task* taskrunning;
ucontext_t taskschedcontext;
Tasklist taskrunqueue;
Task** alltask;
int nalltask;
static void
contextswitch(ucontext_t* from, ucontext_t* to);
void
assertstack(uint n);
static void
taskinfo(int s)
{
int i;
Task* t;
char* extra;
for (i = 0; i < nalltask; ++i) {
t = alltask[i];
if (t == taskrunning)
extra = " (running)";
else if (t->ready)
extra = " (ready)";
else
extra = "";
printf("%s\n", extra);
}
}
static void
taskstart(uint x, uint y)
{
Task* t;
ulong z;
z = x << 16;
z <<= 16;
z |= y;
t = (Task*)z;
t->startfn(t->startarg);
taskexit(0);
}
static Task*
taskalloc(void (*fn)(void*), void* arg, uint stk)
{
Task* t;
sigset_t zero;
uint x, y;
ulong z;
if ((t = malloc(sizeof *t + stk)) == nil) {
exit(1);
}
*t = (Task){
.stk = (uchar*)(&t[1]),
.stksize = stk,
.id = ++taskidgen,
.startfn = fn,
.startarg = arg,
};
sigemptyset(&zero);
sigprocmask(SIG_BLOCK, &zero, &t->uc.uc_sigmask);
if (getcontext(&t->uc)) {
exit(1);
}
t->uc.uc_stack.ss_sp = t->stk + 8;
t->uc.uc_stack.ss_size = t->stksize - 64;
z = (ulong)t;
y = z;
z >>= 16;
x = z >> 16;
makecontext(&t->uc, (void (*)())taskstart, 2, x, y);
return t;
}
int
taskcreate(void (*fn)(void*), void* arg, uint stk)
{
int id;
Task* t;
t = taskalloc(fn, arg, stk);
taskcount++;
id = t->id;
if (nalltask % 64 == 0) {
alltask = realloc(alltask, (nalltask + 64) * sizeof(alltask[0]));
if (alltask == 0) {
exit(1);
}
}
t->alltaskslot = nalltask;
alltask[nalltask++] = t;
taskready(t);
return id;
}
void
taskready(Task* t)
{
t->ready = 1;
addtask(&taskrunqueue, t);
}
void
taskswitch(void)
{
assertstack(0);
contextswitch(&taskrunning->uc, &taskschedcontext);
}
int
taskyield(void)
{
int n;
n = tasknswitch;
taskready(taskrunning);
taskswitch();
return tasknswitch - n - 1;
}
void
taskexit(int val)
{
taskexitval = val;
taskrunning->exiting = 1;
taskswitch();
}
void
taskexitall(int val)
{
exit(val);
}
void
deltask(Tasklist* l, Task* t)
{
if (t->prev)
t->prev->next = t->next;
else
l->head = t->next;
if (t->next)
t->next->prev = t->prev;
else
l->tail = t->prev;
}
void
addtask(Tasklist* l, Task* t)
{
if (l->tail) {
l->tail->next = t;
t->prev = l->tail;
} else {
l->head = t;
t->prev = nil;
}
l->tail = t;
t->next = nil;
}
static void
contextswitch(ucontext_t* from, ucontext_t* to)
{
if (swapcontext(from, to) < 0) {
printf("swapcontext is fail\n");
exit(1);
}
}
void
assertstack(uint n)
{
Task* t;
t = taskrunning;
if ((uchar*)&t <= (uchar*)t->stk || (uchar*)&t - (uchar*)t->stk < 256 + n) {
/* satck over flow */
exit(1);
}
}
static void
taskscheduler(void)
{
int i;
Task* t;
for (;;) {
if (taskcount == 0)
exit(taskexitval);
t = taskrunqueue.head;
if (t == nil) {
/* nothing to do */
exit(1);
}
deltask(&taskrunqueue, t); /* delete from runqueue */
t->ready = 0;
taskrunning = t;
tasknswitch++;
contextswitch(&taskschedcontext, &t->uc);
taskrunning = nil; /* ready for next task */
if (t->exiting) {
taskcount--;
i = t->alltaskslot;
alltask[i] = alltask[--nalltask];
alltask[i]->alltaskslot = i;
free(t);
}
}
}
static char* argv0;
static int taskargc;
static char** taskargv;
int mainstacksize;
static void
taskmainstart(void* v)
{
taskmain(taskargc, taskargv);
}
int
main(int argc, char** argv)
{
struct sigaction sa, osa;
memset(&sa, 0, sizeof(sigaction));
sa.sa_handler = taskinfo;
sa.sa_flags = SA_RESTART;
sigaction(SIGQUIT, &sa, &osa);
/*sigaction(SIGINFO, &sa, &osa);*/
argv0 = argv[0];
taskargc = argc;
taskargv = argv;
mainstacksize = 256 * 1024;
taskcreate(taskmainstart, nil, mainstacksize);
taskscheduler();
exit(0);
}