summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPixel <pixel@nobis-crew.org>2012-04-08 01:21:47 -0700
committerPixel <pixel@nobis-crew.org>2012-04-08 01:56:15 -0700
commit9f0431d84807e37bfa63bd144dcb4d2235fe7772 (patch)
tree2dd90832af0f4dd6692df03be45e13a8b39588f4
parente719749e399fc4490fdaf471a000dc841d576c73 (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.h27
-rw-r--r--src/Task.cc88
-rw-r--r--src/TaskMan.cc2
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)) {