diff options
author | Pixel <pixel@nobis-crew.org> | 2012-04-08 01:21:47 -0700 |
---|---|---|
committer | Pixel <pixel@nobis-crew.org> | 2012-04-08 01:56:15 -0700 |
commit | 9f0431d84807e37bfa63bd144dcb4d2235fe7772 (patch) | |
tree | 2dd90832af0f4dd6692df03be45e13a8b39588f4 | |
parent | e719749e399fc4490fdaf471a000dc841d576c73 (diff) |
Putting the bases for stackless co-routines.
Also cleaning up some code, clarifying some concepts, and adding more exception catchers.
-rw-r--r-- | includes/Task.h | 27 | ||||
-rw-r--r-- | src/Task.cc | 88 | ||||
-rw-r--r-- | src/TaskMan.cc | 2 |
3 files changed, 96 insertions, 21 deletions
diff --git a/includes/Task.h b/includes/Task.h index 3a02c55..db602f0 100644 --- a/includes/Task.h +++ b/includes/Task.h @@ -21,6 +21,11 @@ class EAgain : public GeneralException { Events::BaseEvent * m_evt; }; +class TaskSwitch : public GeneralException { + public: + TaskSwitch() : GeneralException("Task Switch") { } +}; + class TaskMan; class Task; @@ -120,11 +125,22 @@ class Task { enum Status { STARTING, RUNNING, - IDLE, + SLEEPING, STOPPED, FAULTED, YIELDED, }; + static const char * StatusToString(enum Status status) { + static const char * strs[] = { + "STARTING", + "RUNNING", + "SLEEPING", + "STOPPED", + "FAULTED", + "YIELDED", + }; + return strs[status]; + }; Task(); virtual ~Task(); virtual const char * getName() const = 0; @@ -138,7 +154,7 @@ class Task { TaskMan * getTaskMan() const { return m_taskMan; } struct ev_loop * getLoop(); protected: - void yield(bool changeStatus = false); + void yield(bool stillRunning = false) throw (GeneralException); virtual void Do() = 0; void waitFor(Events::BaseEvent * event); bool setOkayToEAgain(bool enable) { @@ -146,6 +162,11 @@ class Task { m_okayToEAgain = enable; return oldValue; } + void setStackless() { + AAssert(m_stackless, "Can't set a task to be stackless twice"); + AAssert(m_status == STARTING, "Can't set a task to be stackless after it started. status = %s", StatusToString(m_status)); + m_stackless = true; + } private: static size_t stackSize() { return 64 * 1024; } void setup(TaskMan * taskMan, void * stack); @@ -167,7 +188,7 @@ class Task { Lock m_eventLock; typedef std::list<Events::TaskEvent *> waitedByList_t; waitedByList_t m_waitedBy; - bool m_okayToEAgain; + bool m_okayToEAgain, m_stackless; }; class QueueBase { diff --git a/src/Task.cc b/src/Task.cc index f4db057..05d0b0a 100644 --- a/src/Task.cc +++ b/src/Task.cc @@ -6,10 +6,7 @@ static Balau::LocalTmpl<Balau::Task> localTask; -Balau::Task::Task() { - m_status = STARTING; - m_okayToEAgain = false; - +Balau::Task::Task() : m_status(STARTING), m_okayToEAgain(false), m_stackless(false) { Printer::elog(E_TASK, "Created a Task at %p", this); } @@ -54,7 +51,9 @@ void Balau::Task::coroutineTrampoline(void * arg) { void Balau::Task::coroutine() { try { - IAssert(m_status == STARTING, "The Task at %p was badly initialized ? m_status = %i", this, m_status); + if (!m_stackless) { + IAssert(m_status == STARTING, "The Task at %p was badly initialized ? m_status = %s", this, StatusToString(m_status)); + } m_status = RUNNING; Do(); m_status = STOPPED; @@ -66,6 +65,12 @@ void Balau::Task::coroutine() { catch (TestException & e) { m_status = STOPPED; Printer::log(M_ERROR, "Unit test failed: %s", e.getMsg()); + const char * details = e.getDetails(); + if (details) + Printer::log(M_ERROR, " %s", details); + auto trace = e.getTrace(); + for (String & str : trace) + Printer::log(M_ERROR, "%s", str.to_charp()); TaskMan::stop(-1); } catch (RessourceException & e) { @@ -74,8 +79,46 @@ void Balau::Task::coroutine() { const char * details = e.getDetails(); if (details) Printer::log(M_ERROR, " %s", details); + auto trace = e.getTrace(); + for (String & str : trace) + Printer::log(M_DEBUG, "%s", str.to_charp()); TaskMan::stop(-1); } + catch (TaskSwitch & e) { + if (!m_stackless) { + Printer::log(M_ERROR, "Task %s at %p isn't stackless, but still caused a task switch.", getName(), this); + const char * details = e.getDetails(); + if (details) + Printer::log(M_ERROR, " %s", details); + auto trace = e.getTrace(); + for (String & str : trace) + Printer::log(M_DEBUG, "%s", str.to_charp()); + m_status = FAULTED; + } + } + catch (EAgain & e) { + waitFor(e.getEvent()); + if (!m_okayToEAgain) { + Printer::log(M_ERROR, "Task %s at %p which is non-okay-to-eagain got an EAgain exception.", getName(), this); + const char * details = e.getDetails(); + if (details) + Printer::log(M_ERROR, " %s", details); + auto trace = e.getTrace(); + for (String & str : trace) + Printer::log(M_DEBUG, "%s", str.to_charp()); + TaskMan::stop(-1); + } + if (!m_stackless) { + Printer::log(M_WARNING, "Task %s at %p hasn't caught an EAgain exception.", getName(), this); + const char * details = e.getDetails(); + if (details) + Printer::log(M_WARNING, " %s", details); + auto trace = e.getTrace(); + for (String & str : trace) + Printer::log(M_DEBUG, "%s", str.to_charp()); + m_status = FAULTED; + } + } catch (GeneralException & e) { Printer::log(M_WARNING, "Task %s at %p caused an exception: `%s' - stopping.", getName(), this, e.getMsg()); const char * details = e.getDetails(); @@ -90,39 +133,50 @@ void Balau::Task::coroutine() { Printer::log(M_WARNING, "Task %s at %p caused an unknown exception - stopping.", getName(), this); m_status = FAULTED; } + if (!m_stackless) { #ifndef _WIN32 - coro_transfer(&m_ctx, &m_taskMan->m_returnContext); + coro_transfer(&m_ctx, &m_taskMan->m_returnContext); #else - SwitchToFiber(m_taskMan->m_fiber); + SwitchToFiber(m_taskMan->m_fiber); #endif + } } void Balau::Task::switchTo() { Printer::elog(E_TASK, "Switching to task %p - %s", this, getName()); - IAssert(m_status == YIELDED || m_status == IDLE || m_status == STARTING, "The task at %p isn't either yielded, idle or starting... ? m_status = %i", this, m_status); + IAssert(m_status == YIELDED || m_status == SLEEPING || m_status == STARTING, "The task at %p isn't either yielded, sleeping or starting... ? m_status = %s", this, StatusToString(m_status)); void * oldTLS = g_tlsManager->getTLS(); g_tlsManager->setTLS(m_tls); - if (m_status == YIELDED || m_status == IDLE) + if (m_status == YIELDED || m_status == SLEEPING) m_status = RUNNING; + if (m_stackless) { + coroutine(); + } else { #ifndef _WIN32 - coro_transfer(&m_taskMan->m_returnContext, &m_ctx); + coro_transfer(&m_taskMan->m_returnContext, &m_ctx); #else - SwitchToFiber(m_fiber); + SwitchToFiber(m_fiber); #endif + } g_tlsManager->setTLS(oldTLS); - if (m_status == RUNNING) - m_status = IDLE; + IAssert(m_status != RUNNING, "Task %s at %p is still running... ?", getName(), this); } -void Balau::Task::yield(bool changeStatus) { +void Balau::Task::yield(bool stillRunning) throw (GeneralException) { Printer::elog(E_TASK, "Task %p - %s yielding", this, getName()); - if (changeStatus) + if (stillRunning) m_status = YIELDED; + else + m_status = SLEEPING; + if (m_stackless) { + throw TaskSwitch(); + } else { #ifndef _WIN32 - coro_transfer(&m_ctx, &m_taskMan->m_returnContext); + coro_transfer(&m_ctx, &m_taskMan->m_returnContext); #else - SwitchToFiber(m_taskMan->m_fiber); + SwitchToFiber(m_taskMan->m_fiber); #endif + } } Balau::Task * Balau::Task::getCurrentTask() { diff --git a/src/TaskMan.cc b/src/TaskMan.cc index 97732e4..048b27a 100644 --- a/src/TaskMan.cc +++ b/src/TaskMan.cc @@ -261,7 +261,7 @@ int Balau::TaskMan::mainLoop() { // let's check who got signaled, and call them for (Task * t : m_signaledTasks) { Printer::elog(E_TASK, "TaskMan at %p Switching to task %p (%s - %s) that got signaled somehow.", this, t, t->getName(), ClassName(t).c_str()); - IAssert(t->getStatus() == Task::IDLE || t->getStatus() == Task::YIELDED, "We're switching to a non-idle/yielded task at %p... ? status = %i", t, t->getStatus()); + IAssert(t->getStatus() == Task::SLEEPING || t->getStatus() == Task::YIELDED, "We're switching to a non-sleeping/yielded task at %p... ? status = %i", t, t->getStatus()); bool wasYielded = t->getStatus() == Task::YIELDED; t->switchTo(); if ((t->getStatus() == Task::STOPPED) || (t->getStatus() == Task::FAULTED)) { |