aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjohannst <johannes.stoelp@gmail.com>2020-09-22 23:48:09 +0200
committerjohannst <johannes.stoelp@gmail.com>2020-09-22 23:48:09 +0200
commit33f286000db35fe50639c237caa736deea304585 (patch)
treeddb74ebd1f626b200cbb5050545ded484dde4787
parent488d4c6237c3f713077fe93e2745ba5defde0aa5 (diff)
downloadmatcha-threads-33f286000db35fe50639c237caa736deea304585.tar.gz
matcha-threads-33f286000db35fe50639c237caa736deea304585.zip
split classes into separate files, add arch specific subdir
-rw-r--r--.gitignore2
-rw-r--r--Makefile13
-rw-r--r--example/demo1.cc (renamed from example/test.cc)7
-rw-r--r--lib/arch/x86_64/README.md9
-rw-r--r--lib/arch/x86_64/asm.h4
-rw-r--r--lib/arch/x86_64/thread_create.s20
-rw-r--r--lib/arch/x86_64/yield.s (renamed from lib/thread_create.s)23
-rw-r--r--lib/compile_guard.cc5
-rw-r--r--lib/executor.cc20
-rw-r--r--lib/executor.h27
-rw-r--r--lib/matcha.cc65
-rw-r--r--lib/matcha.h50
-rw-r--r--lib/thread.cc62
-rw-r--r--lib/thread.h26
14 files changed, 185 insertions, 148 deletions
diff --git a/.gitignore b/.gitignore
index 1a2de75..126d07f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
*.o
*.a
-test
+demo1
.clangd
compile_commands.json
diff --git a/Makefile b/Makefile
index ec64564..3f89456 100644
--- a/Makefile
+++ b/Makefile
@@ -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