]> git.llucax.com Git - software/ev.d.git/commitdiff
Add initial D-ish libev API.
authorLeandro Lucarella <llucax@gmail.com>
Wed, 13 Feb 2008 21:59:43 +0000 (19:59 -0200)
committerLeandro Lucarella <llucax@gmail.com>
Wed, 13 Feb 2008 21:59:43 +0000 (19:59 -0200)
This is almost complete, but not that well tested. Some unittest are
provided but they may not survive, because they are very intrusive for
any application compiled with unittests.

Signed-off-by: Leandro Lucarella <llucax@gmail.com>
ev/d.d [new file with mode: 0644]

diff --git a/ev/d.d b/ev/d.d
new file mode 100644 (file)
index 0000000..8c47c10
--- /dev/null
+++ b/ev/d.d
@@ -0,0 +1,893 @@
+/+
+ + D Programming Language "bindings" to libev
+ + <http://software.schmorp.de/pkg/libev.html>
+ +
+ + Written by Leandro Lucarella (2008).
+ +
+ + Placed under BOLA license <http://auriga.wearlab.de/~alb/bola/> which is
+ + basically public domain.
+ +
+ +/
+
+module ev.d;
+
+import ev.c;
+import std.string;
+
+enum: uint
+{
+       UNDEF    = EV_UNDEF,
+       NONE     = EV_NONE,
+       READ     = EV_READ,
+       WRITE    = EV_WRITE,
+       IOFDSET  = EV_IOFDSET,
+       TIMEOUT  = EV_TIMEOUT,
+       PERIODIC = EV_PERIODIC,
+       SIGNAL   = EV_SIGNAL,
+       CHILD    = EV_CHILD,
+       STAT     = EV_STAT,
+       IDLE     = EV_IDLE,
+       PREPARE  = EV_PREPARE,
+       CHECK    = EV_CHECK,
+       EMBED    = EV_EMBED,
+       FORK     = EV_FORK,
+       ERROR    = EV_ERROR,
+}
+
+enum: uint
+{
+       AUTO       = EVFLAG_AUTO,
+       NOENV      = EVFLAG_NOENV,
+       FORKCHECK  = EVFLAG_FORKCHECK,
+       SELECT     = EVBACKEND_SELECT,
+       POLL       = EVBACKEND_POLL,
+       EPOLL      = EVBACKEND_EPOLL,
+       KQUEUE     = EVBACKEND_KQUEUE,
+       DEVPOLL    = EVBACKEND_DEVPOLL,
+       PORT       = EVBACKEND_PORT,
+}
+
+enum
+{
+       NONBLOCK = EVLOOP_NONBLOCK,
+       ONESHOT  = EVLOOP_ONESHOT,
+}
+
+enum Unloop
+{
+       CANCEL = EVUNLOOP_CANCEL,
+       ONE    = EVUNLOOP_ONE,
+       ALL    = EVUNLOOP_ALL,
+}
+
+alias ev_tstamp tstamp;
+
+alias ev_statdata statdata;
+
+int version_major()
+{
+       return ev_version_major();
+}
+
+int version_minor()
+{
+       return ev_version_minor();
+}
+
+uint supported_backends()
+{
+       return ev_supported_backends();
+}
+
+uint recommended_backends()
+{
+       return ev_recommended_backends();
+}
+
+uint embeddable_backends()
+{
+       return ev_embeddable_backends();
+}
+
+tstamp time()
+{
+       return ev_time();
+}
+
+void sleep(tstamp delay)
+{
+       void ev_sleep(tstamp delay);
+}
+
+private extern(C) void* allocator_thunk(alias Fn)(void* ptr, int size)
+{
+       return Fn(ptr, size);
+}
+
+// Fn is void* function(void* ptr, int size)
+void set_allocator(alias Fn)()
+{
+       ev_set_allocator(&allocator_thunk!(Fn));
+}
+
+debug (ev_d_set_allocator)
+{
+       import std.stdio;
+       unittest
+       {
+               static void* alloc(void* ptr, int size)
+               {
+                       writefln("alloc(", ptr, ", ", size, ")");
+                       return null;
+               }
+               set_allocator!(alloc)();
+               ev.d.loop;
+       }
+}
+
+private extern(C) void syserr_thunk(alias Fn)(char* msg)
+{
+       string m = toString(msg);
+       Fn(m);
+}
+
+// Fn is void function(string msg)
+void set_syserr_cb(alias Fn)()
+{
+       ev_set_syserr_cb(&syserr_thunk!(Fn));
+}
+
+unittest
+{
+       static void syserr(string msg)
+       {
+       }
+       set_syserr_cb!(syserr)();
+}
+
+
+private alias extern (C) void function(int, void*) once_callback_t;
+
+interface ILoop
+{
+
+       alias void delegate(ILoop, int, int) OnceCallback;
+
+       ev_loop_t* ptr();
+
+       void fork();
+
+       tstamp now();
+
+       uint backend();
+
+       uint count();
+
+       void loop(int flags = 0);
+
+       void unloop(Unloop how = Unloop.ONE);
+
+       void ioCollectInterval(tstamp interval);
+
+       void timeoutCollectInterval(tstamp interval);
+
+       void addref();
+
+       void unref();
+
+       void once(int fd, int events, tstamp timeout,
+                       once_callback_t cb, void* arg = null);
+
+       void once(int fd, int events, tstamp timeout, OnceCallback cb);
+
+       void once(int fd, int events, OnceCallback cb);
+
+       void once(tstamp timeout, OnceCallback cb);
+
+       void feedFdEvent(int fd, int revents);
+
+       void feedSignalEvent(int signum);
+
+}
+
+private struct OnceData
+{
+       ILoop loop;
+       int fd;
+       ILoop.OnceCallback cb;
+}
+
+private extern(C) void once_thunk(int revents, void* arg)
+{
+       auto d = cast (OnceData*) arg;
+       d.cb(d.loop, d.fd, revents);
+}
+
+template MLoop()
+{
+
+       ev_loop_t* ptr()
+       {
+               return _ptr;
+       }
+
+       tstamp now()
+       {
+               return ev_now(_ptr);
+       }
+
+       uint backend()
+       {
+               return ev_backend(_ptr);
+       }
+
+       uint count()
+       {
+               return ev_loop_count(_ptr);
+       }
+
+       void loop(int flags = 0)
+       {
+               ev_loop(_ptr, flags);
+       }
+
+       void unloop(Unloop how = Unloop.ONE)
+       {
+               ev_unloop(_ptr, how);
+       }
+
+       void ioCollectInterval(tstamp interval)
+       {
+               ev_set_io_collect_interval(_ptr, interval);
+       }
+
+       void timeoutCollectInterval(tstamp interval)
+       {
+               ev_set_timeout_collect_interval(_ptr, interval);
+       }
+
+       void addref()
+       {
+               ev_ref(_ptr);
+       }
+
+       void unref()
+       {
+               ev_unref(_ptr);
+       }
+
+       void once(int fd, int events, tstamp timeout,
+                       once_callback_t cb, void* arg = null)
+       {
+               ev_once(_ptr, fd, events, timeout, cb, arg);
+       }
+
+       void once(int fd, int events, tstamp timeout, OnceCallback cb)
+       {
+               auto d = new OnceData;
+               d.cb = cb;
+               d.fd = fd;
+               d.loop = this;
+               once(fd, events, timeout, &once_thunk, d);
+       }
+
+       void once(int fd, int events, OnceCallback cb)
+       {
+               once(fd, events, -1.0, cb);
+       }
+
+       void once(tstamp timeout, OnceCallback cb)
+       {
+               once(-1, NONE, timeout, cb);
+       }
+
+       void feedFdEvent(int fd, int revents)
+       {
+               return ev_feed_fd_event(_ptr, fd, revents);
+       }
+
+       void feedSignalEvent(int signum)
+       {
+               return ev_feed_signal_event(_ptr, signum);
+       }
+
+       private ev_loop_t* _ptr;
+
+}
+
+class Loop: ILoop
+{
+
+       mixin MLoop;
+
+       this(uint flags = AUTO)
+       {
+               _ptr = ev_loop_new(flags);
+       }
+
+       ~this()
+       {
+               ev_loop_destroy(_ptr);
+       }
+
+       void fork()
+       {
+               ev_loop_fork(_ptr);
+       }
+
+       unittest
+       {
+               auto loop = new Loop;
+               assert (loop.count == 0);
+               loop.fork;
+       }
+
+}
+
+private class DefaultLoop: ILoop
+{
+
+       mixin MLoop;
+
+       this(uint flags = AUTO)
+       {
+               _ptr = ev_default_loop(flags);
+       }
+
+       ~this()
+       {
+               ev_default_destroy();
+       }
+
+       void fork()
+       {
+               ev_default_fork();
+       }
+
+       void destroy()
+       {
+               delete ev.d._loop;
+               ev.d._loop = null;
+       }
+
+       debug (ev_d_DefaultLoop) import std.stdio;
+       unittest
+       {
+               debug (ev_d_DefaultLoop) writefln("BEGIN UNITTEST");
+               auto loop = new DefaultLoop;
+               debug (ev_d_DefaultLoop) writefln("loop.count = ", loop.count);
+               loop.fork;
+               class C
+               {
+                       void ev(ILoop loop, int fd, int revents)
+                       {
+                               debug (ev_d_DefaultLoop)
+                               {
+                                       writefln("ev");
+                                       if (revents & READ) writefln("\tREAD");
+                                       if (revents & TIMEOUT) writefln("\tTIMEOUT");
+                               }
+                       }
+               }
+               C c = new C;
+               loop.once(0, READ, 2.0, &c.ev);
+               loop.loop(ONESHOT);
+               debug (ev_d_DefaultLoop) writefln("END UNITTEST");
+       }
+
+}
+
+private DefaultLoop _loop;
+
+DefaultLoop loop(uint flags = AUTO)
+{
+       if (!_loop) _loop = new DefaultLoop(flags);
+       return _loop;
+}
+
+interface IWatcher
+{
+
+       void start();
+
+       void stop();
+
+       bool pending();
+
+       bool active();
+
+       int priority();
+
+       void priority(int);
+
+       ILoop loop();
+
+       void loop(ILoop);
+
+       void feed(int revents);
+
+}
+
+enum: bool
+{
+       RO = false,
+       RW = true,
+}
+
+string property(string type, string proxy, string name, bool access = RW)()
+{
+       string s = "\n\t" ~ type ~ " " ~ name ~ "() {\n"
+               "\t\treturn cast (" ~ type ~ ") " ~ proxy ~ "." ~ name ~ ";\n"
+               "\t}\n";
+       if (access == RW)
+       {
+               s ~= "\n\tvoid " ~ name ~ "(" ~ type ~ " " ~ name ~ ") {\n"
+                       "\t\t" ~ proxy ~ "." ~ name ~ " =  cast (" ~ type ~ ") " ~ name ~ ";\n"
+                       "\t}\n";
+       }
+       return s;
+}
+
+string wproperty(string type, string name, bool access = RW)()
+{
+       debug (ev_d_property)
+               pragma(msg, "" ~ property!(type, "ptr", name, access));
+       return property!(type, "ptr", name, access);
+}
+
+string dproperty(string type, string name, bool access = RW)()
+{
+       debug (ev_d_property)
+               pragma(msg, "" ~ property!(type, "data", name, access));
+       return property!(type, "data", name, access);
+}
+
+template MWatcher(DWatcher, CWatcher, alias StartFunc, alias StopFunc,
+               bool DefineData = true)
+{
+
+       static if (DefineData) private struct WatcherData
+       {
+               Callback cb;
+               DWatcher watcher;
+       }
+
+       alias void delegate(DWatcher, int revents) Callback;
+
+       private extern(C) static void watcher_thunk(ev_loop_t* loop,
+                       CWatcher* watcher, int revents)
+       {
+               auto d = cast (WatcherData*) watcher.data;
+               d.cb(d.watcher, revents);
+       }
+
+       private void init(ILoop loop, Callback cb)
+       {
+               ev_init(ptr, &watcher_thunk);
+               auto d = new WatcherData;
+               d.watcher = this;
+               ptr.data = d;
+               this.cb = cb;
+               this.loop = loop;
+       }
+
+       ~this()
+       {
+               if (active) stop;
+       }
+
+       bool pending()
+       {
+               return ev_is_pending(ptr);
+       }
+
+       bool active()
+       {
+               return ev_is_active(ptr);
+       }
+
+       int priority()
+       {
+               return ev_priority(ptr);
+       }
+
+       void priority(int prio)
+       {
+               ev_set_priority(ptr, prio);
+       }
+
+       CWatcher* ptr()
+       {
+               return &_ptr;
+       }
+
+       ILoop loop()
+       {
+               return _loop;
+       }
+
+       void loop(ILoop loop)
+       {
+               bool a = active;
+               if (a) stop;
+               _loop = loop;
+               if (a) start;
+       }
+
+       private ILoop _loop;
+
+       private CWatcher _ptr;
+
+       void start()
+       {
+               StartFunc(loop.ptr, ptr);
+       }
+
+       void stop()
+       {
+               StopFunc(loop.ptr, ptr);
+       }
+
+       void feed(int revents)
+       {
+               ev_feed_event(loop.ptr, ptr, revents);
+       }
+
+       private mixin (wproperty!("WatcherData*", "data"));
+
+       mixin (dproperty!("Callback", "cb"));
+
+}
+
+class Io: IWatcher
+{
+
+       mixin MWatcher!(Io, ev_io, ev_io_start, ev_io_stop);
+
+       this(int fd, int events, Callback cb, ILoop loop = ev.d.loop)
+       {
+               init(loop, cb);
+               ev_io_set(ptr, fd, events);
+       }
+
+       mixin (wproperty!("int", "fd", RO));
+
+       mixin (wproperty!("int", "events", RO));
+
+       debug (ev_d_Io)
+       {
+               import std.stdio;
+       }
+       import std.c.unix.unix;
+       unittest
+       {
+               auto w = new Io(0, READ, (Io w, int revents)
+               {
+                       debug (ev_d_Io)
+                               writefln("io callback: revents=", revents);
+                       char[4096] buff;
+                       int r = read(w.fd, buff.ptr, buff.length);
+                       debug (ev_d_Io)
+                               writefln("\tread %d bytes: %s", r, buff);
+                       w.loop.unloop;
+               });
+               ev.d.loop.loop;
+       }
+
+}
+
+interface ITimer: IWatcher
+{
+
+       void again();
+
+}
+
+template MTimer(alias AgainFunc)
+{
+
+       void again()
+       {
+               AgainFunc(loop.ptr, ptr);
+       }
+
+}
+
+class Timer: ITimer
+{
+
+       mixin MWatcher!(Timer, ev_timer, ev_timer_start, ev_timer_stop);
+
+       mixin MTimer!(ev_timer_again);
+
+       this(tstamp after, tstamp repeat, Callback cb, ILoop loop = ev.d.loop)
+       {
+               init(loop, cb);
+               ev_timer_set(ptr, after, repeat);
+       }
+
+       this(tstamp after, Callback cb, ILoop loop = ev.d.loop)
+       {
+               this(after, 0.0, cb, loop);
+       }
+
+       mixin (wproperty!("tstamp", "repeat"));
+
+       debug (ev_d_Timer)
+       {
+               import std.stdio;
+               import std.c.unix.unix;
+       }
+       unittest
+       {
+               auto w = new Timer(1.0, (Timer w, int revents)
+               {
+                       debug (ev_d_Timer)
+                               writefln("timeout callback: revents=", revents);
+                       w.loop.unloop;
+               });
+               ev.d.loop.loop;
+       }
+
+}
+
+class Periodic: ITimer
+{
+
+       mixin MWatcher!(Periodic, ev_periodic, ev_periodic_start,
+                       ev_periodic_stop, false);
+
+       mixin MTimer!(ev_periodic_again);
+
+       alias tstamp delegate(Periodic, tstamp) RescheduleCallback;
+
+       private struct WatcherData
+       {
+               void delegate(Periodic, int) cb;
+               RescheduleCallback reschedulecb;
+               Periodic watcher;
+       }
+
+       private extern(C) static tstamp reschedule_thunk(ev_periodic* watcher,
+                       tstamp now)
+       {
+               auto d = cast (WatcherData*) watcher.data;
+               return d.reschedulecb(d.watcher, now);
+       }
+
+       this(tstamp at, tstamp interval, RescheduleCallback reschedulecb,
+                       Callback cb, ILoop loop = ev.d.loop)
+       {
+               init(loop, cb);
+               ev_periodic_set(ptr, at, interval, null);
+               this.reschedulecb = reschedulecb;
+       }
+
+       void reschedulecb(RescheduleCallback reschedulecb)
+       {
+               if (reschedulecb is null) {
+                       ptr.reschedule_cb = null;
+                       data.reschedulecb = null;
+               }
+               else {
+                       ptr.reschedule_cb = &reschedule_thunk;
+                       data.reschedulecb = reschedulecb;
+               }
+       }
+
+       mixin (dproperty!("RescheduleCallback", "reschedulecb", RO));
+
+       mixin (wproperty!("tstamp", "offset"));
+
+       mixin (wproperty!("tstamp", "interval"));
+
+       mixin (wproperty!("tstamp", "at", RO));
+
+}
+
+class At: Periodic
+{
+
+       this(tstamp time, Callback cb, ILoop loop = ev.d.loop)
+       {
+               super(time, 0.0, null, cb, loop);
+       }
+
+}
+
+class Cron: Periodic
+{
+
+       this(tstamp offset, tstamp interval, Callback cb,
+                       ILoop loop = ev.d.loop)
+       in {
+               assert (interval > 0);
+       }
+       body {
+               super(offset, interval, null, cb, loop);
+       }
+
+}
+
+class ManualCron: Periodic
+{
+       this(RescheduleCallback reschedulecb, Callback cb,
+                       ILoop loop = ev.d.loop)
+       {
+               super(0.0, 0.0, reschedulecb, cb, loop);
+       }
+}
+
+class Signal: IWatcher
+{
+
+       mixin MWatcher!(Signal, ev_signal, ev_signal_start, ev_signal_stop);
+
+       this(int signum, Callback cb, ILoop loop = ev.d.loop)
+       {
+               init(loop, cb);
+               ev_signal_set(ptr, signum);
+       }
+
+       mixin (wproperty!("int", "signum", RO));
+
+}
+
+class Child: IWatcher
+{
+
+       mixin MWatcher!(Child, ev_child, ev_child_start, ev_child_stop);
+
+       this(int pid, bool trace, Callback cb, ILoop loop = ev.d.loop)
+       {
+               init(loop, cb);
+               ev_child_set(ptr, pid, trace);
+       }
+
+       this(int pid, Callback cb, ILoop loop = ev.d.loop)
+       {
+               this(pid, 0, cb, loop);
+       }
+
+       this(Callback cb, ILoop loop = ev.d.loop)
+       {
+               this(0, 0, cb, loop);
+       }
+
+       mixin (wproperty!("int", "pid", RO));
+
+       mixin (wproperty!("int", "rpid"));
+
+       mixin (wproperty!("int", "rstatus"));
+
+}
+
+class Stat: IWatcher
+{
+
+       import std.string: toString, toStringz;
+
+       mixin MWatcher!(Stat, ev_stat, ev_stat_start, ev_stat_stop);
+
+       this(string path, tstamp interval, Callback cb, ILoop loop = ev.d.loop)
+       {
+               init(loop, cb);
+               ev_stat_set(ptr, toStringz(path.dup), interval);
+       }
+
+       this(string path, Callback cb, ILoop loop = ev.d.loop)
+       {
+               this(path, 0.0, cb, loop);
+       }
+
+       void stat()
+       {
+               ev_stat_stat(loop.ptr, ptr);
+       }
+
+       mixin (wproperty!("statdata", "attr", RO));
+
+       mixin (wproperty!("statdata", "prev", RO));
+
+       mixin (wproperty!("tstamp", "interval", RO));
+
+       /+
+       void path(string path)
+       {
+               ptr.path = toStringz(path.dup);
+       }
+       +/
+
+       string path()
+       {
+               return toString(ptr.path).dup;
+       }
+
+}
+
+class Idle: IWatcher
+{
+
+       mixin MWatcher!(Idle, ev_idle, ev_idle_start, ev_idle_stop);
+
+       this(Callback cb, ILoop loop = ev.d.loop)
+       {
+               init(loop, cb);
+               ev_idle_set(ptr);
+       }
+
+}
+
+class Prepare: IWatcher
+{
+
+       mixin MWatcher!(Prepare, ev_prepare, ev_prepare_start, ev_prepare_stop);
+
+       this(Callback cb, ILoop loop = ev.d.loop)
+       {
+               init(loop, cb);
+               ev_prepare_set(ptr);
+       }
+
+}
+
+class Check: IWatcher
+{
+
+       mixin MWatcher!(Check, ev_check, ev_check_start, ev_check_stop);
+
+       this(Callback cb, ILoop loop = ev.d.loop)
+       {
+               init(loop, cb);
+               ev_check_set(ptr);
+       }
+
+}
+
+class Embed: IWatcher
+{
+
+       mixin MWatcher!(Embed, ev_embed, ev_embed_start, ev_embed_stop, false);
+
+       private struct WatcherData
+       {
+               void delegate(Embed, int) cb;
+               ILoop other;
+               Embed watcher;
+       }
+
+       this(ILoop embeddedloop, Callback cb, ILoop loop = ev.d.loop)
+       {
+               init(loop, cb);
+               ev_embed_set(ptr, embeddedloop.ptr);
+               this.other = embeddedloop;
+       }
+
+       void sweep()
+       {
+               ev_embed_sweep(loop.ptr, ptr);
+       }
+
+       void other(ILoop other)
+       {
+               data.other = other;
+               ev_embed_set(ptr, other.ptr);
+       }
+
+       mixin (dproperty!("ILoop", "other", RO));
+
+}
+
+class Fork: IWatcher
+{
+
+       mixin MWatcher!(Fork, ev_fork, ev_fork_start, ev_fork_stop);
+
+       this(Callback cb, ILoop loop = ev.d.loop)
+       {
+               init(loop, cb);
+               ev_fork_set(ptr);
+       }
+
+}
+