From: Mikko Rasa Date: Mon, 28 Aug 2006 07:59:47 +0000 (+0000) Subject: Rename to libmspcore X-Git-Tag: 1.0~41 X-Git-Url: http://git.tdb.fi/?p=libs%2Fcore.git;a=commitdiff_plain;h=e1ea831a640fba534e7e42e399f04cdf681ef8d3 Rename to libmspcore Combine with libmsptime --- diff --git a/Package b/Package index 70efd84..42d52e4 100644 --- a/Package +++ b/Package @@ -1,5 +1,5 @@ -package="mspframework" +package="mspcore" version="0.1" -description="Mikkosoft Productions application framework library" +description="Mikkosoft Productions core library" requires=("mspmisc","pthread","sigc++-2.0") tarballfile=("License.txt",) diff --git a/source/Module b/source/Module deleted file mode 100644 index 8e1936d..0000000 --- a/source/Module +++ /dev/null @@ -1,4 +0,0 @@ -type="library" -target="mspframework" -installheaders="framework" -installtarget=1 diff --git a/source/application.cpp b/source/application.cpp deleted file mode 100644 index a9aa88e..0000000 --- a/source/application.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#include -#include -#include "application.h" -#include "error.h" - -using namespace std; - -namespace Msp { - -Poller::Slot &Application::add_pollable(Pollable *obj, short events) -{ - if(!poller_) - poller_=new Poller; - - Poller::Slot &slot=poller_->add_pollable(obj, events); - // Interrupt a possible poll in progress -#ifndef WIN32 //XXX - pthread_kill(main_tid, SIGALRM); -#endif - return slot; -} - -EventManager::Event &Application::create_event() -{ - if(!ev_mgr_) - ev_mgr_=new EventManager(*this); - - return ev_mgr_->create_event(); -} - -Application::~Application() -{ - if(poller_) - delete poller_; - if(ev_mgr_) - delete ev_mgr_; -} - -/** -Constructs an instance of the registered application class and runs it. If the -application throws a UsageError, the static usage() function is called. - -This function can only be called once. The global main() function provided by -the library normally does it automatically at program startup. -*/ -int Application::run(int argc, char **argv) -{ - static bool called=false; - if(called) - { - cerr<<"Trying to call Application::run_app twice!\n"; - return 125; - } - called=true; - - if(!reg_app_) - { - cerr<<"Trying to run with no application class registered!\n"; - return 126; - } - -#ifndef WIN32 //XXX - signal(SIGALRM, &sigalrm_); -#endif - - try - { - app_=reg_app_->create_app(argc, argv); - } - catch(const UsageError &e) - { - reg_app_->usage(argv[0], e.get_brief()); - return 1; - } - - int result=app_->main(); - delete app_; - return result; -} - -/** -Prints a message describing the usage of the application. The default version -will blame the programmer for being lazy. - -@param argv0 The value of argv[0], to be used in the message -@param brief Whether to print a brief or long usage message -*/ -void Application::usage(const char *, bool) -{ - cerr<<"The programmer was lazy and didn't write a usage() function for this application.\n"; -} - -Application::Application(): - exit_code(0), - tick_mode_(IDLE), - poller_(0), - ev_mgr_(0) -#ifndef WIN32 - //XXX Figure out how to get the current thread on win32 - ,main_tid(pthread_self()) -#endif -{ } - -/** -Default main loop. Calls tick() periodically if do_ticks is true, otherwise -just sleeps. A custom main loop should monitor the done member variable and -return exit_code. -*/ -int Application::main() -{ - done=false; - while(!done) - { - if(tick_mode_==IDLE) - { - if(poller_) - poller_->poll(0); - tick(); -#ifdef WIN32 - Sleep(0); -#else - sched_yield(); -#endif - } - else - { - if(poller_) - poller_->poll(-1); - else - { -#ifdef WIN32 - Sleep(1); -#else - timespec ts={1000,0}; - nanosleep(&ts, 0); -#endif - } - if(tick_mode_!=NONE) - tick(); - } - } - - return exit_code; -} - -/** -Sets the specified signal to be delivered to the sighandler member function. -*/ -void Application::catch_signal(int s) -{ - signal(s, &sighandler_); -} - -void Application::set_tick_mode(TickMode t) -{ - tick_mode_=t; -#ifndef WIN32 //XXX - pthread_kill(main_tid, SIGALRM); -#endif -} - -/** -Causes the application to exit gracefully with the given exit code. -*/ -void Application::exit(int c) -{ - done=true; - exit_code=c; -#ifndef WIN32 //XXX - pthread_kill(main_tid, SIGALRM); -#endif -} - -void Application::sighandler_(int s) -{ - app_->sighandler(s); -} - -Application::RegBase::RegBase() -{ - if(reg_app_) - { - cerr<<"Warning: registering the application twice\n"; - delete reg_app_; - } - - reg_app_=this; -} - -Application *Application::app_=0; -Application::RegBase *Application::reg_app_=0; - -} // namespace Msp diff --git a/source/application.h b/source/application.h deleted file mode 100644 index 5f3968a..0000000 --- a/source/application.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#ifndef MSP_FRAMEWORK_APPLICATION_H_ -#define MSP_FRAMEWORK_APPLICATION_H_ - -#include "event.h" -#include "poller.h" -#include "types.h" - -namespace Msp { - -/** -Base class for applications. Inherit the main class from this and add a static -member of type RegApp. -*/ -class Application -{ -public: - Poller::Slot &add_pollable(Pollable *, short); - EventManager::Event &create_event(); - virtual ~Application(); - - static int run(int, char **); - static void usage(const char *, bool); -protected: - enum TickMode - { - NONE, /// No ticks - AFTER_POLL, /// One tick after each poll - IDLE /// Constant torrent of ticks - }; - - class RegBase - { - public: - virtual Application *create_app(int, char **)=0; - virtual void usage(const char *, bool)=0; - virtual ~RegBase() { } - protected: - RegBase(); - }; - - template - class RegApp: public RegBase - { - public: - Application *create_app(int argc, char **argv) { return new T(argc, argv); } - void usage(const char *a, bool b) { T::usage(a,b); } - }; - - bool done; - int exit_code; - - Application(); - virtual int main(); - void catch_signal(int); - void set_tick_mode(TickMode); - void exit(int); - virtual void tick() { } - virtual void sighandler(int) { } -private: - TickMode tick_mode_; - Poller *poller_; - EventManager *ev_mgr_; - ThreadHandle main_tid; - - Application(const Application &); - Application &operator=(const Application &); - - static RegBase *reg_app_; - static Application *app_; - - static void sighandler_(int); - static void sigalrm_(int) { } -}; - -} // namespace Msp - -#endif diff --git a/source/core/Module b/source/core/Module new file mode 100644 index 0000000..536d2d4 --- /dev/null +++ b/source/core/Module @@ -0,0 +1,5 @@ +type="library" +target="mspcore" +extradirs=("time",) +installheaders="core" +installtarget=1 diff --git a/source/core/application.cpp b/source/core/application.cpp new file mode 100644 index 0000000..a9aa88e --- /dev/null +++ b/source/core/application.cpp @@ -0,0 +1,198 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#include +#include +#include "application.h" +#include "error.h" + +using namespace std; + +namespace Msp { + +Poller::Slot &Application::add_pollable(Pollable *obj, short events) +{ + if(!poller_) + poller_=new Poller; + + Poller::Slot &slot=poller_->add_pollable(obj, events); + // Interrupt a possible poll in progress +#ifndef WIN32 //XXX + pthread_kill(main_tid, SIGALRM); +#endif + return slot; +} + +EventManager::Event &Application::create_event() +{ + if(!ev_mgr_) + ev_mgr_=new EventManager(*this); + + return ev_mgr_->create_event(); +} + +Application::~Application() +{ + if(poller_) + delete poller_; + if(ev_mgr_) + delete ev_mgr_; +} + +/** +Constructs an instance of the registered application class and runs it. If the +application throws a UsageError, the static usage() function is called. + +This function can only be called once. The global main() function provided by +the library normally does it automatically at program startup. +*/ +int Application::run(int argc, char **argv) +{ + static bool called=false; + if(called) + { + cerr<<"Trying to call Application::run_app twice!\n"; + return 125; + } + called=true; + + if(!reg_app_) + { + cerr<<"Trying to run with no application class registered!\n"; + return 126; + } + +#ifndef WIN32 //XXX + signal(SIGALRM, &sigalrm_); +#endif + + try + { + app_=reg_app_->create_app(argc, argv); + } + catch(const UsageError &e) + { + reg_app_->usage(argv[0], e.get_brief()); + return 1; + } + + int result=app_->main(); + delete app_; + return result; +} + +/** +Prints a message describing the usage of the application. The default version +will blame the programmer for being lazy. + +@param argv0 The value of argv[0], to be used in the message +@param brief Whether to print a brief or long usage message +*/ +void Application::usage(const char *, bool) +{ + cerr<<"The programmer was lazy and didn't write a usage() function for this application.\n"; +} + +Application::Application(): + exit_code(0), + tick_mode_(IDLE), + poller_(0), + ev_mgr_(0) +#ifndef WIN32 + //XXX Figure out how to get the current thread on win32 + ,main_tid(pthread_self()) +#endif +{ } + +/** +Default main loop. Calls tick() periodically if do_ticks is true, otherwise +just sleeps. A custom main loop should monitor the done member variable and +return exit_code. +*/ +int Application::main() +{ + done=false; + while(!done) + { + if(tick_mode_==IDLE) + { + if(poller_) + poller_->poll(0); + tick(); +#ifdef WIN32 + Sleep(0); +#else + sched_yield(); +#endif + } + else + { + if(poller_) + poller_->poll(-1); + else + { +#ifdef WIN32 + Sleep(1); +#else + timespec ts={1000,0}; + nanosleep(&ts, 0); +#endif + } + if(tick_mode_!=NONE) + tick(); + } + } + + return exit_code; +} + +/** +Sets the specified signal to be delivered to the sighandler member function. +*/ +void Application::catch_signal(int s) +{ + signal(s, &sighandler_); +} + +void Application::set_tick_mode(TickMode t) +{ + tick_mode_=t; +#ifndef WIN32 //XXX + pthread_kill(main_tid, SIGALRM); +#endif +} + +/** +Causes the application to exit gracefully with the given exit code. +*/ +void Application::exit(int c) +{ + done=true; + exit_code=c; +#ifndef WIN32 //XXX + pthread_kill(main_tid, SIGALRM); +#endif +} + +void Application::sighandler_(int s) +{ + app_->sighandler(s); +} + +Application::RegBase::RegBase() +{ + if(reg_app_) + { + cerr<<"Warning: registering the application twice\n"; + delete reg_app_; + } + + reg_app_=this; +} + +Application *Application::app_=0; +Application::RegBase *Application::reg_app_=0; + +} // namespace Msp diff --git a/source/core/application.h b/source/core/application.h new file mode 100644 index 0000000..5f3968a --- /dev/null +++ b/source/core/application.h @@ -0,0 +1,82 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_FRAMEWORK_APPLICATION_H_ +#define MSP_FRAMEWORK_APPLICATION_H_ + +#include "event.h" +#include "poller.h" +#include "types.h" + +namespace Msp { + +/** +Base class for applications. Inherit the main class from this and add a static +member of type RegApp. +*/ +class Application +{ +public: + Poller::Slot &add_pollable(Pollable *, short); + EventManager::Event &create_event(); + virtual ~Application(); + + static int run(int, char **); + static void usage(const char *, bool); +protected: + enum TickMode + { + NONE, /// No ticks + AFTER_POLL, /// One tick after each poll + IDLE /// Constant torrent of ticks + }; + + class RegBase + { + public: + virtual Application *create_app(int, char **)=0; + virtual void usage(const char *, bool)=0; + virtual ~RegBase() { } + protected: + RegBase(); + }; + + template + class RegApp: public RegBase + { + public: + Application *create_app(int argc, char **argv) { return new T(argc, argv); } + void usage(const char *a, bool b) { T::usage(a,b); } + }; + + bool done; + int exit_code; + + Application(); + virtual int main(); + void catch_signal(int); + void set_tick_mode(TickMode); + void exit(int); + virtual void tick() { } + virtual void sighandler(int) { } +private: + TickMode tick_mode_; + Poller *poller_; + EventManager *ev_mgr_; + ThreadHandle main_tid; + + Application(const Application &); + Application &operator=(const Application &); + + static RegBase *reg_app_; + static Application *app_; + + static void sighandler_(int); + static void sigalrm_(int) { } +}; + +} // namespace Msp + +#endif diff --git a/source/core/error.h b/source/core/error.h new file mode 100644 index 0000000..c311bcd --- /dev/null +++ b/source/core/error.h @@ -0,0 +1,24 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_FRAMEWORK_ERRORS_H_ +#define MSP_FRAMEWORK_ERRORS_H_ + +#include + +namespace Msp { + +class UsageError: public Msp::Exception +{ +public: + UsageError(bool b=true): Exception(""), brief(b) { } + bool get_brief() const { return brief; } +private: + bool brief; +}; + +} // namespace Msp + +#endif diff --git a/source/core/event.cpp b/source/core/event.cpp new file mode 100644 index 0000000..01b8542 --- /dev/null +++ b/source/core/event.cpp @@ -0,0 +1,75 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifdef WIN32 +#include +#include +#endif + +#include "application.h" +#include "event.h" + +using namespace std; + +namespace Msp { + +EventManager::EventManager(Application &a): + app(a), + next_id(1) +{ + app.add_pollable(&pipe, POLLIN).signal_event.connect(sigc::mem_fun(this, &EventManager::data_available)); +} + +EventManager::Event &EventManager::create_event() +{ + events.push_back(Event(*this, next_id++)); + return events.back(); +} + +void EventManager::data_available(short) +{ + unsigned buf[1024]; + int len=pipe.read((char *)buf, sizeof(buf)); + for(unsigned i=0; i*sizeof(unsigned)<(unsigned)len; ++i) + { + for(list::iterator j=events.begin(); j!=events.end(); ++j) + if(j->get_id()==buf[i]) + j->signal_triggered.emit(); + } +} + +void EventManager::Event::trigger() +{ + mgr.pipe.write((char *)&id, sizeof(id)); +} + +EventManager::Pipe::Pipe() +{ +#ifdef WIN32 + _pipe(fd, 1024, _O_BINARY); +#else + ::pipe(fd); +#endif +} + +int EventManager::Pipe::write(char *buf, unsigned len) +{ +#ifdef WIN32 + return _write(fd[1], buf, len); +#else + return ::write(fd[1], buf, len); +#endif +} + +int EventManager::Pipe::read(char *buf, unsigned len) +{ +#ifdef WIN32 + return _read(fd[0], buf, len); +#else + return ::read(fd[0], buf, len); +#endif +} + +} // namespace Msp diff --git a/source/core/event.h b/source/core/event.h new file mode 100644 index 0000000..091d637 --- /dev/null +++ b/source/core/event.h @@ -0,0 +1,61 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_FRAMEWORK_EVENT_H_ +#define MSP_FRAMEWORK_EVENT_H_ + +#include +#include "pollable.h" + +namespace Msp { + +class Application; + +/** +Events can be used in multi-threaded applictions to trigger actions in the main +thread. +*/ +class EventManager +{ +public: + class Event + { + public: + sigc::signal signal_triggered; + + Event(EventManager &m, unsigned i): mgr(m), id(i) { } + unsigned get_id() const { return id; } + void trigger(); + private: + EventManager &mgr; + unsigned id; + }; + + EventManager(Application &); + Event &create_event(); +private: + class Pipe: public Pollable + { + public: + Pipe(); + int write(char *, unsigned); + int read(char *, unsigned); + private: + int fd[2]; + + int get_fd() { return fd[0]; } + }; + + Application &app; + Pipe pipe; + unsigned next_id; + std::list events; + + void data_available(short); +}; + +} // namespace Msp + +#endif diff --git a/source/core/main.cpp b/source/core/main.cpp new file mode 100644 index 0000000..4b187c3 --- /dev/null +++ b/source/core/main.cpp @@ -0,0 +1,22 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifdef WIN32 +#include +#endif + +#include "application.h" + +#ifdef WIN32 +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + int argc = 0; + LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc); + return Msp::Application::run(argc, (char **)argv); +} +#endif + +int main(int argc, char **argv) +{ return Msp::Application::run(argc, argv); } diff --git a/source/core/mutex.h b/source/core/mutex.h new file mode 100644 index 0000000..b619fcd --- /dev/null +++ b/source/core/mutex.h @@ -0,0 +1,73 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_FRAMEWORK_MUTEX_H_ +#define MSP_FRAMEWORK_MUTEX_H_ + +#include +#include "types.h" + +namespace Msp { + +class Mutex +{ +public: +#ifndef WIN32 + Mutex() { pthread_mutex_init(&mutex, 0); } + int lock() { return pthread_mutex_lock(&mutex); } + int trylock() { return pthread_mutex_trylock(&mutex); } + int unlock() { return pthread_mutex_unlock(&mutex); } + ~Mutex() { pthread_mutex_destroy(&mutex); } +#else + Mutex() { mutex=CreateMutex(0, false, 0); } + int lock() { return WaitForSingleObject(mutex, INFINITE)==WAIT_OBJECT_0; } + int trylock() { return WaitForSingleObject(mutex, 0)==WAIT_OBJECT_0; } + int unlock() { return !ReleaseMutex(mutex); } + ~Mutex() { CloseHandle(mutex); } +#endif +private: + MutexHandle mutex; + + friend class Semaphore; +}; + +class MutexLock +{ +public: + MutexLock(Mutex &m): mutex(m) { mutex.lock(); } + ~MutexLock() { mutex.unlock(); } +private: + Mutex &mutex; +}; + + +template +class MutexPtr: public RefCount +{ +public: + MutexPtr(T *d, Mutex &m): mutex(m), data(d) { mutex.lock(); } + MutexPtr(const MutexPtr &p): RefCount(p), mutex(p.mutex), data(p.data) { } + T &operator*() const { return *data; } + T *operator->() const { return data; } + void clear() { decref(); data=0; } + ~MutexPtr() { decref(); } +protected: + Mutex &mutex; + T *data; + + bool decref() + { + if(!RefCount::decref()) + { + mutex.unlock(); + return false; + } + return true; + } +}; + +} + +#endif diff --git a/source/core/pollable.cpp b/source/core/pollable.cpp new file mode 100644 index 0000000..4db38dd --- /dev/null +++ b/source/core/pollable.cpp @@ -0,0 +1,29 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifdef WIN32 +#include +#include "win32poll.h" +#else +#include +#endif +#include "pollable.h" + +namespace Msp { + +short Pollable::poll(short events, int timeout) +{ +#ifdef WIN32 + return 0; +#else + pollfd pfd={get_fd(), events, 0}; + int result=::poll(&pfd, 1, timeout); + if(result<=0) + return result; + return pfd.revents; +#endif +} + +} diff --git a/source/core/pollable.h b/source/core/pollable.h new file mode 100644 index 0000000..f676d55 --- /dev/null +++ b/source/core/pollable.h @@ -0,0 +1,32 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_FRAMEWORK_POLLABLE_H_ +#define MSP_FRAMEWORK_POLLABLE_H_ + +#ifdef WIN32 +#include "win32poll.h" +#endif + +#include + +namespace Msp { + +class Pollable +{ +public: + sigc::signal signal_deleted; + + virtual short poll(short, int =0); + virtual ~Pollable() { signal_deleted.emit(); } +protected: + virtual int get_fd()=0; + + friend class Poller; +}; + +} + +#endif diff --git a/source/core/poller.cpp b/source/core/poller.cpp new file mode 100644 index 0000000..4a623d4 --- /dev/null +++ b/source/core/poller.cpp @@ -0,0 +1,102 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#include "pollable.h" +#include "poller.h" + +using namespace std; + +namespace Msp { + +Poller::Slot &Poller::add_pollable(Pollable *obj, short events) +{ + MutexLock sl(slots_mutex); + + slots.push_back(new Slot(obj, events)); + if(!pfd_mutex.trylock()) + { + rebuild_pfd(); + pfd_mutex.unlock(); + } + else + dirty=true; + return *slots.back(); +} + +int Poller::poll(int timeout) +{ +#ifdef WIN32 + return 0; +#else + slots_mutex.lock(); + for(list::iterator i=slots.begin(); i!=slots.end();) + { + if((*i)->get_object()) + ++i; + else + { + delete *i; + i=slots.erase(i); + dirty=true; + } + } + + pfd_mutex.lock(); + if(dirty) + { + rebuild_pfd(); + dirty=false; + } + slots_mutex.unlock(); + + int result=::poll(&pfd[0], pfd.size(), timeout); + + if(result>0) + { + list::iterator j=slots.begin(); + for(vector::iterator i=pfd.begin(); i!=pfd.end(); ++i) + { + if(i->revents&POLLNVAL) + dirty=true; + else if(i->revents) + { + while(j!=slots.end() && (!(*j)->get_object() || (*j)->get_object()->get_fd()!=i->fd)) + ++j; + if(j==slots.end()) + break; + (*j)->signal_event.emit(i->revents); + } + } + } + + pfd_mutex.unlock(); + + return result; +#endif +} + +void Poller::rebuild_pfd() +{ + pfd.clear(); + pfd.reserve(slots.size()); + for(list::iterator i=slots.begin(); i!=slots.end(); ++i) + { + if(!(*i)->get_object() || (*i)->get_object()->get_fd()<0) + continue; + + pfd.push_back(pollfd()); + pfd.back().fd=(*i)->get_object()->get_fd(); + pfd.back().events=(*i)->get_events(); + } +} + +Poller::Slot::Slot(Pollable *o, short e): + obj(o), + events(e) +{ + obj->signal_deleted.connect(sigc::mem_fun(this, &Slot::obj_deleted)); +} + +} // namespace Msp diff --git a/source/core/poller.h b/source/core/poller.h new file mode 100644 index 0000000..dcd3658 --- /dev/null +++ b/source/core/poller.h @@ -0,0 +1,56 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_FRAMEWORK_POLLER_H_ +#define MSP_FRAMEWORK_POLLER_H_ + +#ifdef WIN32 +#include "win32poll.h" +#else +#include +#endif +#include +#include +#include "mutex.h" + +namespace Msp { + +class Pollable; + +class Poller +{ +public: + class Slot + { + public: + sigc::signal signal_event; + + Slot(Pollable *, short); + Pollable *get_object() const { return obj; } + short get_events() const { return events; } + private: + Pollable *obj; + short events; + + void obj_deleted() { obj=0; } + }; + + Slot &add_pollable(Pollable *, short); + int poll(int =0); +private: + std::list slots; + std::vector pfd; + Mutex slots_mutex; + Mutex pfd_mutex; + bool dirty; + + void remove_stale_slots(); + void rebuild_pfd(); + void pollable_deleted(Pollable *); +}; + +}; + +#endif diff --git a/source/core/semaphore.cpp b/source/core/semaphore.cpp new file mode 100644 index 0000000..9139ce1 --- /dev/null +++ b/source/core/semaphore.cpp @@ -0,0 +1,33 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#include +#include +#include "semaphore.h" +#include "../time/timestamp.h" +#include "../time/utils.h" + +namespace Msp { + +#ifndef WIN32 +int Semaphore::wait(const Time::TimeDelta &d) +{ + Time::TimeStamp ts=Time::now()+d; + + timespec timeout; + timeout.tv_sec=ts.raw()/1000000; + timeout.tv_nsec=(ts.raw()%1000000)*1000; + + MutexLock l(mutex); + int r=pthread_cond_timedwait(&cond, &mutex.mutex, &timeout); + if(r==ETIMEDOUT) + return 1; + else if(r) + return -1; + return 0; +} +#endif + +} // namespace Msp diff --git a/source/core/semaphore.h b/source/core/semaphore.h new file mode 100644 index 0000000..75c51e6 --- /dev/null +++ b/source/core/semaphore.h @@ -0,0 +1,34 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_FRAMEWORK_SEMAPHORE_H_ +#define MSP_FRAMEWORK_SEMAPHORE_H_ + +#include +#include "mutex.h" +#include "../time/timedelta.h" + +namespace Msp { + +class Semaphore +{ +public: +#ifndef WIN32 + Semaphore() { pthread_cond_init(&cond, 0); } + //Mutex &get_mutex() { return mutex; } + int signal() { MutexLock l(mutex); return pthread_cond_signal(&cond); } + int broadcast() { MutexLock l(mutex); return pthread_cond_broadcast(&cond); } + int wait() { mutex.lock(); return pthread_cond_wait(&cond, &mutex.mutex); } + int wait(const Time::TimeDelta &); + ~Semaphore() { pthread_cond_destroy(&cond); } +#endif +private: + Mutex mutex; + pthread_cond_t cond; +}; + +} + +#endif diff --git a/source/core/thread.cpp b/source/core/thread.cpp new file mode 100644 index 0000000..6858ce9 --- /dev/null +++ b/source/core/thread.cpp @@ -0,0 +1,72 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef WIN32 +#include +#endif +#include "thread.h" + +namespace Msp { + +/** +Waits for the thread to exit. Calling this from the thread will cause a +deadlock. +*/ +void Thread::join() +{ + if(!launched_) + return; + +#ifdef WIN32 + WaitForSingleObject(thread_, INFINITE); +#else + pthread_join(thread_, 0); +#endif + launched_=false; +} + +/** +Requests the thread to terminate gracefully. Currently unimplemented on win32. +*/ +void Thread::cancel() +{ +#ifndef WIN32 //XXX + pthread_cancel(thread_); +#endif +} + +/** +Violently terminates the thread. +*/ +void Thread::kill() +{ +#ifdef WIN32 + TerminateThread(thread_, 0); +#else + pthread_kill(thread_, SIGKILL); +#endif +} + +Thread::~Thread() +{ + if(launched_) + kill(); +} + +void Thread::launch() +{ + if(launched_) + return; + +#ifdef WIN32 + DWORD dummy; // Win9x needs the lpTthreadId parameter + thread_=CreateThread(0, 0, &main_, this, 0, &dummy); +#else + pthread_create(&thread_, 0, &main_, this); +#endif + launched_=true; +} + +} // namespace Msp diff --git a/source/core/thread.h b/source/core/thread.h new file mode 100644 index 0000000..2b0766c --- /dev/null +++ b/source/core/thread.h @@ -0,0 +1,50 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_FRAMEWORK_THREAD_H_ +#define MSP_FRAMEWORK_THREAD_H_ + +#include "types.h" + +namespace Msp { + +/** +Base class for threads. To create a thread for some task, derive it from this +class and implement the main() function. Note that threads are not +automatically started upon creation - you must manually call launch() instead. +This is to allow initializing variables of the derived class before the thread +is started. +*/ +class Thread +{ +public: + void join(); + void cancel(); + void kill(); + virtual ~Thread(); +protected: + Thread(): launched_(false) { } + void launch(); + virtual void main()=0; + void check_cancel(); +private: + ThreadHandle thread_; + bool launched_; + + Thread(const Thread &); + Thread &operator=(const Thread &); + +#ifdef WIN32 +# define THREAD_RETURN_ DWORD WINAPI +#else +# define THREAD_RETURN_ void * +#endif + static THREAD_RETURN_ main_(void *t) { ((Thread *)t)->main(); return 0; } +#undef THREAD_RETURN_ +}; + +} // namespace Msp + +#endif diff --git a/source/core/types.h b/source/core/types.h new file mode 100644 index 0000000..7296689 --- /dev/null +++ b/source/core/types.h @@ -0,0 +1,27 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_FRAMEWORK_TYPES_H_ +#define MSP_FRAMEWORK_TYPES_H_ + +#ifdef WIN32 +#include +#else +#include +#endif + +namespace Msp { + +#ifdef WIN32 +typedef HANDLE ThreadHandle; +typedef HANDLE MutexHandle; +#else +typedef pthread_t ThreadHandle; +typedef pthread_mutex_t MutexHandle; +#endif + +} // namespace Msp + +#endif diff --git a/source/core/win32poll.h b/source/core/win32poll.h new file mode 100644 index 0000000..f39d53e --- /dev/null +++ b/source/core/win32poll.h @@ -0,0 +1,29 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_FRAMEWORK_WIN32POLL_H_ +#define MSP_FRAMEWORK_WIN32POLL_H_ + +#ifdef WIN32 +// From Linux sys/poll.h +struct pollfd +{ + int fd; /* File descriptor to poll. */ + short int events; /* Types of events poller cares about. */ + short int revents; /* Types of events that actually occurred. */ +}; + +#ifndef POLLIN +// From Linux pth.h +#define POLLIN 0x0001 /* any readable data available */ +#endif + +#ifndef POLLNVAL +// From Linux pth.h +#define POLLNVAL 0x0020 /* requested events "invalid" */ +#endif + +#endif // Win32 +#endif diff --git a/source/error.h b/source/error.h deleted file mode 100644 index c311bcd..0000000 --- a/source/error.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#ifndef MSP_FRAMEWORK_ERRORS_H_ -#define MSP_FRAMEWORK_ERRORS_H_ - -#include - -namespace Msp { - -class UsageError: public Msp::Exception -{ -public: - UsageError(bool b=true): Exception(""), brief(b) { } - bool get_brief() const { return brief; } -private: - bool brief; -}; - -} // namespace Msp - -#endif diff --git a/source/event.cpp b/source/event.cpp deleted file mode 100644 index 01b8542..0000000 --- a/source/event.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#ifdef WIN32 -#include -#include -#endif - -#include "application.h" -#include "event.h" - -using namespace std; - -namespace Msp { - -EventManager::EventManager(Application &a): - app(a), - next_id(1) -{ - app.add_pollable(&pipe, POLLIN).signal_event.connect(sigc::mem_fun(this, &EventManager::data_available)); -} - -EventManager::Event &EventManager::create_event() -{ - events.push_back(Event(*this, next_id++)); - return events.back(); -} - -void EventManager::data_available(short) -{ - unsigned buf[1024]; - int len=pipe.read((char *)buf, sizeof(buf)); - for(unsigned i=0; i*sizeof(unsigned)<(unsigned)len; ++i) - { - for(list::iterator j=events.begin(); j!=events.end(); ++j) - if(j->get_id()==buf[i]) - j->signal_triggered.emit(); - } -} - -void EventManager::Event::trigger() -{ - mgr.pipe.write((char *)&id, sizeof(id)); -} - -EventManager::Pipe::Pipe() -{ -#ifdef WIN32 - _pipe(fd, 1024, _O_BINARY); -#else - ::pipe(fd); -#endif -} - -int EventManager::Pipe::write(char *buf, unsigned len) -{ -#ifdef WIN32 - return _write(fd[1], buf, len); -#else - return ::write(fd[1], buf, len); -#endif -} - -int EventManager::Pipe::read(char *buf, unsigned len) -{ -#ifdef WIN32 - return _read(fd[0], buf, len); -#else - return ::read(fd[0], buf, len); -#endif -} - -} // namespace Msp diff --git a/source/event.h b/source/event.h deleted file mode 100644 index 091d637..0000000 --- a/source/event.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#ifndef MSP_FRAMEWORK_EVENT_H_ -#define MSP_FRAMEWORK_EVENT_H_ - -#include -#include "pollable.h" - -namespace Msp { - -class Application; - -/** -Events can be used in multi-threaded applictions to trigger actions in the main -thread. -*/ -class EventManager -{ -public: - class Event - { - public: - sigc::signal signal_triggered; - - Event(EventManager &m, unsigned i): mgr(m), id(i) { } - unsigned get_id() const { return id; } - void trigger(); - private: - EventManager &mgr; - unsigned id; - }; - - EventManager(Application &); - Event &create_event(); -private: - class Pipe: public Pollable - { - public: - Pipe(); - int write(char *, unsigned); - int read(char *, unsigned); - private: - int fd[2]; - - int get_fd() { return fd[0]; } - }; - - Application &app; - Pipe pipe; - unsigned next_id; - std::list events; - - void data_available(short); -}; - -} // namespace Msp - -#endif diff --git a/source/main.cpp b/source/main.cpp deleted file mode 100644 index 4b187c3..0000000 --- a/source/main.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#ifdef WIN32 -#include -#endif - -#include "application.h" - -#ifdef WIN32 -int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) -{ - int argc = 0; - LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc); - return Msp::Application::run(argc, (char **)argv); -} -#endif - -int main(int argc, char **argv) -{ return Msp::Application::run(argc, argv); } diff --git a/source/mutex.h b/source/mutex.h deleted file mode 100644 index b619fcd..0000000 --- a/source/mutex.h +++ /dev/null @@ -1,73 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#ifndef MSP_FRAMEWORK_MUTEX_H_ -#define MSP_FRAMEWORK_MUTEX_H_ - -#include -#include "types.h" - -namespace Msp { - -class Mutex -{ -public: -#ifndef WIN32 - Mutex() { pthread_mutex_init(&mutex, 0); } - int lock() { return pthread_mutex_lock(&mutex); } - int trylock() { return pthread_mutex_trylock(&mutex); } - int unlock() { return pthread_mutex_unlock(&mutex); } - ~Mutex() { pthread_mutex_destroy(&mutex); } -#else - Mutex() { mutex=CreateMutex(0, false, 0); } - int lock() { return WaitForSingleObject(mutex, INFINITE)==WAIT_OBJECT_0; } - int trylock() { return WaitForSingleObject(mutex, 0)==WAIT_OBJECT_0; } - int unlock() { return !ReleaseMutex(mutex); } - ~Mutex() { CloseHandle(mutex); } -#endif -private: - MutexHandle mutex; - - friend class Semaphore; -}; - -class MutexLock -{ -public: - MutexLock(Mutex &m): mutex(m) { mutex.lock(); } - ~MutexLock() { mutex.unlock(); } -private: - Mutex &mutex; -}; - - -template -class MutexPtr: public RefCount -{ -public: - MutexPtr(T *d, Mutex &m): mutex(m), data(d) { mutex.lock(); } - MutexPtr(const MutexPtr &p): RefCount(p), mutex(p.mutex), data(p.data) { } - T &operator*() const { return *data; } - T *operator->() const { return data; } - void clear() { decref(); data=0; } - ~MutexPtr() { decref(); } -protected: - Mutex &mutex; - T *data; - - bool decref() - { - if(!RefCount::decref()) - { - mutex.unlock(); - return false; - } - return true; - } -}; - -} - -#endif diff --git a/source/pollable.cpp b/source/pollable.cpp deleted file mode 100644 index 4db38dd..0000000 --- a/source/pollable.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#ifdef WIN32 -#include -#include "win32poll.h" -#else -#include -#endif -#include "pollable.h" - -namespace Msp { - -short Pollable::poll(short events, int timeout) -{ -#ifdef WIN32 - return 0; -#else - pollfd pfd={get_fd(), events, 0}; - int result=::poll(&pfd, 1, timeout); - if(result<=0) - return result; - return pfd.revents; -#endif -} - -} diff --git a/source/pollable.h b/source/pollable.h deleted file mode 100644 index f676d55..0000000 --- a/source/pollable.h +++ /dev/null @@ -1,32 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#ifndef MSP_FRAMEWORK_POLLABLE_H_ -#define MSP_FRAMEWORK_POLLABLE_H_ - -#ifdef WIN32 -#include "win32poll.h" -#endif - -#include - -namespace Msp { - -class Pollable -{ -public: - sigc::signal signal_deleted; - - virtual short poll(short, int =0); - virtual ~Pollable() { signal_deleted.emit(); } -protected: - virtual int get_fd()=0; - - friend class Poller; -}; - -} - -#endif diff --git a/source/poller.cpp b/source/poller.cpp deleted file mode 100644 index 4a623d4..0000000 --- a/source/poller.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#include "pollable.h" -#include "poller.h" - -using namespace std; - -namespace Msp { - -Poller::Slot &Poller::add_pollable(Pollable *obj, short events) -{ - MutexLock sl(slots_mutex); - - slots.push_back(new Slot(obj, events)); - if(!pfd_mutex.trylock()) - { - rebuild_pfd(); - pfd_mutex.unlock(); - } - else - dirty=true; - return *slots.back(); -} - -int Poller::poll(int timeout) -{ -#ifdef WIN32 - return 0; -#else - slots_mutex.lock(); - for(list::iterator i=slots.begin(); i!=slots.end();) - { - if((*i)->get_object()) - ++i; - else - { - delete *i; - i=slots.erase(i); - dirty=true; - } - } - - pfd_mutex.lock(); - if(dirty) - { - rebuild_pfd(); - dirty=false; - } - slots_mutex.unlock(); - - int result=::poll(&pfd[0], pfd.size(), timeout); - - if(result>0) - { - list::iterator j=slots.begin(); - for(vector::iterator i=pfd.begin(); i!=pfd.end(); ++i) - { - if(i->revents&POLLNVAL) - dirty=true; - else if(i->revents) - { - while(j!=slots.end() && (!(*j)->get_object() || (*j)->get_object()->get_fd()!=i->fd)) - ++j; - if(j==slots.end()) - break; - (*j)->signal_event.emit(i->revents); - } - } - } - - pfd_mutex.unlock(); - - return result; -#endif -} - -void Poller::rebuild_pfd() -{ - pfd.clear(); - pfd.reserve(slots.size()); - for(list::iterator i=slots.begin(); i!=slots.end(); ++i) - { - if(!(*i)->get_object() || (*i)->get_object()->get_fd()<0) - continue; - - pfd.push_back(pollfd()); - pfd.back().fd=(*i)->get_object()->get_fd(); - pfd.back().events=(*i)->get_events(); - } -} - -Poller::Slot::Slot(Pollable *o, short e): - obj(o), - events(e) -{ - obj->signal_deleted.connect(sigc::mem_fun(this, &Slot::obj_deleted)); -} - -} // namespace Msp diff --git a/source/poller.h b/source/poller.h deleted file mode 100644 index dcd3658..0000000 --- a/source/poller.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#ifndef MSP_FRAMEWORK_POLLER_H_ -#define MSP_FRAMEWORK_POLLER_H_ - -#ifdef WIN32 -#include "win32poll.h" -#else -#include -#endif -#include -#include -#include "mutex.h" - -namespace Msp { - -class Pollable; - -class Poller -{ -public: - class Slot - { - public: - sigc::signal signal_event; - - Slot(Pollable *, short); - Pollable *get_object() const { return obj; } - short get_events() const { return events; } - private: - Pollable *obj; - short events; - - void obj_deleted() { obj=0; } - }; - - Slot &add_pollable(Pollable *, short); - int poll(int =0); -private: - std::list slots; - std::vector pfd; - Mutex slots_mutex; - Mutex pfd_mutex; - bool dirty; - - void remove_stale_slots(); - void rebuild_pfd(); - void pollable_deleted(Pollable *); -}; - -}; - -#endif diff --git a/source/semaphore.h b/source/semaphore.h deleted file mode 100644 index 36e9690..0000000 --- a/source/semaphore.h +++ /dev/null @@ -1,30 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#ifndef MSP_FRAMEWORK_SEMAPHORE_H_ -#define MSP_FRAMEWORK_SEMAPHORE_H_ - -#include -#include "mutex.h" - -namespace Msp { - -class Semaphore -{ -public: - Semaphore() { pthread_cond_init(&cond, 0); } - Mutex &get_mutex() { return mutex; } - int signal() { return pthread_cond_signal(&cond); } - int broadcast() { return pthread_cond_broadcast(&cond); } - int wait() { return pthread_cond_wait(&cond, &mutex.mutex); } - ~Semaphore() { pthread_cond_destroy(&cond); } -private: - Mutex mutex; - pthread_cond_t cond; -}; - -} - -#endif diff --git a/source/thread.cpp b/source/thread.cpp deleted file mode 100644 index 6858ce9..0000000 --- a/source/thread.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#ifndef WIN32 -#include -#endif -#include "thread.h" - -namespace Msp { - -/** -Waits for the thread to exit. Calling this from the thread will cause a -deadlock. -*/ -void Thread::join() -{ - if(!launched_) - return; - -#ifdef WIN32 - WaitForSingleObject(thread_, INFINITE); -#else - pthread_join(thread_, 0); -#endif - launched_=false; -} - -/** -Requests the thread to terminate gracefully. Currently unimplemented on win32. -*/ -void Thread::cancel() -{ -#ifndef WIN32 //XXX - pthread_cancel(thread_); -#endif -} - -/** -Violently terminates the thread. -*/ -void Thread::kill() -{ -#ifdef WIN32 - TerminateThread(thread_, 0); -#else - pthread_kill(thread_, SIGKILL); -#endif -} - -Thread::~Thread() -{ - if(launched_) - kill(); -} - -void Thread::launch() -{ - if(launched_) - return; - -#ifdef WIN32 - DWORD dummy; // Win9x needs the lpTthreadId parameter - thread_=CreateThread(0, 0, &main_, this, 0, &dummy); -#else - pthread_create(&thread_, 0, &main_, this); -#endif - launched_=true; -} - -} // namespace Msp diff --git a/source/thread.h b/source/thread.h deleted file mode 100644 index 2b0766c..0000000 --- a/source/thread.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#ifndef MSP_FRAMEWORK_THREAD_H_ -#define MSP_FRAMEWORK_THREAD_H_ - -#include "types.h" - -namespace Msp { - -/** -Base class for threads. To create a thread for some task, derive it from this -class and implement the main() function. Note that threads are not -automatically started upon creation - you must manually call launch() instead. -This is to allow initializing variables of the derived class before the thread -is started. -*/ -class Thread -{ -public: - void join(); - void cancel(); - void kill(); - virtual ~Thread(); -protected: - Thread(): launched_(false) { } - void launch(); - virtual void main()=0; - void check_cancel(); -private: - ThreadHandle thread_; - bool launched_; - - Thread(const Thread &); - Thread &operator=(const Thread &); - -#ifdef WIN32 -# define THREAD_RETURN_ DWORD WINAPI -#else -# define THREAD_RETURN_ void * -#endif - static THREAD_RETURN_ main_(void *t) { ((Thread *)t)->main(); return 0; } -#undef THREAD_RETURN_ -}; - -} // namespace Msp - -#endif diff --git a/source/time/Module b/source/time/Module new file mode 100644 index 0000000..9ccc1f2 --- /dev/null +++ b/source/time/Module @@ -0,0 +1 @@ +installheaders="time" diff --git a/source/time/timedelta.cpp b/source/time/timedelta.cpp new file mode 100644 index 0000000..891d560 --- /dev/null +++ b/source/time/timedelta.cpp @@ -0,0 +1,99 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#include +#include "timedelta.h" +#include "units.h" + +using namespace std; + +namespace Msp { +namespace Time { + +void print_part(ostream &out, int64_t &value, int64_t unit, char sep, bool &first) +{ + if(value>=unit || !first) + { + if(first) + out< +#include +#include + +namespace Msp { +namespace Time { + +/** +Represents a quantity of time, such as five seconds. +*/ +class TimeDelta +{ +public: + /** + Constructs a zero TimeDelta. + */ + TimeDelta(): usec(0) { } + + /** + Constructs a TimeDelta from a plain number. The purpose of this is to allow + serialization together with the raw() function. For creating TimeDeltas + with a specific length, see units.h. + */ + explicit TimeDelta(int64_t u): usec(u) { } + + /** + Returns the raw number stored inside the TimeDelta. This should only be used + for serialization and the result should not be interpreted in any way. + */ + int64_t raw() const { return usec; } + + /** + Fills in a timespec struct. To get a meaningful scalar value from the + TimeDelta, divide with one of the values in units.h. + */ + void fill_timespec(timespec &ts) const { ts.tv_sec=usec/1000000; ts.tv_nsec=(usec%1000000)*1000; } + + TimeDelta operator+(const TimeDelta &t) const { return TimeDelta(usec+t.usec); } + TimeDelta &operator+=(const TimeDelta &t) { usec+=t.usec; return *this; } + TimeDelta operator-(const TimeDelta &t) const { return TimeDelta(usec-t.usec); } + TimeDelta &operator-=(const TimeDelta &t) { usec-=t.usec; return *this; } + + template + TimeDelta operator*(T a) const { return TimeDelta((int64_t)(usec*a)); } + template + TimeDelta &operator*=(T a) { usec=(int64_t)(usec*a); return *this; } + + template + TimeDelta operator/(T a) const { return TimeDelta((int64_t)(usec/a)); } + template + TimeDelta &operator/=(T a) { usec=(int64_t)(usec/a); return *this; } + + double operator/(const TimeDelta &t) const { return (double)usec/t.usec; } + + bool operator>(const TimeDelta &t) const { return usec>t.usec; } + bool operator>=(const TimeDelta &t) const { return usec>=t.usec; } + bool operator<(const TimeDelta &t) const { return usec +inline TimeDelta operator*(T a, const TimeDelta &t) { return t*a; } + +} // namespace Time +} // namespace Msp + +#endif diff --git a/source/time/timer.cpp b/source/time/timer.cpp new file mode 100644 index 0000000..641d55f --- /dev/null +++ b/source/time/timer.cpp @@ -0,0 +1,119 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#include +#include "timer.h" +#include "utils.h" + +using namespace std; + +namespace Msp { +namespace Time { + +Timer::Timer(const Time::TimeDelta &d): + interval(d), + timeout(now()+d) +{ + MutexLock l(set_mutex); + timers.insert(this); + thread.nudge(); +} + +Timer::~Timer() +{ + MutexLock l(set_mutex); + timers.erase(this); + thread.nudge(); +} + +Timer::ThreadProxy Timer::thread; +Mutex Timer::set_mutex; +set Timer::timers; + +Timer::Thread::Thread(): + done(false) +{ + launch(); +} + +/** +Notifies the thread that a change in the timers occurred. +*/ +void Timer::Thread::nudge() +{ + sem.signal(); +} + +/** +Tells the thread to finish and terminate gracefully. This function will return +after the thread has terminated. +*/ +void Timer::Thread::finish() +{ + if(!done) + { + done=true; + sem.signal(); + } + + join(); +} + +void Timer::Thread::main() +{ + while(!done) + { + set_mutex.lock(); + Timer *next=0; + TimeStamp next_ts; + for(set::iterator i=timers.begin(); i!=timers.end(); ++i) + { + const TimeStamp &ts=(*i)->get_timeout(); + if(tssignal_timeout.emit()) + next->timeout+=next->interval; + else + delete next; + } + } + else + pause(); + } +} + +/** +Creates the thread if it doesn't exist, otherwise nudges it. +*/ +void Timer::ThreadProxy::nudge() +{ + if(!thread) + thread=new Thread(); + else + thread->nudge(); +} + +Timer::ThreadProxy::~ThreadProxy() +{ + if(thread) + { + thread->finish(); + delete thread; + } +} + +} // namespace Time +} // namespace Msp diff --git a/source/time/timer.h b/source/time/timer.h new file mode 100644 index 0000000..ef7ea8b --- /dev/null +++ b/source/time/timer.h @@ -0,0 +1,78 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_TIME_TIMER_H_ +#define MSP_TIME_TIMER_H_ + +#include +#include +#include "../core/mutex.h" +#include "../core/semaphore.h" +#include "../core/thread.h" +#include "timedelta.h" +#include "timestamp.h" + +namespace Msp { +namespace Time { + +/** +A class for executing functions periodically. Every time the timeout is +reached, signal_timeout will be emitted. If the functor connected to this +signal returns true, the timer is rescheduled by incrementing the timeout +by the interval. Otherwise the timer is canceled. + +A separate thread is used for running the timers. All signal emissions will +happen in this thread - be careful with your variables. +*/ +class Timer +{ +public: + sigc::signal signal_timeout; + + Timer(const Time::TimeDelta &); + const Time::TimeStamp &get_timeout() const { return timeout; } + ~Timer(); +private: + /** + A thread to run the timers independently of the rest of the program. + */ + class Thread: public Msp::Thread + { + public: + Thread(); + void nudge(); + void finish(); + private: + bool done; + Semaphore sem; + + void main(); + }; + + /** + Proxy class to handle automatic starting and termination of the thread. + */ + class ThreadProxy + { + public: + ThreadProxy(): thread(0) { } + void nudge(); + ~ThreadProxy(); + private: + Thread *thread; + }; + + Time::TimeDelta interval; + Time::TimeStamp timeout; + + static ThreadProxy thread; + static Mutex set_mutex; + static std::set timers; +}; + +} // namespace Time +} // namespace Msp + +#endif diff --git a/source/time/timestamp.h b/source/time/timestamp.h new file mode 100644 index 0000000..da4e457 --- /dev/null +++ b/source/time/timestamp.h @@ -0,0 +1,62 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_TIME_TIMESTAMP_H_ +#define MSP_TIME_TIMESTAMP_H_ + +#include +#include "timedelta.h" + +namespace Msp { +namespace Time { + +/** +Represents a moment in time, such as the last tick. This class is NOT intended +to be used for storing arbitary user-defined times, I'll add a DateTime class +if the need arises. +*/ +class TimeStamp +{ +public: + /** + Construct a TimeStamp that represents an arbitarily distant point in the + past. It's guaranteed to be less than any valid timestamp. + */ + TimeStamp(): usec(0) { } + + /** + Constructs a TimeStamp from a plain number. The purpose of this is to allow + serialization together with the raw() function. + */ + explicit TimeStamp(int64_t u): usec(u) { } + + /** + Returns the raw number stored inside the TimeStamp. This should only be used + for serialization and the result should not be interpreted in any way. + */ + int64_t raw() const { return usec; } + + TimeStamp operator+(const TimeDelta &t) const { return TimeStamp(usec+t.raw()); } + TimeStamp &operator+=(const TimeDelta &t) { usec+=t.raw(); return *this; } + TimeStamp operator-(const TimeDelta &t) const { return TimeStamp(usec-t.raw()); } + TimeStamp &operator-=(const TimeDelta &t) { usec-=t.raw(); return *this; } + TimeDelta operator-(const TimeStamp &t) const { return TimeDelta(usec-t.usec); } + bool operator>=(const TimeStamp &t) const { return usec>=t.usec; } + bool operator>(const TimeStamp &t) const { return usec>t.usec; } + bool operator<=(const TimeStamp &t) const { return usec<=t.usec; } + bool operator<(const TimeStamp &t) const { return usec0; } + + static TimeStamp from_unixtime(time_t t) { return TimeStamp(t*1000000LL); } +private: + int64_t usec; +}; + +} // namespace Time +} // namespace Msp + +#endif diff --git a/source/time/units.cpp b/source/time/units.cpp new file mode 100644 index 0000000..e37987b --- /dev/null +++ b/source/time/units.cpp @@ -0,0 +1,21 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#include "units.h" + +namespace Msp { +namespace Time { + +// Constants to be used in creation of TimeDeltas +const TimeDelta usec(1); +const TimeDelta msec(1000); +const TimeDelta sec(1000000); +const TimeDelta min(60*1000000); +const TimeDelta hour(3600*1000000LL); +const TimeDelta day(86400*1000000LL); +const TimeDelta week(7*86400*1000000LL); + +} // namespace Time +} // namespace Msp diff --git a/source/time/units.h b/source/time/units.h new file mode 100644 index 0000000..6417782 --- /dev/null +++ b/source/time/units.h @@ -0,0 +1,25 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_TIME_UNITS_H_ +#define MSP_TIME_UNITS_H_ + +#include "timedelta.h" + +namespace Msp { +namespace Time { + +extern const TimeDelta usec; +extern const TimeDelta msec; +extern const TimeDelta sec; +extern const TimeDelta min; +extern const TimeDelta hour; +extern const TimeDelta day; +extern const TimeDelta week; + +} // namespace Time +} // namespace Msp + +#endif diff --git a/source/time/utils.cpp b/source/time/utils.cpp new file mode 100644 index 0000000..00c389d --- /dev/null +++ b/source/time/utils.cpp @@ -0,0 +1,52 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#include +#include +#include "timedelta.h" +#include "timestamp.h" +#include "units.h" +#include "utils.h" + +namespace Msp { +namespace Time { + +/** +Returns the current timestamp. +*/ +TimeStamp now() +{ + timeval tv; + gettimeofday(&tv, 0); + return TimeStamp(tv.tv_sec*1000000LL+tv.tv_usec); +} + +/** +Returns the CPU time used by the program so far. +*/ +TimeDelta get_cpu_time() +{ + rusage ru; + getrusage(RUSAGE_SELF, &ru); + return (ru.ru_utime.tv_sec+ru.ru_stime.tv_sec)*sec + (ru.ru_utime.tv_usec+ru.ru_stime.tv_usec)*usec; +} + +/** +Sleeps for the given time. +*/ +int sleep(const TimeDelta &d) +{ +#ifndef WIN32 + timespec ts; + d.fill_timespec(ts); + return nanosleep(&ts, 0); +#else + Sleep(d/msec); + return 0; +#endif +} + +} // namespace Time +} // namespace Msp diff --git a/source/time/utils.h b/source/time/utils.h new file mode 100644 index 0000000..9efe39a --- /dev/null +++ b/source/time/utils.h @@ -0,0 +1,22 @@ +/* +This file is part of libmspframework +Copyright © 2006 Mikko Rasa, Mikkosoft Productions +Distributed under the LGPL +*/ +#ifndef MSP_TIME_UTILS_H_ +#define MSP_TIME_UTILS_H_ + +namespace Msp { +namespace Time { + +class TimeDelta; +class TimeStamp; + +extern TimeStamp now(); +extern TimeDelta get_cpu_time(); +extern int sleep(const TimeDelta &); + +} // namespace Time +} // namespace Msp + +#endif diff --git a/source/types.h b/source/types.h deleted file mode 100644 index 7296689..0000000 --- a/source/types.h +++ /dev/null @@ -1,27 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#ifndef MSP_FRAMEWORK_TYPES_H_ -#define MSP_FRAMEWORK_TYPES_H_ - -#ifdef WIN32 -#include -#else -#include -#endif - -namespace Msp { - -#ifdef WIN32 -typedef HANDLE ThreadHandle; -typedef HANDLE MutexHandle; -#else -typedef pthread_t ThreadHandle; -typedef pthread_mutex_t MutexHandle; -#endif - -} // namespace Msp - -#endif diff --git a/source/win32poll.h b/source/win32poll.h deleted file mode 100644 index f39d53e..0000000 --- a/source/win32poll.h +++ /dev/null @@ -1,29 +0,0 @@ -/* -This file is part of libmspframework -Copyright © 2006 Mikko Rasa, Mikkosoft Productions -Distributed under the LGPL -*/ -#ifndef MSP_FRAMEWORK_WIN32POLL_H_ -#define MSP_FRAMEWORK_WIN32POLL_H_ - -#ifdef WIN32 -// From Linux sys/poll.h -struct pollfd -{ - int fd; /* File descriptor to poll. */ - short int events; /* Types of events poller cares about. */ - short int revents; /* Types of events that actually occurred. */ -}; - -#ifndef POLLIN -// From Linux pth.h -#define POLLIN 0x0001 /* any readable data available */ -#endif - -#ifndef POLLNVAL -// From Linux pth.h -#define POLLNVAL 0x0020 /* requested events "invalid" */ -#endif - -#endif // Win32 -#endif