67 lines
1.5 KiB
Markdown
67 lines
1.5 KiB
Markdown
|
# utask
|
||
|
|
||
|
utask (micro-task) is an extremely lightweight cooperative multitasking
|
||
|
library. µTasks are simply executed round-robin until each one yields.
|
||
|
|
||
|
By using compiler extensions in the form of synchronization builtins and
|
||
|
carefully constructed clobber lists, utask gets an absolutely minimal context
|
||
|
switch. Instead of saving and restoring all registers every time (on some
|
||
|
architectures this is a lot — a RISC-V with floating point has 63 registers!),
|
||
|
utask simply invalidates all registers before the context switch. This tells
|
||
|
the compiler that after the switch, it is responsible for recomputing the
|
||
|
contents of any registers that had active allocations. This is _much_ faster,
|
||
|
because chances are, you do not have all 63 registers in use at once.
|
||
|
|
||
|
## Supported architectures
|
||
|
|
||
|
- RISC-V
|
||
|
|
||
|
## API example
|
||
|
|
||
|
```c
|
||
|
static void task1_f(uintptr_t arg);
|
||
|
static void task2_f(uintptr_t arg);
|
||
|
|
||
|
utask_decl_stack(task1_s, 1024);
|
||
|
utask_decl_stack(task2_s, 1024);
|
||
|
|
||
|
utask_t task1 = {
|
||
|
.func = task1_f,
|
||
|
.stack = task1_s,
|
||
|
.stack_size = sizeof(task1_s),
|
||
|
.name = "task1",
|
||
|
UTASK_INIT
|
||
|
};
|
||
|
|
||
|
utask_t task2 = {
|
||
|
.func = task2_f,
|
||
|
.stack = task2_s,
|
||
|
.stack_size = sizeof(task2_s),
|
||
|
.name = "task2",
|
||
|
UTASK_INIT
|
||
|
};
|
||
|
|
||
|
int main()
|
||
|
{
|
||
|
utask_add(&task2, 0);
|
||
|
utask_add(&task1, 0);
|
||
|
utask_run();
|
||
|
}
|
||
|
|
||
|
static void task1_f(uintptr_t arg)
|
||
|
{
|
||
|
printf("Hello, ");
|
||
|
utask_yield();
|
||
|
}
|
||
|
|
||
|
static void task2_f(uintptr_t arg)
|
||
|
{
|
||
|
printf("world!\n");
|
||
|
utask_yield();
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Copying
|
||
|
|
||
|
This code may not be used by anybody for any purpose. Be gay, do crime.
|