Advertisement
mere_mortal

Cooperative concurrency

Mar 19th, 2024
397
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 3.01 KB | None | 0 0
  1. #include <cassert>
  2. #include <functional>
  3. #include <iostream>
  4. #include <vector>
  5.  
  6. struct ISpringBoard {
  7.     [[noreturn]] virtual void Run() = 0;
  8. };
  9.  
  10. struct Context;
  11.  
  12. extern "C" {
  13.     void SwitchContext(Context* from, Context to);
  14.     void SetupContext(Context* self, void* stackTop, void* springBoard,
  15.                       void (*runSpringBoard)(void*, void*, void*, void*, void*, void*, void*));
  16. }
  17.  
  18. asm(R"(
  19. SwitchContext:
  20.    # Callee-saved registers according to x86_64 ABI
  21.    # rbx, rsp, rbp, r12, r13, r14, r15
  22.    pushq %rbx
  23.    pushq %rbp
  24.    pushq %r12
  25.    pushq %r13
  26.    pushq %r14
  27.    pushq %r15
  28.  
  29.    movq %rsp, (%rdi)
  30.    movq %rsi, %rsp
  31.  
  32.    popq %r15
  33.    popq %r14
  34.    popq %r13
  35.    popq %r12
  36.    popq %rbp
  37.    popq %rbx
  38.  
  39.    ret
  40.  
  41. SetupContext:
  42.    movq %rsp, %rax;
  43.    movq %rsi, %rsp;
  44.  
  45.    pushq $0; # Stack alignment
  46.    pushq %rdx;
  47.    pushq $0; # Fake return address
  48.    pushq %rcx;
  49.  
  50.    pushq $0; # rbx
  51.    pushq $0; # rbp
  52.    pushq $0; # r12
  53.    pushq $0; # r13
  54.    pushq $0; # r14
  55.    pushq $0; # r15
  56.  
  57.    movq %rsp, (%rdi);
  58.  
  59.    movq %rax, %rsp;
  60.    ret
  61. )");
  62.  
  63.  
  64. struct Context {
  65.     void* rsp;
  66.  
  67.     void Setup(ISpringBoard& springBoard, void* stackTop) {
  68.         SetupContext(this, stackTop, &springBoard, &Context::RunSpringBoard);
  69.     }
  70.  
  71.     void SwitchTo(Context to) {
  72.         SwitchContext(this, to);
  73.     }
  74.  
  75. private:
  76.     [[noreturn]] static void RunSpringBoard(void*, void*, void*, void*, void*, void*, void* springBoardRaw) {
  77.         auto springBoard = static_cast<ISpringBoard*>(springBoardRaw);
  78.         springBoard->Run();
  79.     }
  80. };
  81.  
  82. class Coroutine: private ISpringBoard {
  83. public:
  84.     Coroutine(std::function<void()> runnable, size_t stackSize)
  85.         : runnable(std::move(runnable))
  86.         , stack(stackSize) {
  87.         context.Setup(*this, stack.data() + stackSize);
  88.     }
  89.  
  90.     void Resume() {
  91.         auto prev = std::exchange(currentCoroutine, this);
  92.         context.SwitchTo(context);
  93.         currentCoroutine = prev;
  94.     }
  95.  
  96.     static void Suspend() {
  97.         assert(currentCoroutine);
  98.         auto& currentContext = currentCoroutine->context;
  99.         currentContext.SwitchTo(currentContext);
  100.     }
  101.  
  102. private:
  103.     [[noreturn]] void Run() override {
  104.         runnable();
  105.         context.SwitchTo(context);
  106.         std::abort(); // Finished corouting resumed
  107.     }
  108.  
  109.     std::function<void()> runnable;
  110.     Context context;
  111.     std::vector<char> stack;
  112.  
  113.     static thread_local Coroutine *currentCoroutine;
  114. };
  115.  
  116. thread_local Coroutine *Coroutine::currentCoroutine = nullptr;
  117.  
  118. int main() {
  119.     Coroutine a([] {
  120.         std::cout << "2" << std::endl;
  121.         Coroutine::Suspend();
  122.         std::cout << "6" << std::endl;
  123.     }, 50 << 10);
  124.  
  125.     Coroutine b([] {
  126.         std::cout << "3" << std::endl;
  127.         Coroutine::Suspend();
  128.         std::cout << "5" << std::endl;
  129.     }, 20 << 10);
  130.  
  131.     std::cout << "1" << std::endl;
  132.     a.Resume();
  133.     b.Resume();
  134.     std::cout << "4" << std::endl;
  135.     b.Resume();
  136.     a.Resume();
  137. }
  138.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement