From d440c3f50a918a932293ad98bcec96eaa4683222 Mon Sep 17 00:00:00 2001
From: Pixel <pixel@nobis-crew.org>
Date: Sun, 4 Dec 2011 01:19:09 -0800
Subject: Reworked some things in the architecture, mainly exceptions and
 asserts. -) Removed Assert() -) Added AAssert(), IAssert(), RAssert(),
 TAssert() and Failure() -) Reworked all asserts in the code, and added
 meaningful messages to them. -) Changed the way the startup code is
 generated; BALAU_STARTUP is no longer necessary.

---
 includes/AtStartExit.h    | 27 +++++++++++++
 includes/BString.h        |  6 +--
 includes/Exceptions.h     | 90 ++++++++++++++++++++++++++++++++++++-------
 includes/Handle.h         | 13 +++++--
 includes/HttpServer.h     |  6 +--
 includes/Main.h           | 98 ++++-------------------------------------------
 includes/Printer.h        | 10 ++---
 includes/SimpleMustache.h |  8 ++--
 includes/Socket.h         |  2 +
 includes/Task.h           | 17 ++++++--
 includes/TaskMan.h        | 10 +++--
 includes/Threads.h        |  9 +++--
 12 files changed, 163 insertions(+), 133 deletions(-)
 create mode 100644 includes/AtStartExit.h

(limited to 'includes')

diff --git a/includes/AtStartExit.h b/includes/AtStartExit.h
new file mode 100644
index 0000000..9b55d93
--- /dev/null
+++ b/includes/AtStartExit.h
@@ -0,0 +1,27 @@
+#pragma once
+
+namespace Balau {
+
+class AtStart {
+  protected:
+      AtStart(int priority = 0);
+    virtual void doStart() = 0;
+  private:
+    const int m_priority;
+    AtStart * m_next;
+    static AtStart * s_head;
+    friend class Main;
+};
+
+class AtExit {
+  protected:
+      AtExit(int priority = 0);
+    virtual void doExit() = 0;
+  private:
+    const int m_priority;
+    AtExit * m_next;
+    static AtExit * s_head;
+    friend class Main;
+};
+
+};
diff --git a/includes/BString.h b/includes/BString.h
index d8487d0..29f3d22 100644
--- a/includes/BString.h
+++ b/includes/BString.h
@@ -26,12 +26,12 @@ class String : private std::string {
       String(const String & s) : std::string(s) { }
       String(const std::string & s) : std::string(s) { }
 
-    String & set(const char * fmt, va_list);
-    String & set(const char * fmt, ...) { va_list ap; va_start(ap, fmt); set(fmt, ap); va_end(ap); return *this; }
+    String & set(const char * fmt, va_list) __attribute__((format(printf, 2, 0)));
+    String & set(const char * fmt, ...) __attribute__((format(printf, 2, 3))) { va_list ap; va_start(ap, fmt); set(fmt, ap); va_end(ap); return *this; }
     String & set(const String & fmt, ...) { va_list ap; va_start(ap, fmt); set(fmt.to_charp(), ap); va_end(ap); return *this; }
 
     int scanf(const char * fmt, va_list ap) const { return ::vsscanf(c_str(), fmt, ap); }
-    int scanf(const char * fmt, ...) const { va_list ap; va_start(ap, fmt); int r = scanf(fmt, ap); va_end(ap); return r; }
+    int scanf(const char * fmt, ...) const __attribute__((format(scanf, 2, 3))) { va_list ap; va_start(ap, fmt); int r = scanf(fmt, ap); va_end(ap); return r; }
     int scanf(const String & fmt, ...) const { va_list ap; va_start(ap, fmt); int r = scanf(fmt.to_charp(), ap); va_end(ap); return r; }
 
     const char * to_charp(size_t begin = 0) const { return c_str() + begin; }
diff --git a/includes/Exceptions.h b/includes/Exceptions.h
index c397265..1f65b12 100644
--- a/includes/Exceptions.h
+++ b/includes/Exceptions.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <stdarg.h>
 #include <typeinfo>
 #include <cxxabi.h>
 #include <BString.h>
@@ -9,11 +10,12 @@ namespace Balau {
 
 class GeneralException {
   public:
-      GeneralException(const char * msg) : m_msg(::strdup(msg)) { genTrace(); }
-      GeneralException(const String & msg) : m_msg(msg.strdup()) { genTrace(); }
-      GeneralException(const GeneralException & e) : m_msg(strdup(e.m_msg)), m_trace(e.m_trace) { }
+      GeneralException(const char * msg, const char * details = NULL) : m_msg(::strdup(msg)) { setDetails(details); genTrace(); }
+      GeneralException(const String & msg, const char * details = NULL) : m_msg(msg.strdup()) { setDetails(details); genTrace(); }
+      GeneralException(const GeneralException & e) : m_msg(strdup(e.m_msg)), m_trace(e.m_trace) { setDetails(e.m_details); }
       ~GeneralException() { if (m_msg) free(m_msg); }
     const char * getMsg() const { return m_msg; }
+    const char * getDetails() const { return m_details; }
     const std::vector<String> getTrace() const { return m_trace; }
 
   protected:
@@ -23,37 +25,78 @@ class GeneralException {
 
   private:
     char * m_msg;
+    char * m_details;
     std::vector<String> m_trace;
+
+    void setDetails(const char * details) {
+        if (details)
+            m_details = ::strdup(details);
+        else
+            m_details = NULL;
+    }
+};
+
+class RessourceException : public GeneralException {
+  public:
+      RessourceException(const String & msg, const char * details) : GeneralException(msg, details) { }
 };
 
-static inline void * malloc(size_t size) throw (GeneralException) {
+void ExitHelper(const String & msg, const char * fmt = NULL, ...) __attribute__((format(printf, 2, 3)));
+
+static inline void * malloc(size_t size) {
     void * r = ::malloc(size);
 
     if (!r && size)
-        throw GeneralException("Failed to allocate memory.");
+        ExitHelper("Failed to allocate memory", "%li bytes", size);
 
     return r;
 }
 
-static inline void * calloc(size_t count, size_t size) throw (GeneralException) {
+static inline void * calloc(size_t count, size_t size) {
     void * r = ::calloc(count, size);
 
     if (!r && ((count * size) != 0))
-        throw GeneralException("Failed to allocate memory.");
+        ExitHelper("Failed to allocate memory", "%li * %li = %li bytes", count, size, count * size);
 
     return r;
 }
 
-static inline void * realloc(void * previous, size_t size) throw (GeneralException) {
+static inline void * realloc(void * previous, size_t size) {
     void * r = ::realloc(previous, size);
 
     if (!r && size)
-        throw GeneralException("Failed to allocate memory.");
+        ExitHelper("Failed to re-allocate memory", "%li bytes", size);
 
     return r;
 }
 
-static inline void AssertHelper(const String & msg) throw(GeneralException) { throw GeneralException(msg); }
+static inline void AssertHelperInner(const String & msg, const char * details = NULL) throw (GeneralException) {
+    throw GeneralException(msg, details);
+}
+
+static inline void AssertHelper(const String & msg, const char * fmt = NULL, ...) __attribute__((format(printf, 2, 3)));
+
+static inline void AssertHelper(const String & msg, const char * fmt, ...) {
+    if (fmt) {
+        String details;
+        va_list ap;
+        va_start(ap, fmt);
+        details.set(fmt, ap);
+        va_end(ap);
+        AssertHelperInner(msg, details.to_charp());
+    } else {
+        AssertHelperInner(msg);
+    }
+}
+
+class TestException : public GeneralException {
+  public:
+      TestException(const String & msg) : GeneralException(msg) { }
+};
+
+static inline void TestHelper(const String & msg) throw (TestException) {
+    throw TestException(msg);
+}
 
 class ClassName {
   public:
@@ -72,8 +115,29 @@ ClassName::ClassName(T * ptr) {
 
 };
 
-#define Assert(c) if (!(c)) { \
+#define Failure(msg) Balau::AssertHelper(msg);
+#define FailureDetails(msg, ...) Balau::AssertHelper(msg, __VA_ARGS__);
+
+#define IAssert(c, ...) if (!__builtin_expect(!!(c), 0)) { \
+    Balau::String msg; \
+    msg.set("Internal Assertion " #c " failed at %s:%i", __FILE__, __LINE__); \
+    Balau::AssertHelper(msg, __VA_ARGS__); \
+}
+
+#define AAssert(c, ...) if (!__builtin_expect(!!(c), 0)) { \
+    Balau::String msg; \
+    msg.set("API Assertion " #c " failed at %s:%i", __FILE__, __LINE__); \
+    Balau::AssertHelper(msg, __VA_ARGS__); \
+}
+
+#define RAssert(c, ...) if (!__builtin_expect(!!(c), 0)) { \
+    Balau::String msg; \
+    msg.set("Ressource Assertion " #c " failed at %s:%i", __FILE__, __LINE__); \
+    Balau::ExitHelper(msg, __VA_ARGS__); \
+}
+
+#define TAssert(c) if (!__builtin_expect(!!(c), 0)) { \
     Balau::String msg; \
-    msg.set("Assertion " #c " failed at %s:%i", __FILE__, __LINE__); \
-    Balau::AssertHelper(msg); \
+    msg.set("UnitTest Assert " #c " failed at %s:%i", __FILE__, __LINE__); \
+    Balau::TestHelper(msg); \
 }
diff --git a/includes/Handle.h b/includes/Handle.h
index cc66a22..e3ff356 100644
--- a/includes/Handle.h
+++ b/includes/Handle.h
@@ -29,7 +29,7 @@ class BaseEvent;
 
 class Handle {
   public:
-      virtual ~Handle() { Assert(m_refCount == 0); }
+      virtual ~Handle() { AAssert(m_refCount == 0, "Do not use handles directly; warp them in IO<>"); }
     virtual void close() throw (GeneralException) = 0;
     virtual bool isClosed() = 0;
     virtual bool isEOF() = 0;
@@ -107,7 +107,12 @@ class IO : public IOBase {
       template<class U>
       bool isA() { return !!dynamic_cast<U *>(m_h); }
     IO<T> & operator=(const IO<T> & io) { if (m_h) m_h->delRef(); setHandle(io.m_h); return *this; }
-    T * operator->() { Assert(m_h); T * r = dynamic_cast<T *>(m_h); Assert(r); return r; }
+    T * operator->() {
+        AAssert(m_h, "Can't use %s->() with a null Handle", ClassName(this).c_str());
+        T * r = dynamic_cast<T *>(m_h);
+        AAssert(r, "%s->() used with an incompatible Handle type", ClassName(this).c_str());
+        return r;
+    }
     bool isNull() { return dynamic_cast<T *>(m_h); }
 };
 
@@ -129,7 +134,7 @@ class SeekableHandle : public Handle {
 
 class ReadOnly : public Handle {
   public:
-      ReadOnly(IO<Handle> & io) : m_io(io) { Assert(m_io->canRead()); }
+      ReadOnly(IO<Handle> & io) : m_io(io) { AAssert(m_io->canRead(), "You need to use ReadOnly with a Handle that can at least read"); }
     virtual void close() throw (GeneralException) { m_io->close(); }
     virtual bool isClosed() { return m_io->isClosed(); }
     virtual bool isEOF() { return m_io->isEOF(); }
@@ -151,7 +156,7 @@ class ReadOnly : public Handle {
 
 class WriteOnly : public Handle {
   public:
-      WriteOnly(IO<Handle> & io) : m_io(io) { Assert(m_io->canWrite()); }
+      WriteOnly(IO<Handle> & io) : m_io(io) { AAssert(m_io->canWrite(), "You need to use WriteOnly with a Handle that can at least write"); }
     virtual void close() throw (GeneralException) { m_io->close(); }
     virtual bool isClosed() { return m_io->isClosed(); }
     virtual bool isEOF() { return m_io->isEOF(); }
diff --git a/includes/HttpServer.h b/includes/HttpServer.h
index 6c97799..9885d50 100644
--- a/includes/HttpServer.h
+++ b/includes/HttpServer.h
@@ -21,7 +21,7 @@ class HttpServer {
     class Action {
       public:
           Action(const Regex & regex, const Regex & host = Regexes::any) : m_regex(regex), m_host(host), m_refCount(0) { }
-          ~Action() { Assert(m_refCount == 0); }
+          ~Action() { AAssert(m_refCount == 0, "Don't delete an Action directl"); }
         struct ActionMatch {
             Regex::Captures uri, host;
         };
@@ -39,8 +39,8 @@ class HttpServer {
       ~HttpServer() { if (!m_started) stop(); }
     void start();
     void stop();
-    void setPort(int port) { Assert(!m_started); m_port = port; }
-    void setLocal(const char * local) { Assert(!m_started); m_local = local; }
+    void setPort(int port) { AAssert(!m_started, "You can't set the HTTP port once the server has started"); m_port = port; }
+    void setLocal(const char * local) { AAssert(!m_started, "You can't set the HTTP IP once the server has started"); m_local = local; }
     void registerAction(Action * action);
     void flushAllActions();
     struct ActionFound {
diff --git a/includes/Main.h b/includes/Main.h
index 1c902ec..a890525 100644
--- a/includes/Main.h
+++ b/includes/Main.h
@@ -1,31 +1,10 @@
 #pragma once
 
 #include <Exceptions.h>
+#include <Task.h>
 
 namespace Balau {
 
-class AtStart {
-  protected:
-      AtStart(int priority = 0);
-    virtual void doStart() = 0;
-  private:
-    const int m_priority;
-    AtStart * m_next;
-    static AtStart * s_head;
-    friend class Main;
-};
-
-class AtExit {
-  protected:
-      AtExit(int priority = 0);
-    virtual void doExit() = 0;
-  private:
-    const int m_priority;
-    AtExit * m_next;
-    static AtExit * s_head;
-    friend class Main;
-};
-
 class Exit : public GeneralException {
   public:
       Exit(int code = -1) : GeneralException(), m_code(code) { String s; s.set("Application exitting with code = %i", code); setMsg(s.strdup()); }
@@ -34,19 +13,11 @@ class Exit : public GeneralException {
     int m_code;
 };
 
-};
-
-#include <Printer.h>
-#include <Task.h>
-#include <TaskMan.h>
-
-namespace Balau {
-
 class MainTask : public Task {
   public:
       MainTask() : m_stopTaskManOnExit(true) { }
-      virtual ~MainTask() { if (m_stopTaskManOnExit) TaskMan::stop(); }
-    virtual const char * getName() { return "Main Task"; }
+      virtual ~MainTask();
+    virtual const char * getName();
     virtual void Do();
     void stopTaskManOnExit(bool v) { m_stopTaskManOnExit = v; }
   private:
@@ -62,50 +33,10 @@ class Main {
         STOPPING,
         STOPPED,
     };
-      Main() : m_status(UNKNOWN) { Assert(s_application == 0); s_application = this; }
-    static Status status() { return s_application->m_status; }
-    int bootstrap(int _argc, char ** _argv) {
-        int r = 0;
-        m_status = STARTING;
-
-        argc = _argc;
-        argv = _argv;
-        enve = NULL;
-
-        for (AtStart * ptr = AtStart::s_head; ptr; ptr = ptr->m_next)
-            ptr->doStart();
-
-        try {
-            m_status = RUNNING;
-            MainTask * mainTask = createTask(new MainTask());
-            TaskMan::getDefaultTaskMan()->mainLoop();
-            m_status = STOPPING;
-        }
-        catch (Exit e) {
-            m_status = STOPPING;
-            r = e.getCode();
-        }
-        catch (GeneralException e) {
-            m_status = STOPPING;
-            Printer::log(M_ERROR | M_ALERT, "The application caused an exception: %s", e.getMsg());
-            std::vector<String> trace = e.getTrace();
-            for (std::vector<String>::iterator i = trace.begin(); i != trace.end(); i++)
-                Printer::log(M_DEBUG, "%s", i->to_charp());
-            r = -1;
-        }
-        catch (...) {
-            m_status = STOPPING;
-            Printer::log(M_ERROR | M_ALERT, "The application caused an unknown exception");
-            r = -1;
-        }
-        m_status = STOPPING;
-
-        for (AtExit * ptr = AtExit::s_head; ptr; ptr = ptr->m_next)
-            ptr->doExit();
-
-        m_status = STOPPED;
-        return r;
-    }
+      Main() : m_status(UNKNOWN) { IAssert(s_application == NULL, "There can't be two main apps"); s_application = this; }
+    static Status getStatus() { return s_application->m_status; }
+    int bootstrap(int _argc, char ** _argv);
+    static bool hasMain() { return s_application; }
   protected:
     int argc;
     char ** argv;
@@ -115,19 +46,4 @@ class Main {
     static Main * s_application;
 };
 
-#define BALAU_STARTUP \
-\
-class Application : public Balau::Main { \
-  public: \
-    virtual int startup() throw (Balau::GeneralException); \
-}; \
-\
-extern "C" { \
-    int main(int argc, char ** argv) { \
-        setlocale(LC_ALL, ""); \
-        Balau::Main mainClass; \
-        return mainClass.bootstrap(argc, argv); \
-    } \
-}
-
 };
diff --git a/includes/Printer.h b/includes/Printer.h
index e321557..b4001f1 100644
--- a/includes/Printer.h
+++ b/includes/Printer.h
@@ -56,14 +56,14 @@ class Printer {
 
     static Printer * getPrinter();
     static void log(uint32_t level, const String & fmt, ...) { va_list ap; va_start(ap, fmt); vlog(level, fmt.to_charp(), ap); va_end(ap); }
-    static void log(uint32_t level, const char * fmt, ...) { va_list ap; va_start(ap, fmt); vlog(level, fmt, ap); va_end(ap); }
-    static void vlog(uint32_t level, const char * fmt, va_list ap) { getPrinter()->_log(level, fmt, ap); }
+    static void log(uint32_t level, const char * fmt, ...) __attribute__((format(printf, 2, 3))) { va_list ap; va_start(ap, fmt); vlog(level, fmt, ap); va_end(ap); }
+    static void vlog(uint32_t level, const char * fmt, va_list ap) __attribute__((format(printf, 2, 0))) { getPrinter()->_log(level, fmt, ap); }
     static void print(const String & fmt, ...) { va_list ap; va_start(ap, fmt); vprint(fmt.to_charp(), ap); va_end(ap); }
-    static void print(const char * fmt, ...) { va_list ap; va_start(ap, fmt); vprint(fmt, ap); va_end(ap); }
-    static void vprint(const char * fmt, va_list ap) { getPrinter()->_print(fmt, ap); }
+    static void print(const char * fmt, ...) __attribute__((format(printf, 1, 2))) { va_list ap; va_start(ap, fmt); vprint(fmt, ap); va_end(ap); }
+    static void vprint(const char * fmt, va_list ap) __attribute__((format(printf, 1, 0))) { getPrinter()->_print(fmt, ap); }
 
 #ifdef DEBUG
-    static void elog(uint32_t engine, const char * fmt, ...) { va_list ap; va_start(ap, fmt); getPrinter()->_log(M_ENGINE_DEBUG, fmt, ap); }
+    static void elog(uint32_t engine, const char * fmt, ...) __attribute__((format(printf, 2, 3))) { va_list ap; va_start(ap, fmt); getPrinter()->_log(M_ENGINE_DEBUG, fmt, ap); }
 #else
     static void elog(uint32_t engine, const char * fmt, ...) { }
 #endif
diff --git a/includes/SimpleMustache.h b/includes/SimpleMustache.h
index b16f683..2bb0cb1 100644
--- a/includes/SimpleMustache.h
+++ b/includes/SimpleMustache.h
@@ -57,8 +57,8 @@ class SimpleMustache {
             LAMBDA,
         } m_type;
           Context(ContextType type) : m_type(type), m_root(false) { }
-          Context(Context & c) { Assert(false); }
-          Context & operator=(Context & c) { Assert(false); return *this; }
+          Context(Context & c) { Failure("You can't copy a Context; use references"); }
+          Context & operator=(Context & c) { Failure("You can't assign a Context; use references"); return *this; }
         String m_str;
         bool m_bool;
         typedef std::map<String, Context *> SubContext;
@@ -82,9 +82,9 @@ class SimpleMustache {
     }
     void setTemplate(const char * str, ssize_t s = -1) { setTemplate((const uint8_t *) str, s); }
     void setTemplate(const String & str) { setTemplate((const uint8_t *) str.to_charp(), str.strlen()); }
-    void render(IO<Handle> h, Context * ctx) { Assert(ctx); render_r(h, ctx, "", m_fragments.begin(), false, -1); }
+    void render(IO<Handle> h, Context * ctx) { AAssert(ctx, "Please pass on a context to render"); render_r(h, ctx, "", m_fragments.begin(), false, -1); }
     void empty() { while (!m_fragments.empty()) { delete m_fragments.front(); m_fragments.pop_front(); } }
-    void checkTemplate() { Fragments::iterator end = checkTemplate_r(m_fragments.begin()); Assert(end == m_fragments.end()); }
+    void checkTemplate() { Fragments::iterator end = checkTemplate_r(m_fragments.begin()); AAssert(end == m_fragments.end(), "The template wasn't fully checked; possibly mismatched sections"); }
       ~SimpleMustache() { empty(); }
   private:
     struct Fragment {
diff --git a/includes/Socket.h b/includes/Socket.h
index a19b1e2..77e1165 100644
--- a/includes/Socket.h
+++ b/includes/Socket.h
@@ -29,6 +29,8 @@ class Socket : public Handle {
 
     bool setLocal(const char * hostname = NULL, int port = 0);
     bool connect(const char * hostname, int port);
+    bool gotR() { return m_evtR->gotSignal(); }
+    bool gotW() { return m_evtW->gotSignal(); }
     IO<Socket> accept() throw (GeneralException);
     bool listen();
   private:
diff --git a/includes/Task.h b/includes/Task.h
index 2e52747..03233f8 100644
--- a/includes/Task.h
+++ b/includes/Task.h
@@ -38,9 +38,20 @@ class BaseEvent {
       virtual ~BaseEvent() { if (m_cb) delete m_cb; }
     bool gotSignal() { return m_signal; }
     void doSignal();
-    void reset() { Assert(m_task != NULL); m_signal = false; gotOwner(m_task); }
-    Task * taskWaiting() { Assert(m_task); return m_task; }
-    void registerOwner(Task * task) { if (m_task == task) return; Assert(m_task == NULL); m_task = task; gotOwner(task); }
+    void reset() {
+        // could be potentially changed into a simple return
+        AAssert(m_task != NULL, "Can't reset an event that doesn't have a task");
+        m_signal = false;
+        gotOwner(m_task);
+    }
+    Task * taskWaiting() { AAssert(m_task, "No task is waiting for that event"); return m_task; }
+    void registerOwner(Task * task) {
+        if (m_task == task)
+            return;
+        AAssert(m_task == NULL, "Can't register an event for another task");
+        m_task = task;
+        gotOwner(task);
+    }
   protected:
     virtual void gotOwner(Task * task) { }
   private:
diff --git a/includes/TaskMan.h b/includes/TaskMan.h
index 1e7a2f1..6a50491 100644
--- a/includes/TaskMan.h
+++ b/includes/TaskMan.h
@@ -27,13 +27,14 @@ class TaskMan {
   public:
       TaskMan();
       ~TaskMan();
-    void mainLoop();
+    int mainLoop();
     static TaskMan * getDefaultTaskMan();
     struct ev_loop * getLoop() { return m_loop; }
     void signalTask(Task * t);
-    static void stop();
-    void stopMe() { m_stopped = true; }
+    static void stop(int code);
+    void stopMe(int code) { m_stopped = true; m_stopCode = code; }
     static Thread * createThreadedTaskMan();
+    bool stopped() { return m_stopped; }
   private:
     static void registerTask(Task * t, Task * stick);
     void * getStack();
@@ -58,9 +59,10 @@ class TaskMan {
     ev::async m_evt;
     std::queue<void *> m_stacks;
     int m_nStacks;
+    int m_stopCode;
 };
 
 template<class T>
-T * createTask(T * t, Task * stick) { TaskMan::registerTask(t, stick); Assert(dynamic_cast<Task *>(t)); return t; }
+T * createTask(T * t, Task * stick) { TaskMan::registerTask(t, stick); return t; }
 
 };
diff --git a/includes/Threads.h b/includes/Threads.h
index ca60627..bc2670d 100644
--- a/includes/Threads.h
+++ b/includes/Threads.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <AtStartExit.h>
 #include <pthread.h>
 
 namespace Balau {
@@ -21,17 +22,19 @@ class Lock {
 
 class ThreadHelper;
 
-class Thread {
+class Thread : public AtExit {
   public:
       virtual ~Thread();
     void threadStart();
     void * join();
   protected:
-      Thread() : m_joined(false) { }
+      Thread(bool registerAtExit = false) : AtExit(registerAtExit ? 1 : -1), m_joined(false) { }
     virtual void * proc() = 0;
+    virtual void threadExit();
   private:
     pthread_t m_thread;
-    bool m_joined;
+    volatile bool m_joined;
+    virtual void doExit() { join(); }
 
     friend class ThreadHelper;
 };
-- 
cgit v1.2.3