-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",)
+++ /dev/null
-type="library"
-target="mspframework"
-installheaders="framework"
-installtarget=1
+++ /dev/null
-/*
-This file is part of libmspframework
-Copyright © 2006 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-#include <signal.h>
-#include <iostream>
-#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
+++ /dev/null
-/*
-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<MainClass>.
-*/
-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<typename T>
- 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
--- /dev/null
+type="library"
+target="mspcore"
+extradirs=("time",)
+installheaders="core"
+installtarget=1
--- /dev/null
+/*
+This file is part of libmspframework
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#include <signal.h>
+#include <iostream>
+#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
--- /dev/null
+/*
+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<MainClass>.
+*/
+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<typename T>
+ 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
--- /dev/null
+/*
+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 <msp/error.h>
+
+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
--- /dev/null
+/*
+This file is part of libmspframework
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#ifdef WIN32
+#include <io.h>
+#include <fcntl.h>
+#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<Event>::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
--- /dev/null
+/*
+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 <sigc++/sigc++.h>
+#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<void> 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<Event> events;
+
+ void data_available(short);
+};
+
+} // namespace Msp
+
+#endif
--- /dev/null
+/*
+This file is part of libmspframework
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#ifdef WIN32
+#include <windows.h>
+#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); }
--- /dev/null
+/*
+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 <msp/refcount.h>
+#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<typename T>
+class MutexPtr: public RefCount
+{
+public:
+ MutexPtr(T *d, Mutex &m): mutex(m), data(d) { mutex.lock(); }
+ MutexPtr(const MutexPtr<T> &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
--- /dev/null
+/*
+This file is part of libmspframework
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#ifdef WIN32
+#include <winsock2.h>
+#include "win32poll.h"
+#else
+#include <poll.h>
+#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
+}
+
+}
--- /dev/null
+/*
+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 <sigc++/sigc++.h>
+
+namespace Msp {
+
+class Pollable
+{
+public:
+ sigc::signal<void> signal_deleted;
+
+ virtual short poll(short, int =0);
+ virtual ~Pollable() { signal_deleted.emit(); }
+protected:
+ virtual int get_fd()=0;
+
+ friend class Poller;
+};
+
+}
+
+#endif
--- /dev/null
+/*
+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<Slot *>::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<Slot *>::iterator j=slots.begin();
+ for(vector<pollfd>::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<Slot *>::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
--- /dev/null
+/*
+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 <sys/poll.h>
+#endif
+#include <vector>
+#include <sigc++/sigc++.h>
+#include "mutex.h"
+
+namespace Msp {
+
+class Pollable;
+
+class Poller
+{
+public:
+ class Slot
+ {
+ public:
+ sigc::signal<void, short> 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<Slot *> slots;
+ std::vector<pollfd> pfd;
+ Mutex slots_mutex;
+ Mutex pfd_mutex;
+ bool dirty;
+
+ void remove_stale_slots();
+ void rebuild_pfd();
+ void pollable_deleted(Pollable *);
+};
+
+};
+
+#endif
--- /dev/null
+/*
+This file is part of libmspframework
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#include <sys/time.h>
+#include <errno.h>
+#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
--- /dev/null
+/*
+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 <pthread.h>
+#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
--- /dev/null
+/*
+This file is part of libmspframework
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#ifndef WIN32
+#include <signal.h>
+#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
--- /dev/null
+/*
+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
--- /dev/null
+/*
+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 <windows.h>
+#else
+#include <pthread.h>
+#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
--- /dev/null
+/*
+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
+++ /dev/null
-/*
-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 <msp/error.h>
-
-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
+++ /dev/null
-/*
-This file is part of libmspframework
-Copyright © 2006 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-#ifdef WIN32
-#include <io.h>
-#include <fcntl.h>
-#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<Event>::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
+++ /dev/null
-/*
-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 <sigc++/sigc++.h>
-#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<void> 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<Event> events;
-
- void data_available(short);
-};
-
-} // namespace Msp
-
-#endif
+++ /dev/null
-/*
-This file is part of libmspframework
-Copyright © 2006 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-#ifdef WIN32
-#include <windows.h>
-#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); }
+++ /dev/null
-/*
-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 <msp/refcount.h>
-#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<typename T>
-class MutexPtr: public RefCount
-{
-public:
- MutexPtr(T *d, Mutex &m): mutex(m), data(d) { mutex.lock(); }
- MutexPtr(const MutexPtr<T> &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
+++ /dev/null
-/*
-This file is part of libmspframework
-Copyright © 2006 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-#ifdef WIN32
-#include <winsock2.h>
-#include "win32poll.h"
-#else
-#include <poll.h>
-#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
-}
-
-}
+++ /dev/null
-/*
-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 <sigc++/sigc++.h>
-
-namespace Msp {
-
-class Pollable
-{
-public:
- sigc::signal<void> signal_deleted;
-
- virtual short poll(short, int =0);
- virtual ~Pollable() { signal_deleted.emit(); }
-protected:
- virtual int get_fd()=0;
-
- friend class Poller;
-};
-
-}
-
-#endif
+++ /dev/null
-/*
-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<Slot *>::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<Slot *>::iterator j=slots.begin();
- for(vector<pollfd>::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<Slot *>::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
+++ /dev/null
-/*
-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 <sys/poll.h>
-#endif
-#include <vector>
-#include <sigc++/sigc++.h>
-#include "mutex.h"
-
-namespace Msp {
-
-class Pollable;
-
-class Poller
-{
-public:
- class Slot
- {
- public:
- sigc::signal<void, short> 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<Slot *> slots;
- std::vector<pollfd> pfd;
- Mutex slots_mutex;
- Mutex pfd_mutex;
- bool dirty;
-
- void remove_stale_slots();
- void rebuild_pfd();
- void pollable_deleted(Pollable *);
-};
-
-};
-
-#endif
+++ /dev/null
-/*
-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 <pthread.h>
-#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
+++ /dev/null
-/*
-This file is part of libmspframework
-Copyright © 2006 Mikko Rasa, Mikkosoft Productions
-Distributed under the LGPL
-*/
-#ifndef WIN32
-#include <signal.h>
-#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
+++ /dev/null
-/*
-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
--- /dev/null
+installheaders="time"
--- /dev/null
+/*
+This file is part of libmspframework
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#include <sstream>
+#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<<value/unit;
+ else
+ {
+ out.width(2);
+ out<<value/unit;
+ }
+ if(sep) out<<sep;
+ value%=unit;
+ first=false;
+ }
+}
+
+ostream &operator<<(ostream &out, const TimeDelta &td)
+{
+ ostringstream ss;
+ ss.fill('0');
+ if(td.usec<1000)
+ ss<<td.usec<<"µs";
+ else if(td.usec<1000000)
+ {
+ ss<<td.usec/1000;
+ if(td.usec%1000)
+ {
+ ss<<'.';
+ ss.width(3);
+ ss<<td.usec%1000;
+ }
+ ss<<"ms";
+ }
+ else if(td.usec<60000000)
+ {
+ ss<<td.usec/1000000;
+ if(td.usec%1000000)
+ {
+ ss<<'.';
+ if(td.usec%1000)
+ {
+ ss.width(6);
+ ss<<td.usec%1000000;
+ }
+ else
+ {
+ ss.width(3);
+ ss<<td.usec/1000%1000;
+ }
+ }
+ ss<<"s";
+ }
+ else
+ {
+ int64_t temp=td.usec;
+ bool first=true;
+ print_part(ss, temp, day.raw(), '-', first);
+ print_part(ss, temp, hour.raw(), ':', first);
+ print_part(ss, temp, min.raw(), ':', first);
+ print_part(ss, temp, sec.raw(), 0, first);
+ if(temp)
+ {
+ ss<<'.';
+ if(temp%1000)
+ {
+ ss.width(6);
+ ss<<temp;
+ }
+ else
+ {
+ ss.width(3);
+ ss<<temp/1000;
+ }
+ }
+ ss<<"s";
+ }
+
+ out<<ss.str();
+
+ return out;
+}
+
+} // namespace Time
+} // namespace Msp
--- /dev/null
+/*
+This file is part of libmspframework
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#ifndef MSP_TIME_TIMEDELTA_H_
+#define MSP_TIME_TIMEDELTA_H_
+
+#include <stdint.h>
+#include <time.h>
+#include <ostream>
+
+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<typename T>
+ TimeDelta operator*(T a) const { return TimeDelta((int64_t)(usec*a)); }
+ template<typename T>
+ TimeDelta &operator*=(T a) { usec=(int64_t)(usec*a); return *this; }
+
+ template<typename T>
+ TimeDelta operator/(T a) const { return TimeDelta((int64_t)(usec/a)); }
+ template<typename T>
+ 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<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!=t.usec; }
+
+ operator bool() const { return usec; }
+
+ friend std::ostream &operator<<(std::ostream &, const TimeDelta &);
+private:
+ int64_t usec;
+};
+
+template<typename T>
+inline TimeDelta operator*(T a, const TimeDelta &t) { return t*a; }
+
+} // namespace Time
+} // namespace Msp
+
+#endif
--- /dev/null
+/*
+This file is part of libmspframework
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#include <signal.h>
+#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 *> 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<Timer *>::iterator i=timers.begin(); i!=timers.end(); ++i)
+ {
+ const TimeStamp &ts=(*i)->get_timeout();
+ if(ts<next_ts || !next)
+ {
+ next_ts=ts;
+ next=*i;
+ }
+ }
+ set_mutex.unlock();
+
+ if(next)
+ {
+ const TimeStamp t=now();
+ if(next_ts<=t || sem.wait(next_ts-t)==1)
+ {
+ if(next->signal_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
--- /dev/null
+/*
+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 <set>
+#include <sigc++/sigc++.h>
+#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<bool> 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<Timer *> timers;
+};
+
+} // namespace Time
+} // namespace Msp
+
+#endif
--- /dev/null
+/*
+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 <stdint.h>
+#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 usec<t.usec; }
+ bool operator==(const TimeStamp &t) const { return usec==t.usec; }
+ bool operator!=(const TimeStamp &t) const { return usec!=t.usec; }
+ operator bool() const { return usec>0; }
+
+ static TimeStamp from_unixtime(time_t t) { return TimeStamp(t*1000000LL); }
+private:
+ int64_t usec;
+};
+
+} // namespace Time
+} // namespace Msp
+
+#endif
--- /dev/null
+/*
+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
--- /dev/null
+/*
+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
--- /dev/null
+/*
+This file is part of libmspframework
+Copyright © 2006 Mikko Rasa, Mikkosoft Productions
+Distributed under the LGPL
+*/
+#include <sys/resource.h>
+#include <sys/time.h>
+#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
--- /dev/null
+/*
+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
+++ /dev/null
-/*
-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 <windows.h>
-#else
-#include <pthread.h>
-#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
+++ /dev/null
-/*
-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