Skip to content

ucontext

簡介 ucontext

Example from wiki:

cpp
#include <stdio.h>
#include <ucontext.h>
#include <unistd.h>
 
int main(){
    ucontext_t context;
 
    getcontext(&context);
    puts("Hello world");
    sleep(1);
    setcontext(&context);
    return 0;
}

會輸出

cpp
$ ./a.out
Hello world
Hello world
Hello world
^C

包裝

cpp
#include <ucontext.h>
#include <cstdint>
#include <memory>
#include <functional>
 
struct coroutine {
    coroutine(const std::function<void (coroutine &)> func, size_t stack_size = SIGSTKSZ)
        : stack{new unsigned char[stack_size]}, func{func} {
        getcontext(&callee);
        callee.uc_link = &caller;
        callee.uc_stack.ss_size = stack_size;
        callee.uc_stack.ss_sp = stack.get();
        makecontext(&callee, reinterpret_cast<void (*)()>(&coroutine_call), 2, reinterpret_cast<size_t>(this) >> 32, this);
    }

    coroutine(const coroutine &) = delete;
    coroutine & operator=(const coroutine &) = delete;
    coroutine(coroutine &&) = default;
    coroutine & operator=(coroutine &&) = default;
 
    void operator()() {
        if (returned) return;
        swapcontext(&caller, &callee);
    }
 
    operator bool() const {
        return !returned;
    }
 
    void yield() {
        swapcontext(&callee, &caller);
    }
 
private:
    ucontext_t caller;
    ucontext_t callee;
    std::unique_ptr<unsigned char[]> stack;
    std::function<void (coroutine &)> func;
    bool returned = false;
 
    static void coroutine_call(
				    uint32_t this_pointer_left_half, uint32_t this_pointer_right_half) {
        coroutine & this_ = *reinterpret_cast<coroutine *>((static_cast<size_t>(this_pointer_left_half) << 32) + this_pointer_right_half);
        this_.func(this_);
        this_.returned = true;
    }
};

使用

cpp
#include <coroutine.h>
#include <iostream>

int main() {
    coroutine test{[](coroutine & self) {
        for (int i = 0; i < 5; ++i) {
            std::cout << "coroutine " << i << std::endl;
            self.yield();
        }
    }};

    while(test) {
        std::cout << "main" << std::endl;
        test();
    }
    return 0;
}

Ref

Changelog

Just observe 👀