From d440c3f50a918a932293ad98bcec96eaa4683222 Mon Sep 17 00:00:00 2001 From: Pixel 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 #include #include #include @@ -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 getTrace() const { return m_trace; } protected: @@ -23,37 +25,78 @@ class GeneralException { private: char * m_msg; + char * m_details; std::vector 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 bool isA() { return !!dynamic_cast(m_h); } IO & operator=(const IO & io) { if (m_h) m_h->delRef(); setHandle(io.m_h); return *this; } - T * operator->() { Assert(m_h); T * r = dynamic_cast(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(m_h); + AAssert(r, "%s->() used with an incompatible Handle type", ClassName(this).c_str()); + return r; + } bool isNull() { return dynamic_cast(m_h); } }; @@ -129,7 +134,7 @@ class SeekableHandle : public Handle { class ReadOnly : public Handle { public: - ReadOnly(IO & io) : m_io(io) { Assert(m_io->canRead()); } + ReadOnly(IO & 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 & io) : m_io(io) { Assert(m_io->canWrite()); } + WriteOnly(IO & 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 +#include 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 -#include -#include - -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 trace = e.getTrace(); - for (std::vector::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 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 h, Context * ctx) { Assert(ctx); render_r(h, ctx, "", m_fragments.begin(), false, -1); } + void render(IO 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 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 m_stacks; int m_nStacks; + int m_stopCode; }; template -T * createTask(T * t, Task * stick) { TaskMan::registerTask(t, stick); Assert(dynamic_cast(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 #include 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