diff options
-rw-r--r-- | lib/executor.cc | 4 | ||||
-rw-r--r-- | lib/executor.h | 16 | ||||
-rw-r--r-- | lib/thread.cc | 8 | ||||
-rw-r--r-- | lib/thread.h | 49 |
4 files changed, 64 insertions, 13 deletions
diff --git a/lib/executor.cc b/lib/executor.cc index 3d3c044..774354f 100644 --- a/lib/executor.cc +++ b/lib/executor.cc @@ -5,6 +5,10 @@ #include "arch.h" namespace nMatcha { + const void* Executor::getStackPtr() const { + return mStackPtr; + } + void Executor::spawn(std::unique_ptr<Thread> t) { mThreads.push_front(std::move(t)); mThreads.front()->mExecutor = this; diff --git a/lib/executor.h b/lib/executor.h index f04f3f1..0f2eca9 100644 --- a/lib/executor.h +++ b/lib/executor.h @@ -8,16 +8,26 @@ #include <memory> namespace nMatcha { + // Cooperative user thread scheduler. + // + // The executor is responsible to schedule the next user thread after one + // thread yielded, as long as there are user threads that didn't finish. + // + // When a `Thread` instance is spawned on an `Executor`, its ownership is + // transfered to the executor. + // struct Executor { Executor(const Executor&) = delete; Executor& operator=(const Executor&) = delete; Executor() = default; - const void* getStackPtr() const { - return mStackPtr; - } + const void* getStackPtr() const; + // Spawn an user thread on the executor. void spawn(std::unique_ptr<Thread> t); + + // Run the executor until all user threads are finished. + // This example executor implements round robin scheduling. void run(); private: diff --git a/lib/thread.cc b/lib/thread.cc index ed2dee3..424041d 100644 --- a/lib/thread.cc +++ b/lib/thread.cc @@ -41,8 +41,12 @@ namespace nMatcha { mStackPtr = init_stack(mStackPtr, Thread::entry, static_cast<void*>(this)); } - void Thread::entry(void* obj) { - Thread* t = static_cast<Thread*>(obj); + bool Thread::isFinished() const { + return mFinished; + } + + void Thread::entry(void* ctx) { + Thread* t = static_cast<Thread*>(ctx); try { t->threadFn(); } catch (const std::exception& e) { diff --git a/lib/thread.h b/lib/thread.h index c181425..3dfa457 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -8,23 +8,42 @@ namespace nMatcha { struct Executor; + // An abstract base class for implementing cooperative user threads. + // + // # Usage + // + // To implement a cooperative user thread following interface invoked by + // the Executor must be implemented: + // virtual void threadFn() = 0; + // By calling `yield()` a thread can give control back to the executor. + // + // # Example + // + // ```cpp + // class MyThread : public nMatcha::Thread { + // virtual void threadFn() override { + // puts("Hello"); + // yield(); + // puts("World"); + // } + // }; + // ``` + // struct Thread { Thread(const Thread&) = delete; Thread& operator=(const Thread&) = delete; Thread(); virtual ~Thread() {} - virtual void threadFn() = 0; - - bool isFinished() const { - return mFinished; - } + bool isFinished() const; protected: void yield(); private: - static void entry(void* obj); + virtual void threadFn() = 0; + + static void entry(void* ctx); void* mStackPtr; bool mFinished; @@ -32,20 +51,34 @@ namespace nMatcha { const Executor* mExecutor; }; + // Helper interface to implement yielding for function objects. struct Yielder { virtual void yield() = 0; }; + // Utility class to create cooperative user threads from function objects. + // + // # Example + // + // ```cpp + // auto t = nMatcha::FnThread::make([](nMatcha::Yielder& y) { + // puts("Hello"); + // y.yield(); + // puts("World"); + // }); + // ``` + // struct FnThread : public Thread, public Yielder { using UserFn = std::function<void(Yielder&)>; + + // Factory method to create `FnThread` objects. static std::unique_ptr<Thread> make(UserFn f); private: + UserFn mUserFn; virtual void threadFn() override; virtual void yield() override; - UserFn mUserFn; - enum class CreatorToken {}; public: |