Software |
Example |
Has its own... |
Function |
foo(); |
Scratch registers and area of the stack |
Coroutine / Userspace Thread |
see below |
Registers and stack |
OS Kernel Thread |
std::thread t(foo); |
... and thread-local storage |
OS Process |
fork(); |
... and memory space, open files, and
permissions |
OS Container |
see docker |
... and filesystem, and network configuration |
Virtual Machine |
see VM
software |
... and OS, and hardware |
; swap64(old,new): switch coroutines global swap64 swap64: ; Save preserved registers to old stack push rdi push rbp push rbx push r12 push r13 push r14 push r15 ; Save old stack pointer mov [rdi],rsp ; Load new stack pointer mov rsp,[rsi] ; Restore preserved regs from new stack pop r15 pop r14 pop r13 pop r12 pop rbx pop rbp pop rdi ret
typedef void (*coroutine_run_t)(void *arg); void coroutine_exit(void) { std::cout<<"Coroutine has exited. Goodbye.\n"; exit(0); } class coroutine { public: long *stack; // top of coroutine's stack long *stack_alloc; // allocated memory for stack // Used to make main into a coroutine coroutine() { stack=0; stack_alloc=0; } // Used to create a new coroutine coroutine(coroutine_run_t run,void *arg,int stacksize=1024*1024) { stack_alloc=new long[stacksize/sizeof(long)]; stack=&stack_alloc[stacksize/sizeof(long)-1]; // top of stack *(--stack)=(long)coroutine_exit; // coroutine cleanup *(--stack)=(long)run; // user's function to run (rop style!) *(--stack)=(long)arg; // user's function argument (rdi) for (int saved=0;saved<6;saved++) *(--stack)=0xdeadbeef; // initial values for saved registers } // Cleanup ~coroutine() { delete[] stack_alloc; } }; extern "C" void swap64(coroutine *old_co,coroutine *new_co); coroutine *main_co; coroutine *sub_co; void sub(void *arg) { int i; // random local, to see stack pointer std::cout<<" Inside sub-coroutine. Stack="<<&i<<endl; swap64(sub_co,main_co); // back to main std::cout<<" Back in sub. Stack="<<&i<<endl; swap64(sub_co,main_co); // back to main } long foo(void) { int i; std::cout<<"Making coroutines. Stack="<<&i<<std::endl; main_co=new coroutine(); sub_co=new coroutine(sub,0); std::cout<<"Switching to coroutine"<<std::endl; swap64(main_co,sub_co); std::cout<<"Back in main from coroutine. Stack="<<&i<<std::endl; swap64(main_co,sub_co); std::cout<<"Any questions?"<<std::endl; return 0; }On my machine, this prints:
Making coroutines. Stack=0x7fffffffe61c Switching to coroutine Inside sub-coroutine. Stack=0x2aaaab8f2fe4 Back in main from coroutine. Stack=0x7fffffffe61c Back in sub. Stack=0x2aaaab8f2fe4 Any questions? Program complete. Return 0 (0x0)
If you use
this in a real project, you should use a real library like boost::coroutines
or libconcurrency.