Fundamentals 7 min read

Build a Cooperative Multitasking System in C Using setjmp/longjmp

This article explains how to use the C standard library functions setjmp and longjmp to create a lightweight cooperative multitasking scheduler without timers, covering their mechanics, stack handling, task creation, time‑slice yielding, and provides complete example code and a Git repository link.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Build a Cooperative Multitasking System in C Using setjmp/longjmp

Introduction

Task scheduling and cooperation are core concepts in computer science. While traditional operating systems provide pre‑emptive scheduling, sometimes a more flexible, cooperative approach is needed. This guide shows how to build a simple query‑style cooperative multitasking system in C using only the standard library functions setjmp and longjmp, without any timer‑based context switching.

setjmp and longjmp

The functions setjmp and longjmp are defined in <setjmp.h>. setjmp saves the current execution context (program counter, registers, and stack pointer) into a jmp_buf structure and returns 0. longjmp restores a previously saved context, causing execution to resume at the point where setjmp was called.

Different platforms define jmp_buf differently; its size is typically under 30 bytes but varies with architecture.

Cooperative Multitasking

In cooperative scheduling, a running task must voluntarily yield its time slice so that other tasks can run; there is no pre‑emptive interruption or priority enforcement. Although true priority is absent, a simple priority scheme can be simulated by having a task voluntarily yield only when a higher‑priority task is ready.

Time‑slice pre‑emptive scheduling cannot yield a slice before a task finishes; cooperative yielding provides that flexibility.

Implementation Idea

Using setjmp to capture a task’s context and longjmp to switch to another task’s saved context enables a scheduler without timers. However, jmp_buf only stores the stack pointer, not the data on the stack, so each task must have its own reserved stack space. When a task yields, the scheduler searches for the next runnable task.

jmp_buf records only the stack pointer, not the contents pointed to by the stack.

Code Implementation

Task creation saves the current context with setjmp and allocates a stack region for the new task.

int cotOs_Creat(OsTask_cb pfnOsTaskEnter, size_t stack) {
    size_t oldsp;
    if (sg_OsInfo.taskNum >= COT_OS_MAX_TASK || sg_OsInfo.pfnGetTimerMs == NULL) {
        return -1;
    }
    COT_OS_GET_STACK(oldsp);
    COT_OS_SET_STACK(sg_OsInfo.stackTop);
    if (0 == setjmp(sg_OsInfo.tcb[sg_OsInfo.taskNum].env)) {
        COT_OS_SET_STACK(oldsp);
        sg_OsInfo.tcb[sg_OsInfo.taskNum].pfnOsTaskEnter = pfnOsTaskEnter;
        sg_OsInfo.taskNum++;
        sg_OsInfo.stackTop -= stack;
    } else {
        sg_OsInfo.tcb[sg_OsInfo.taskId].pfnOsTaskEnter();
    }
    return 0;
}

Yielding a time slice uses longjmp and includes a waiting mechanism that forces a task to stay idle until the specified delay expires.

void cotOs_WaitFor(uint32_t time) {
    uint32_t timer = sg_OsInfo.pfnGetTimerMs();
    setjmp(sg_OsInfo.tcb[sg_OsInfo.taskId].env);
    if (!(sg_OsInfo.pfnGetTimerMs() - timer > time)) {
        sg_OsInfo.taskId++;
        if (sg_OsInfo.taskId >= sg_OsInfo.taskNum) {
            sg_OsInfo.taskId = 0;
        }
        longjmp(sg_OsInfo.tcb[sg_OsInfo.taskId].env, 1);
    }
}

A macro provides conditional yielding: if a condition is false, the current task yields and the scheduler runs the next task.

#define cotOs_WaitFor_Cond(cond)   do{\
        extern jmp_buf *cotOs_GetTaskEnv1(void);\
        setjmp((*cotOs_GetTaskEnv1()));\
        if (!(cond)) {\
            extern void cotOs_RunNextTask(void);\
            cotOs_RunNextTask();\
        }\
    }while (0)

Code Repository

The complete implementation runs on an STM32 board and is available at:

https://gitee.com/cot_package/cot_os

Extensions

Beyond multitasking, setjmp / longjmp can emulate C++‑style try/catch exception handling.

Because of readability concerns, it is advisable to use these functions sparingly in production code, unless they are encapsulated into a well‑defined abstraction.
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Coscooperative multitaskinglongjmpsetjmp
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.