#define WORD_ALIGN(x) (((x) + WORD_SIZE) & ~(WORD_SIZE))
typedef struct Task_T {
- volatile long* stack_top; // top of tasks's stack
- long* stack_base; // allocated memory for stack
- struct Task_T* next; // pointer to next task
- long* heap_beg;
- long* heap_ptr;
- long* heap_end;
+ 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[];
} Task_T;
typedef struct {
typedef struct {
Task_T* task;
Task_T* idle;
+ Task_T* dead;
pthread_t thread;
} CpuState_T;
static pthread_cond_t ScheduleCond;
static __thread int CpuID = 0;
static TaskQueue_T ReadyQueue = {0};
-static TaskQueue_T DeadQueue = {0};
static CpuState_T* Running = NULL;
static int CpuCount;
" ret\n"
);
-void PickNewTask(bool requeue)
+void PickNewTask(bool dead)
{
AcquireLock();
+
+ /* unload the current task, and clear the stack top.
+ 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);
- if (requeue)
+
+
+ /* decide what to do with the task */
+ if (dead)
+ {
+ Running[CpuID].dead = prev;
+ }
+ else
{
Enter(prev);
}
ReleaseLock();
WaitForTaskFree(prev, Running[CpuID].task);
SwapTask(prev, Running[CpuID].task);
+ if (Running[CpuID].dead)
+ {
+ free(Running[CpuID].dead);
+ Running[CpuID].dead = NULL;
+ }
}
void Kernel_Yield(void)
-{
- PickNewTask(true);
-}
-
-void Kernel_Exit(void)
{
PickNewTask(false);
}
-static void* TaskAllocate(Task_T* task, size_t sz)
+void Kernel_Exit(void)
{
- sz = WORD_ALIGN(sz);
- size_t free_sz = (size_t)task->heap_end - (size_t)task->heap_ptr;
- assert(sz <= free_sz);
- void* ptr = task->heap_ptr;
- task->heap_ptr = (void*)((size_t)task->heap_end + sz);
- return ptr;
+ PickNewTask(true);
}
-static Task_T* CreateTask(void (*task_fn)(void*), void* arg, long int argsz, long int stacksz, long int heapsz)
+static Task_T* CreateTask(void (*task_fn)(void*), void* arg, long int argsz, long int memsz)
{
- AcquireLock();
- Task_T* task = Dequeue(&DeadQueue);
- WaitForTaskFree(NULL, task);
- ReleaseLock();
- if (!task)
+ /* allocate a new task. default memory size is used if 0 provided */
+ if (memsz == 0)
{
- task = calloc(1, sizeof(Task_T));
+ memsz = 32768;
}
-
- /* create the task's heap and copy the argument onto the tasks heap */
- task->heap_beg = malloc(WORD_ALIGN(heapsz));
- task->heap_ptr = task->heap_beg;
- task->heap_end = (void*)((size_t)task->heap_beg + heapsz);
- void* newarg = TaskAllocate(task, argsz);
+ memsz = WORD_ALIGN(memsz);
+ Task_T* task = calloc(1, sizeof(Task_T) + memsz);
+ task->heap_top = task->memory;
+ task->stack_top = (void*)((size_t)task->memory + (size_t)memsz - sizeof(long));
+
+ /* 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);
- /* allocate stack (default size of 32768) */
- if (stacksz == 0)
- {
- stacksz = 32768;
- }
- task->stack_base = realloc(task->stack_base, stacksz);
-
/* populate the initial context on the stack so SwapTasks works */
- task->stack_top = &task->stack_base[stacksz / sizeof(long)-1]; // top of stack
*(--task->stack_top) = (long)Kernel_Exit; // coroutine cleanup
*(--task->stack_top) = (long)task_fn; // user's function to run (rop style!)
*(--task->stack_top) = (long)newarg; // user's function argument (rdi)
return task;
}
-void Kernel_Spawn(void (*task_fn)(void*), void* arg, long int argsz, long int stacksz, long int heapsz)
+void Kernel_Spawn(void (*task_fn)(void*), void* arg, long int argsz, long int memsz)
{
- Task_T* task = CreateTask(task_fn, arg, argsz, stacksz, heapsz);
+ Task_T* task = CreateTask(task_fn, arg, argsz, memsz);
AcquireLock();
Enter(task);
ReleaseLock();
static void* CpuMain(void* arg)
{
CpuID = (long int)arg;
- Running[CpuID].idle = CreateTask(CpuIdle, 0, 0, 0, 0);
+ Running[CpuID].idle = CreateTask(CpuIdle, 0, 0, 0);
Running[CpuID].task = Running[CpuID].idle;
StartTask(Running[CpuID].idle);
return NULL; /* unreachable */
}
}
+void* Kernel_Allocate(size_t sz)
+{
+ Task_T* task = 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;
+ assert(sz <= free_sz);
+ void* ptr = task->heap_top;
+ task->heap_top = (void*)((ssize_t)task->heap_top + sz);
+ return ptr;
+}
+
/***************************************
Main Routine
***************************************/
for (int i = 0; i < 100; i++)
{
- int* val = malloc(sizeof(int));
- *val = i;
- Kernel_Spawn(task1, &i, sizeof(int), 0, sizeof(int));
+ Kernel_Spawn(task1, &i, sizeof(int), 0);
}
/* wait for all jobs to be done */