| .. | ||
| Makefile | ||
| README.md | ||
| taskswitch.c | ||
| taskswitch.h | ||
taskswitch -- Cooperative Task Switching Library
Cooperative (non-preemptive) multitasking library for DJGPP/DPMI (DOS
protected mode). Built as libtasks.lib -- a DXE3 module loaded by
the DVX loader.
Why Cooperative
DOS is single-threaded with no kernel scheduler. DPMI provides no thread or timer-based preemption. The DVX GUI event model is inherently single-threaded (one compositor, one input queue, one window stack). Cooperative switching lets each task yield at safe points, avoiding the need for synchronization primitives entirely.
Scheduling Algorithm
Credit-based weighted fair-share, round-robin within a credit epoch.
Each task receives (priority + 1) credits per scheduling round.
Tasks run round-robin, consuming one credit per turn. When all
credits are spent, every ready task is refilled.
| Priority | Constant | Credits/Round | Relative Share |
|---|---|---|---|
| 0 | TS_PRIORITY_LOW |
1 | ~4% |
| 5 | TS_PRIORITY_NORMAL |
6 | ~22% |
| 10 | TS_PRIORITY_HIGH |
11 | ~41% |
Higher-priority tasks run proportionally more often but never starve lower ones. A priority-10 task gets 11 turns per round while a priority-0 task gets 1.
Task States
| State | Description |
|---|---|
TaskStateReady |
Eligible for scheduling |
TaskStateRunning |
Currently executing (cosmetic marker) |
TaskStatePaused |
Skipped until resumed |
TaskStateTerminated |
Slot available for reuse |
API Reference
| Function | Description |
|---|---|
tsInit() |
Initialize task system; calling context becomes task 0 (main) |
tsShutdown() |
Shut down and free all resources |
tsCreate(name, entry, arg, stackSize, priority) |
Create a new task; returns task ID |
tsYield() |
Yield CPU to next eligible task (credit-based round-robin) |
tsPause(taskId) |
Pause a task (implicit yield if self) |
tsResume(taskId) |
Resume a paused task (refills credits) |
tsSetPriority(taskId, priority) |
Set priority (refills credits immediately) |
tsGetPriority(taskId) |
Get task priority |
tsGetState(taskId) |
Get task state |
tsCurrentId() |
Get currently running task's ID |
tsGetName(taskId) |
Get task name |
tsExit() |
Terminate calling task (never returns) |
tsKill(taskId) |
Forcibly terminate another task |
tsRecoverToMain() |
Force scheduler back to task 0 after crash longjmp |
tsActiveCount() |
Count of non-terminated tasks |
Error Codes
| Constant | Value | Description |
|---|---|---|
TS_OK |
0 | Success |
TS_ERR_INIT |
-1 | Initialization failure |
TS_ERR_PARAM |
-2 | Invalid parameter |
TS_ERR_FULL |
-3 | No available task slots |
TS_ERR_NOMEM |
-4 | Stack allocation failed |
TS_ERR_STATE |
-5 | Invalid state transition |
Constants
| Constant | Value | Description |
|---|---|---|
TS_DEFAULT_STACK_SIZE |
32768 | Default stack size (32KB) |
TS_NAME_MAX |
32 | Maximum task name length |
Task 0 (Main Task)
Task 0 is special:
- Cannot be killed or paused
- Uses the process stack directly (no separate allocation)
tsRecoverToMain()always returns control here after a crash- The DVX shell runs as task 0 with
TS_PRIORITY_HIGH
Crash Recovery
tsRecoverToMain() is called after longjmp from a signal handler
that fired in a non-main task. It fixes the scheduler's currentIdx
to point back to task 0. The crashed task is NOT cleaned up by this
call -- tsKill() must be called separately afterward.
Files
| File | Description |
|---|---|
taskswitch.h |
Public API header |
taskswitch.c |
Complete implementation (context switch, scheduler, stack management) |
Makefile |
Builds bin/libs/libtasks.lib |
Build
make # builds bin/libs/libtasks.lib
make clean # removes objects and library
No dependencies. Exports all symbols matching _ts*.