aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorjohannst <johannes.stoelp@gmail.com>2020-09-17 00:06:28 +0200
committerjohannst <johannes.stoelp@gmail.com>2020-09-17 00:06:28 +0200
commitdfa05a97e083122a788c0ffb5c9e26888fe3dcd1 (patch)
tree7655b3c4ff7ff5bda43a6a26bcbb406e9bf2999f /lib
parent4e784fc5668909789fa90b8448116b096130740f (diff)
downloadmatcha-threads-dfa05a97e083122a788c0ffb5c9e26888fe3dcd1.tar.gz
matcha-threads-dfa05a97e083122a788c0ffb5c9e26888fe3dcd1.zip
setup new stack + basic yielding between two stacks
Diffstat (limited to 'lib')
-rw-r--r--lib/matcha.h77
-rw-r--r--lib/thread_create.s65
2 files changed, 142 insertions, 0 deletions
diff --git a/lib/matcha.h b/lib/matcha.h
new file mode 100644
index 0000000..9387bf9
--- /dev/null
+++ b/lib/matcha.h
@@ -0,0 +1,77 @@
+#include <cassert>
+#include <cstdint>
+#include <cstdio>
+#include <sys/mman.h>
+#include <unistd.h> // sysconf
+
+extern "C" void thread_create();
+extern "C" void yield(void* new_stack, void** old_stack);
+
+long get_pagesize() {
+ return sysconf(_SC_PAGESIZE);
+}
+
+struct Thread {
+ Thread(const Thread&) = delete;
+ Thread& operator=(const Thread&) = delete;
+ Thread(void (*fn)());
+
+ static void entry(void* obj);
+
+ // private:
+ void* mStackPtr;
+ void (*mUserFn)();
+};
+
+Thread::Thread(void (*fn)()) : mUserFn(fn) {
+ const long PAGE_SIZE = get_pagesize();
+ const long STACK_SIZE = 8 * PAGE_SIZE;
+
+ // create new stack
+ void* stack = mmap(nullptr, STACK_SIZE, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1 /* fd */, 0 /* offset */);
+ assert(stack != MAP_FAILED);
+
+ // protect last stack page (lowest addr)
+ int ret = mprotect(stack, PAGE_SIZE, PROT_NONE);
+ assert(ret == 0);
+
+ // stack grows downwards
+ mStackPtr = static_cast<uint8_t*>(stack) + STACK_SIZE;
+ {
+ uint64_t* stack = static_cast<uint64_t*>(mStackPtr);
+ *(--stack) = (uint64_t)this;
+ *(--stack) = (uint64_t)Thread::entry;
+
+ // initial values for yield epilog
+ *(--stack) = (uint64_t)thread_create;
+ *(--stack) = (uint64_t)0;
+
+ // initial values for callee saved regs
+ *(--stack) = (uint64_t)0; // rbx
+ *(--stack) = (uint64_t)(static_cast<uint64_t*>(mStackPtr) - 4); // rbp
+ *(--stack) = (uint64_t)0; // r12
+ *(--stack) = (uint64_t)0; // r13
+ *(--stack) = (uint64_t)0; // r14
+ *(--stack) = (uint64_t)0; // r15
+
+ mStackPtr = static_cast<void*>(stack);
+ }
+}
+
+void Thread::entry(void* obj) {
+ Thread* t = static_cast<Thread*>(obj);
+
+ puts("thread entry");
+ t->mUserFn();
+ puts("thread done");
+}
+
+void* gOriginalStack;
+
+void yield_to(const Thread& t) {
+ yield(t.mStackPtr, &gOriginalStack);
+}
+
+void yield_from(Thread& t) {
+ yield(gOriginalStack, &t.mStackPtr);
+}
diff --git a/lib/thread_create.s b/lib/thread_create.s
new file mode 100644
index 0000000..6d6fa96
--- /dev/null
+++ b/lib/thread_create.s
@@ -0,0 +1,65 @@
+# SysV AMD64 ABI
+# int/ptr args : rdi, rsi, rdx, rcx, r8, r9
+# int/ptr ret : rax
+
+ .intel_syntax noprefix
+ .section .text ,"ax",@progbits
+
+ # extern "C" void thread_create();
+ .global thread_create
+ .type thread_create, @function
+thread_create:
+ .cfi_startproc
+ mov rdi, qword ptr [rsp+0x8]
+ mov rsi, qword ptr [rsp]
+
+ call rsi
+
+ # FIXME: no return from thread after user fn finished.
+1:
+ jmp 1b
+ .cfi_endproc
+ .size thread_create, .-thread_create
+
+
+
+ # extern "C" void yield(void* new_stack, void** old_stack);
+ # ^^^^^^^^^ ^^^^^^^^^
+ # rdi rsi
+ .global yield
+ .type yield, @function
+yield:
+ .cfi_startproc
+ // prologue
+ push rbp
+ mov rbp, rsp
+
+ // push callee saved registers
+ push rbx
+ push rbp
+ push r12
+ push r13
+ push r14
+ push r15
+
+ // arg0: rdi holds new stack
+ // arg1: rsi holds addr to location current stack must be saved
+ mov [rsi], rsp # save current stack ptr
+ mov rsp, rdi # switch to new stack ptr
+
+ // pop callee saved registers
+ pop r15
+ pop r14
+ pop r13
+ pop r12
+ pop rbp
+ pop rbx
+
+ // epilogue
+ mov rsp, rbp
+ pop rbp
+
+ ret
+ .cfi_endproc
+ .size yield, .-yield
+