.topic libtasks .title libtasks -- Cooperative Task Switching .toc 1 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.