Implementation of Stackful Coroutine in C++

Viewed 101

Now I am trying to implement stackful coroutine in C++17 on Windows x64 OS, but, unfortunately, I have encountered the problem: I can't throw exception in my coroutine, if I do so, the program is immediately terminated with a bad exit code.

Implementation
At the begining, I allocate a stack for a new coroutine, the code looks something like that:

void* Allocate() {
  static constexpr std::size_t kStackSize{524'288};
  auto new_stack{::operator new(kStackSize)};
  return static_cast<std::byte *>(new_stack) + kStackSize;
}

The next step is setting a trampoline function on the recently allocated stack. The code is written using MASM, since I utilize MVSC (I would like to use GCC and NASM but I have the problem with thread_local variables, see question, if it is interesting):

SetTrampoline PROC
    mov rax, rsp ; saves the current stack pointer
    mov rsp, [rcx] ; sets the new stack pointer

    sub rsp, 20h ; shadow stack
    push rdx ; saves the function pointer

    ; place for nonvolatile registers
    sub rsp, 0e0h

    mov [rcx], rsp ; saves the moved stack pointer
    mov rsp, rax ; returns the initial stack pointer

    ret
SetTrampoline ENDP

Then I switch machine context with this assembly function (I read this calling convetion):

SwitchContext PROC
    ; saves all nonvolatile registers to the caller stack
    push rbx
    push rbp
    push rdi
    push rsi
    push r12
    push r13
    push r14
    push r15

    sub rsp, 10h
    movdqu [rsp], xmm6

    ; ... pushes xmm7 - xmm14 in here, removed for brevity

    sub rsp, 10h
    movdqu [rsp], xmm15

    mov [rdx], rsp ; saves the caller stack pointer

SwitchContextFinally PROC
    mov rsp, [rcx] ; sets the callee stack pointer

    ; takes out the callee registers
    movdqu xmm15, [rsp]
    add rsp, 10h

    ; ... pops xmm7 - xmm14 in here, removed for brevity    
  
    movdqu xmm6, [rsp]
    add rsp, 10h

    pop r15
    pop r14
    pop r13
    pop r12
    pop rsi
    pop rdi
    pop rbp
    pop rbx

    ret
SwitchContextFinally ENDP
SwitchContext ENDP

Problem
Inside the trampoline I just invoke any passed function and within these functions I can't throw exceptions and catch them instantly in the same fucntion. What have I done wrong? Is it possible to throw exceptions in my case? Should I have shadow stack in SetTrampoline?

Also, I guarantee that the exception thrown don't go outside the trampoline function.

0 Answers
Related