diff options
author | johannst <johannes.stoelp@gmail.com> | 2020-09-22 23:48:09 +0200 |
---|---|---|
committer | johannst <johannes.stoelp@gmail.com> | 2020-09-22 23:48:09 +0200 |
commit | 33f286000db35fe50639c237caa736deea304585 (patch) | |
tree | ddb74ebd1f626b200cbb5050545ded484dde4787 | |
parent | 488d4c6237c3f713077fe93e2745ba5defde0aa5 (diff) | |
download | matcha-threads-33f286000db35fe50639c237caa736deea304585.tar.gz matcha-threads-33f286000db35fe50639c237caa736deea304585.zip |
split classes into separate files, add arch specific subdir
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | example/demo1.cc (renamed from example/test.cc) | 7 | ||||
-rw-r--r-- | lib/arch/x86_64/README.md | 9 | ||||
-rw-r--r-- | lib/arch/x86_64/asm.h | 4 | ||||
-rw-r--r-- | lib/arch/x86_64/thread_create.s | 20 | ||||
-rw-r--r-- | lib/arch/x86_64/yield.s (renamed from lib/thread_create.s) | 23 | ||||
-rw-r--r-- | lib/compile_guard.cc | 5 | ||||
-rw-r--r-- | lib/executor.cc | 20 | ||||
-rw-r--r-- | lib/executor.h | 27 | ||||
-rw-r--r-- | lib/matcha.cc | 65 | ||||
-rw-r--r-- | lib/matcha.h | 50 | ||||
-rw-r--r-- | lib/thread.cc | 62 | ||||
-rw-r--r-- | lib/thread.h | 26 |
14 files changed, 185 insertions, 148 deletions
@@ -1,6 +1,6 @@ *.o *.a -test +demo1 .clangd compile_commands.json @@ -6,7 +6,8 @@ CXX := g++ CXXFLAGS := -g -O0 -Wall -Wextra -I. AR := ar -lib/libmatcha.a: lib/matcha.o lib/thread_create.o lib/matcha.h +lib/libmatcha.a: lib/compile_guard.o lib/thread.o lib/executor.o lib/arch/x86_64/thread_create.o lib/arch/x86_64/yield.o \ + lib/thread.h lib/executor.h lib/arch/x86_64/asm.h $(AR) rcs $@ $(filter %.o,$^) %.o: %.cc @@ -18,13 +19,13 @@ lib/libmatcha.a: lib/matcha.o lib/thread_create.o lib/matcha.h fmt: fd --type f '.+.h$$|.+.cc$$' --exec clang-format -i {} -example/test: example/test.o lib/libmatcha.a +example/demo1: example/demo1.o lib/libmatcha.a $(CXX) -o $@ $^ -gdb: example/test - which cgdb && cgdb -x util.gdb -ex 'start' example/test \ - || gdb -x util.gdb -ex 'start' example/test +gdb: example/demo1 + which cgdb && cgdb -x util.gdb -ex 'start' example/demo1 \ + || gdb -x util.gdb -ex 'start' example/demo1 clean: - rm -f example/test + rm -f example/demo1 rm -f **/*.o **/lib*.a diff --git a/example/test.cc b/example/demo1.cc index c9691f3..1c910cd 100644 --- a/example/test.cc +++ b/example/demo1.cc @@ -1,11 +1,12 @@ /* Copyright (c) 2020 Johannes Stoelp */ -#include "lib/matcha.h" +#include "lib/executor.h" +#include "lib/thread.h" #include <cstdio> #include <memory> -struct TestThread : public Thread { +struct TestThread : public nMatcha::Thread { TestThread(const char* name) : Thread(), mName(name) {} virtual void threadFn() override { @@ -21,7 +22,7 @@ struct TestThread : public Thread { int main() { puts("[main] start main thread"); - Executor e; + nMatcha::Executor e; e.spawn(std::make_unique<TestThread>("Thread1")); e.spawn(std::make_unique<TestThread>("Thread2")); e.spawn(std::make_unique<TestThread>("Thread3")); diff --git a/lib/arch/x86_64/README.md b/lib/arch/x86_64/README.md new file mode 100644 index 0000000..f573843 --- /dev/null +++ b/lib/arch/x86_64/README.md @@ -0,0 +1,9 @@ +# SystemV AMD64 ABI + +- Integer/pointer arguments via `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9` +- Integer/pointer return values via `rax` +- Callee saved registers `rbx`, `rbp`, `r12` – `r15` + +## Reference +- [johannst x86_64 notes](https://johannst.github.io/notes/arch/x86_64.html) + diff --git a/lib/arch/x86_64/asm.h b/lib/arch/x86_64/asm.h new file mode 100644 index 0000000..962ff1b --- /dev/null +++ b/lib/arch/x86_64/asm.h @@ -0,0 +1,4 @@ +/* Copyright (c) 2020 Johannes Stoelp */ + +extern "C" void thread_create(); +extern "C" void yield(const void* new_stack, void* const* old_stack); diff --git a/lib/arch/x86_64/thread_create.s b/lib/arch/x86_64/thread_create.s new file mode 100644 index 0000000..37c368c --- /dev/null +++ b/lib/arch/x86_64/thread_create.s @@ -0,0 +1,20 @@ +# Copyright (c) 2020 Johannes Stoelp + + .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 diff --git a/lib/thread_create.s b/lib/arch/x86_64/yield.s index 2aeb758..d40bcd7 100644 --- a/lib/thread_create.s +++ b/lib/arch/x86_64/yield.s @@ -1,30 +1,8 @@ # Copyright (c) 2020 Johannes Stoelp -# 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(const void* new_stack, void* const* old_stack); # ^^^^^^^^^ ^^^^^^^^^ # rdi rsi @@ -64,4 +42,3 @@ yield: ret .cfi_endproc .size yield, .-yield - diff --git a/lib/compile_guard.cc b/lib/compile_guard.cc new file mode 100644 index 0000000..61c7ad5 --- /dev/null +++ b/lib/compile_guard.cc @@ -0,0 +1,5 @@ +/* Copyright (c) 2020 Johannes Stoelp */ + +#if !defined(linux) || (!defined(__x86_64__) && !defined(__amd64__)) +static_assert(false, "Matcha Threads only supported on Linux x86_64!"); +#endif diff --git a/lib/executor.cc b/lib/executor.cc new file mode 100644 index 0000000..fb79b49 --- /dev/null +++ b/lib/executor.cc @@ -0,0 +1,20 @@ +/* Copyright (c) 2020 Johannes Stoelp */ + +#include "executor.h" + +#include "arch/x86_64/asm.h" + +namespace nMatcha { + void Executor::spawn(std::unique_ptr<Thread> t) { + mThreads.push_back(std::move(t)); + mThreads.back()->mExecutor = this; + } + + void Executor::run() { + for (const std::unique_ptr<Thread>& t : mThreads) { + yield_to(t.get()); + } + } + + void Executor::yield_to(const Thread* t) const { ::yield(t->mStackPtr, &mStackPtr); } +} // namespace nMatcha diff --git a/lib/executor.h b/lib/executor.h new file mode 100644 index 0000000..5d2e5b6 --- /dev/null +++ b/lib/executor.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2020 Johannes Stoelp */ + +#pragma once + +#include "thread.h" + +#include <memory> +#include <vector> + +namespace nMatcha { + struct Executor { + Executor(const Executor&) = delete; + Executor& operator=(const Executor&) = delete; + Executor() = default; + + const void* getStackPtr() const { return mStackPtr; } + + void spawn(std::unique_ptr<Thread> t); + void run(); + + private: + void* mStackPtr; + std::vector<std::unique_ptr<Thread>> mThreads; + + void yield_to(const Thread* t) const; + }; +} // namespace nMatcha diff --git a/lib/matcha.cc b/lib/matcha.cc deleted file mode 100644 index 4315e9e..0000000 --- a/lib/matcha.cc +++ /dev/null @@ -1,65 +0,0 @@ -/* Copyright (c) 2020 Johannes Stoelp */ - -#include "matcha.h" - -#include <cassert> -#include <cstdint> -#include <sys/mman.h> -#include <unistd.h> // sysconf - -// asm fns -extern "C" void thread_create(); -extern "C" void yield(const void* new_stack, void* const* old_stack); - -long get_pagesize() { - return sysconf(_SC_PAGESIZE); -} - -Thread::Thread() { - 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); - t->threadFn(); -} - -void Thread::yield() { - assert(mExecutor); - ::yield(mExecutor->getStackPtr(), &mStackPtr); -} - -void Executor::yield_to(const Thread* t) const { - ::yield(t->mStackPtr, &mStackPtr); -} diff --git a/lib/matcha.h b/lib/matcha.h deleted file mode 100644 index 5843cb7..0000000 --- a/lib/matcha.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (c) 2020 Johannes Stoelp */ - -#include <memory> -#include <vector> - -struct Executor; - -struct Thread { - Thread(const Thread&) = delete; - Thread& operator=(const Thread&) = delete; - Thread(); - virtual ~Thread() {} - - virtual void threadFn() = 0; - - protected: - void yield(); - - private: - static void entry(void* obj); - void* mStackPtr; - - friend struct Executor; - const Executor* mExecutor; -}; - - -struct Executor { - Executor(const Executor&) = delete; - Executor& operator=(const Executor&) = delete; - Executor() = default; - - const void* getStackPtr() const { return mStackPtr; } - - void spawn(std::unique_ptr<Thread> t) { - mThreads.push_back(std::move(t)); - mThreads.back()->mExecutor = this; - } - void run() { - for (const std::unique_ptr<Thread>& t : mThreads) { - yield_to(t.get()); - } - } - - private: - void* mStackPtr; - std::vector<std::unique_ptr<Thread>> mThreads; - - void yield_to(const Thread* t) const; -}; diff --git a/lib/thread.cc b/lib/thread.cc new file mode 100644 index 0000000..ad07d96 --- /dev/null +++ b/lib/thread.cc @@ -0,0 +1,62 @@ +/* Copyright (c) 2020 Johannes Stoelp */ + +#include "thread.h" + +#include "arch/x86_64/asm.h" +#include "executor.h" + +#include <cassert> +#include <cstdint> // uintN_t +#include <sys/mman.h> // mmap +#include <unistd.h> // sysconf + +namespace { + static long get_pagesize() { return sysconf(_SC_PAGESIZE); } +} // namespace + +namespace nMatcha { + Thread::Thread() { + 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); + t->threadFn(); + } + + void Thread::yield() { + assert(mExecutor); + ::yield(mExecutor->getStackPtr(), &mStackPtr); + } +} // namespace nMatcha diff --git a/lib/thread.h b/lib/thread.h new file mode 100644 index 0000000..e392052 --- /dev/null +++ b/lib/thread.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2020 Johannes Stoelp */ + +#pragma once + +namespace nMatcha { + struct Executor; + + struct Thread { + Thread(const Thread&) = delete; + Thread& operator=(const Thread&) = delete; + Thread(); + virtual ~Thread() {} + + virtual void threadFn() = 0; + + protected: + void yield(); + + private: + static void entry(void* obj); + void* mStackPtr; + + friend struct Executor; + const Executor* mExecutor; + }; +} // namespace nMatcha |