From: Leandro Lucarella Date: Wed, 13 Feb 2008 21:59:43 +0000 (-0200) Subject: Add initial D-ish libev API. X-Git-Tag: 0.1~2 X-Git-Url: https://git.llucax.com/software/ev.d.git/commitdiff_plain/5dadbf640f14869daa2d1d21511ae7754e008225 Add initial D-ish libev API. 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 --- diff --git a/ev/d.d b/ev/d.d new file mode 100644 index 0000000..8c47c10 --- /dev/null +++ b/ev/d.d @@ -0,0 +1,893 @@ +/+ + + D Programming Language "bindings" to libev + + + + + + Written by Leandro Lucarella (2008). + + + + Placed under BOLA license 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); + } + +} +