From: Michael D. Lowis Date: Tue, 13 Sep 2022 19:22:26 +0000 (-0400) Subject: added task table and reworked so things are 'mostly' back to working again. Sanitizer... X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=962428a72eb2a13cf227d639597e4d756fbe9f29;p=proto%2Fcerise-os.git added task table and reworked so things are 'mostly' back to working again. Sanitizers say i have data races. will take time to diagnose and fix those --- diff --git a/Kernel.c b/Kernel.c index fc22d9a..cd30388 100644 --- a/Kernel.c +++ b/Kernel.c @@ -17,11 +17,14 @@ #define WAIT_TIMEOUT_MS 100u #define WORD_SIZE sizeof(void*) -#define MAX_TASKS 4194304 +#define MAX_TASKS 1024 +#define TASK_NONE ((TaskID_T)-1) #define WORD_ALIGN(x) \ (((x) + WORD_SIZE) & ~(WORD_SIZE)) +typedef long TaskID_T; + typedef enum { STATE_READY = 0, STATE_RUNNING, @@ -31,10 +34,10 @@ typedef enum { } TaskState_T; typedef struct Task_T { - long* stack_top; // top of task's stack - long* heap_top; // top of task's heap - struct Task_T* next; // pointer to next task - long memory[]; + long* stack_top; + long* heap_top; + long* memory; + struct Task_T* next; } Task_T; typedef struct { @@ -43,9 +46,9 @@ typedef struct { } TaskQueue_T; typedef struct { - Task_T* task; - Task_T* idle; - Task_T* dead; + Task_T idle; + TaskID_T task; + TaskID_T dead; pthread_t thread; } CpuState_T; @@ -54,13 +57,14 @@ typedef struct { ***************************************/ static pthread_mutex_t ScheduleLock; static pthread_cond_t ScheduleCond; +static long CpuCount; static __thread int CpuID = 0; static TaskQueue_T ReadyQueue = {0}; static CpuState_T* Running = NULL; -static int CpuCount; static long NextTask = 0; -static Task_T* Tasks[MAX_TASKS]; +static long TaskCount = 0; +static Task_T Tasks[MAX_TASKS]; /*************************************** Lock and Condition Operations @@ -83,11 +87,14 @@ static void WaitForCondition(pthread_cond_t* cond, pthread_mutex_t* mutex, int m pthread_cond_timedwait(cond, mutex, &expire_time); } -static void WaitForTaskFree(Task_T* prev, Task_T* next) +static void WaitForTaskFree(TaskID_T prev, TaskID_T next) { - if (next && prev != next) + if (next != TASK_NONE && prev != next) { - while (!atomic_load(&next->stack_top)) + Task_T* ntask = &Tasks[next]; + assert(ntask); + + while (!atomic_load(&ntask->stack_top)) { } } @@ -97,8 +104,9 @@ static void WaitForTaskFree(Task_T* prev, Task_T* next) Queue Operations ***************************************/ -static void Enqueue(TaskQueue_T* queue, Task_T* task) +static void Enqueue(TaskQueue_T* queue, TaskID_T tid) { + Task_T* task = &Tasks[tid]; if (queue->tail) { queue->tail->next = task; @@ -112,8 +120,9 @@ static void Enqueue(TaskQueue_T* queue, Task_T* task) } } -static Task_T* Dequeue(TaskQueue_T* queue) +static TaskID_T Dequeue(TaskQueue_T* queue) { + TaskID_T tid = TASK_NONE; Task_T* task = queue->head; if (task) { @@ -123,8 +132,10 @@ static Task_T* Dequeue(TaskQueue_T* queue) { queue->tail = NULL; } + tid = (TaskID_T)(task - Tasks); } - return task; + + return tid; } /*************************************** @@ -132,27 +143,31 @@ static Task_T* Dequeue(TaskQueue_T* queue) ***************************************/ // Should eventually handle prioritization -static void Enter(Task_T* task) +static void Enter(TaskID_T task) { - if (task && task != Running[CpuID].idle) + if (task != TASK_NONE) { Enqueue(&ReadyQueue, task); pthread_cond_signal(&ScheduleCond); } } -static Task_T* Select(void) +static Task_T* LoadNextTask(void) { - Task_T* task = Dequeue(&ReadyQueue); - if (!task) + Task_T* next_task; + TaskID_T task = Dequeue(&ReadyQueue); + assert(task == TASK_NONE || task < MAX_TASKS); + if (task != TASK_NONE) { - task = Running[CpuID].idle; + pthread_cond_signal(&ScheduleCond); + Running[CpuID].task = task; + next_task = &Tasks[task]; } else { - pthread_cond_signal(&ScheduleCond); + next_task = &Running[CpuID].idle; } - return task; + return next_task; } /*************************************** @@ -203,12 +218,21 @@ void PickNewTask(bool dead) the stack top pointer is used as a spinlock to ensure another thread does not start the task before we have saved off our context */ - Task_T* prev = Running[CpuID].task; - Running[CpuID].task = NULL; - atomic_store(&prev->stack_top, NULL); + Task_T* prev_task; + TaskID_T prev = Running[CpuID].task; + Running[CpuID].task = TASK_NONE; + if (prev != TASK_NONE) + { + atomic_store(&Tasks[prev].stack_top, NULL); + prev_task = &Tasks[prev]; + } + else + { + prev_task = &Running[CpuID].idle; + } - /* decide what to do with the task */ + /* decide what to do with the old task */ if (dead) { Running[CpuID].dead = prev; @@ -217,14 +241,19 @@ void PickNewTask(bool dead) { Enter(prev); } - Running[CpuID].task = Select(); + + /* select the next task to run */ + Task_T* next_task = LoadNextTask(); + ReleaseLock(); + WaitForTaskFree(prev, Running[CpuID].task); - SwapTask(prev, Running[CpuID].task); - if (Running[CpuID].dead) + SwapTask(prev_task, next_task); + if (Running[CpuID].dead != TASK_NONE) { - free(Running[CpuID].dead); - Running[CpuID].dead = NULL; + WaitForTaskFree(prev, Running[CpuID].dead); + free(Tasks[Running[CpuID].dead].memory); + Running[CpuID].dead = TASK_NONE; } } @@ -238,7 +267,25 @@ void Kernel_Exit(void) PickNewTask(true); } -static Task_T* CreateTask(void (*task_fn)(void*), void* arg, long int argsz, long int memsz) +static TaskID_T AllocateTaskID(void) +{ + AcquireLock(); + TaskID_T tid = TASK_NONE; + if (TaskCount < MAX_TASKS) + { + TaskCount++; + tid = NextTask++; + } + else + { + /* TODO: iterate over table to find free slot... */ + assert(!"out of task IDs"); + } + ReleaseLock(); + return tid; +} + +static void InitializeTask(Task_T* task, void (*task_fn)(void*), void* arg, long int argsz, long int memsz) { /* allocate a new task. default memory size is used if 0 provided */ if (memsz == 0) @@ -246,14 +293,18 @@ static Task_T* CreateTask(void (*task_fn)(void*), void* arg, long int argsz, lon memsz = 32768; } memsz = WORD_ALIGN(memsz); - Task_T* task = calloc(1, sizeof(Task_T) + memsz); + task->memory = calloc(1, WORD_ALIGN(memsz)); task->heap_top = task->memory; - task->stack_top = (void*)((size_t)task->memory + (size_t)memsz - sizeof(long)); + atomic_store(&task->stack_top, + task->memory + (memsz/WORD_SIZE) - 1); /* copy the argument to the task's heap */ void* newarg = (arg ? task->memory : NULL); - task->heap_top = (void*)((ssize_t)task->heap_top + WORD_ALIGN(argsz)); - memcpy(newarg, arg, argsz); + task->heap_top = task->heap_top + (WORD_ALIGN(argsz)/WORD_SIZE); + if (newarg && argsz) + { + memcpy(newarg, arg, argsz); + } /* populate the initial context on the stack so SwapTasks works */ *(--task->stack_top) = (long)Kernel_Exit; // coroutine cleanup @@ -263,20 +314,24 @@ static Task_T* CreateTask(void (*task_fn)(void*), void* arg, long int argsz, lon { *(--task->stack_top) = 0xdeadbeef; // initial values for saved registers } - - return task; } -void Kernel_Spawn(void (*task_fn)(void*), void* arg, long int argsz, long int memsz) +TaskID_T Kernel_Spawn(void (*task_fn)(void*), void* arg, long int argsz, long int memsz) { - Task_T* task = CreateTask(task_fn, arg, argsz, memsz); - AcquireLock(); - Enter(task); - ReleaseLock(); + TaskID_T tid = AllocateTaskID(); + if (tid != TASK_NONE) + { + InitializeTask(&Tasks[tid], task_fn, arg, argsz, memsz); + AcquireLock(); + Enter(tid); + ReleaseLock(); + } + return tid; } static void CpuIdle(void* arg) { + (void)arg; while(1) { Kernel_Yield(); @@ -289,9 +344,9 @@ static void CpuIdle(void* arg) static void* CpuMain(void* arg) { CpuID = (long int)arg; - Running[CpuID].idle = CreateTask(CpuIdle, 0, 0, 0); - Running[CpuID].task = Running[CpuID].idle; - StartTask(Running[CpuID].idle); + Running[CpuID].task = TASK_NONE; + InitializeTask(&Running[CpuID].idle, CpuIdle, 0, 0, 0); + StartTask(&Running[CpuID].idle); return NULL; /* unreachable */ } @@ -300,6 +355,7 @@ void Kernel(void) pthread_mutex_init(&ScheduleLock, 0); pthread_cond_init(&ScheduleCond, NULL); CpuCount = sysconf(_SC_NPROCESSORS_ONLN); +// CpuCount = 1; Running = calloc(CpuCount, sizeof(CpuState_T)); for (long int i = 0; i < CpuCount; i++) { @@ -318,7 +374,7 @@ void Kernel_Run(void) bool done = true; for (long int i = 0; i < CpuCount; i++) { - done = done && (Running[i].task == Running[i].idle); + done = done && (Running[i].task == TASK_NONE); } if (done) { @@ -329,15 +385,15 @@ void Kernel_Run(void) } } -void* Kernel_Allocate(size_t sz) +void* Kernel_Allocate(long sz) { - Task_T* task = Running[CpuID].task; + Task_T* task = &Tasks[Running[CpuID].task]; task->stack_top = &(long){0}; sz = WORD_ALIGN(sz); - ssize_t free_sz = (ssize_t)task->stack_top - (ssize_t)task->heap_top; + long free_sz = (long)task->stack_top - (long)task->heap_top; assert(sz <= free_sz); void* ptr = task->heap_top; - task->heap_top = (void*)((ssize_t)task->heap_top + sz); + task->heap_top = task->heap_top + (sz / WORD_SIZE); return ptr; } @@ -375,13 +431,10 @@ int main(int argc, char** argv) (void)argv; Kernel(); - for (int i = 0; i < 100; i++) { Kernel_Spawn(task1, &i, sizeof(int), 0); } - - /* wait for all jobs to be done */ Kernel_Run(); return 0; diff --git a/Kernel.h b/Kernel.h index 07b694f..4081e1e 100644 --- a/Kernel.h +++ b/Kernel.h @@ -1,3 +1,7 @@ +//#define throws_error __attribute__((warn_unused_result)) long +//#define throw(val) return (val); +//#define try(expr) + void Kernel_Yield(void); void Kernel_Exit(void); void Kernel_Spawn(void (*task_fn)(void*), void* arg, int stacksize); diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..7db3dbd --- /dev/null +++ b/build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +cc -g -Wall -Wextra -Werror -pedantic --std=c11 -fsanitize=undefined -fsanitize=thread Kernel.c && ./a.out \ No newline at end of file