DVX_GUI/tasks/libtasks.dhs

261 lines
7.6 KiB
Text

.section Libraries
.topic libtasks
.title libtasks -- Cooperative Task Switching
.toc 0 libtasks -- Cooperative Task Switching
.index libtasks
.index Task Switching
.index Cooperative Multitasking
.index taskswitch.h
.h1 libtasks -- Cooperative Task Switching
Credit-based cooperative (non-preemptive) multitasking library for DOS protected mode (DJGPP/DPMI). Each task receives (priority + 1) credits per scheduling round. Tasks run round-robin, consuming one credit per turn. When all credits are exhausted, every ready task is refilled. Higher-priority tasks run proportionally more often but never starve lower ones.
Header: tasks/taskswitch.h
.h2 Why Cooperative
DOS is single-threaded with no kernel scheduler. DPMI provides no preemption primitives. 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 synchronization primitives entirely.
.h2 Error Codes
.table
Constant Value Description
-------- ----- -----------
TS_OK 0 Success
TS_ERR_INIT -1 Task system not initialized
TS_ERR_PARAM -2 Invalid parameter (bad task ID, etc.)
TS_ERR_FULL -3 Task array full (should not occur; array grows)
TS_ERR_NOMEM -4 Memory allocation failed
TS_ERR_STATE -5 Invalid state transition
.endtable
.h2 Constants
.table
Constant Value Description
-------- ----- -----------
TS_DEFAULT_STACK_SIZE 32768 Default stack size per task (32 KB)
TS_NAME_MAX 32 Maximum task name length including NUL
TS_PRIORITY_LOW 0 Low priority (1 credit per round)
TS_PRIORITY_NORMAL 5 Normal priority (6 credits per round)
TS_PRIORITY_HIGH 10 High priority (11 credits per round)
.endtable
.h2 Types
.h3 TaskStateE
Task scheduling state. Only Ready tasks participate in scheduling.
.table
Value Description
----- -----------
TaskStateReady Eligible for scheduling
TaskStateRunning Currently executing (cosmetic; marks active task)
TaskStatePaused Skipped by scheduler until explicitly resumed
TaskStateTerminated Slot available for reuse
.endtable
.h3 TaskEntryT
.code
typedef void (*TaskEntryT)(void *arg);
.endcode
Task entry point function signature. The void* argument lets the caller pass arbitrary context (e.g. a shell app descriptor).
.h2 tsInit
.code
int32_t tsInit(void);
.endcode
Initialize the task system. The calling context becomes task 0 (the main task). Task 0 is special: it cannot be killed or paused, and tsRecoverToMain() always returns control here. No separate stack is allocated for task 0; it uses the process stack directly.
Returns: TS_OK on success, TS_ERR_INIT if already initialized.
.h2 tsShutdown
.code
void tsShutdown(void);
.endcode
Shut down the task system and free all task stacks and internal storage. Safe to call even if tsInit() was never called.
.h2 tsCreate
.code
int32_t tsCreate(const char *name, TaskEntryT entry, void *arg, uint32_t stackSize, int32_t priority);
.endcode
Create a new task. Terminated task slots are recycled to avoid unbounded array growth.
.table
Parameter Description
--------- -----------
name Task name (truncated to TS_NAME_MAX - 1 characters)
entry Task entry point function
arg Opaque argument passed to entry
stackSize Stack size in bytes (pass 0 for TS_DEFAULT_STACK_SIZE)
priority Scheduling priority (0..10; use TS_PRIORITY_* constants)
.endtable
Returns: Task ID (>= 0) on success, or a negative error code (TS_ERR_INIT, TS_ERR_PARAM, TS_ERR_NOMEM).
.h2 tsYield
.code
void tsYield(void);
.endcode
Yield CPU to the next eligible ready task using credit-based round-robin. This is the sole mechanism for task switching. Every task must call this (or a GUI function that calls it) periodically, or it will monopolize the CPU.
.h2 tsPause
.code
int32_t tsPause(uint32_t taskId);
.endcode
Pause a task, removing it from scheduling. Cannot pause the main task (ID 0). If a task pauses itself, an implicit yield occurs.
.table
Parameter Description
--------- -----------
taskId ID of the task to pause
.endtable
Returns: TS_OK on success, TS_ERR_PARAM on invalid ID, TS_ERR_STATE if the task is not in a pausable state.
.h2 tsResume
.code
int32_t tsResume(uint32_t taskId);
.endcode
Resume a paused task. Credits are refilled so the task gets a fair share of CPU time immediately rather than waiting for the next scheduling round.
.table
Parameter Description
--------- -----------
taskId ID of the task to resume
.endtable
Returns: TS_OK on success, TS_ERR_PARAM on invalid ID, TS_ERR_STATE if the task is not paused.
.h2 tsSetPriority
.code
int32_t tsSetPriority(uint32_t taskId, int32_t priority);
.endcode
Set a task's scheduling priority. Also refills credits so the change takes effect immediately.
.table
Parameter Description
--------- -----------
taskId ID of the task to modify
priority New priority level (0..10)
.endtable
Returns: TS_OK on success, TS_ERR_PARAM on invalid ID or out-of-range priority.
.h2 tsGetPriority
.code
int32_t tsGetPriority(uint32_t taskId);
.endcode
Get a task's current scheduling priority.
.table
Parameter Description
--------- -----------
taskId ID of the task to query
.endtable
Returns: Priority value (0..10) on success, TS_ERR_PARAM on invalid ID.
.h2 tsGetState
.code
TaskStateE tsGetState(uint32_t taskId);
.endcode
Get a task's current scheduling state.
.table
Parameter Description
--------- -----------
taskId ID of the task to query
.endtable
Returns: TaskStateE value. Returns TaskStateTerminated for invalid IDs.
.h2 tsCurrentId
.code
uint32_t tsCurrentId(void);
.endcode
Get the ID of the currently executing task. Always valid while the task system is initialized.
Returns: Current task ID.
.h2 tsGetName
.code
const char *tsGetName(uint32_t taskId);
.endcode
Get a task's name string.
.table
Parameter Description
--------- -----------
taskId ID of the task to query
.endtable
Returns: Pointer to the task's name, or NULL on invalid ID. The pointer remains valid until the task slot is reused.
.h2 tsExit
.code
void tsExit(void);
.endcode
Terminate the calling task. Must not be called from the main task (ID 0). The stack is freed immediately and the slot is marked for reuse. This function never returns; it performs an internal context switch to the next ready task.
.h2 tsKill
.code
int32_t tsKill(uint32_t taskId);
.endcode
Forcibly terminate another task. Cannot kill the main task (ID 0) or the currently running task (use tsExit() for self-termination). Safe because cooperative scheduling guarantees the target is suspended at a yield point.
.table
Parameter Description
--------- -----------
taskId ID of the task to terminate
.endtable
Returns: TS_OK on success, TS_ERR_PARAM on invalid ID or illegal target (main task, self).
.h2 tsRecoverToMain
.code
void tsRecoverToMain(void);
.endcode
Crash recovery: force the scheduler back to the main task (ID 0). Call after longjmp from a signal handler that fired in a non-main task. The crashed task is NOT cleaned up by this call; call tsKill() afterward to free its resources. This exists because longjmp unwinds the crashed task's stack but the scheduler state still points to it.
.h2 tsActiveCount
.code
uint32_t tsActiveCount(void);
.endcode
Get the number of non-terminated tasks currently in the system.
Returns: Count of tasks in Ready, Running, or Paused state.