DVX_GUI/tasks
2026-03-25 22:42:07 -05:00
..
Makefile MAJOR refactoring. Everything is dynamically loaded now. All new bugs to squash! 2026-03-24 23:03:05 -05:00
README.md Docs. 2026-03-25 22:42:07 -05:00
taskswitch.c MAJOR refactoring. Everything is dynamically loaded now. All new bugs to squash! 2026-03-24 23:03:05 -05:00
taskswitch.h Unicode removed from source. Lots of bugs fixed. Close to Alpha 2! 2026-03-20 19:10:53 -05:00

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*.