]> git.llucax.com Git - software/ev.d.git/blob - ev/d.d
Don't compile unittests by default.
[software/ev.d.git] / ev / d.d
1 /+
2  + D Programming Language "bindings" to libev
3  + <http://software.schmorp.de/pkg/libev.html>
4  +
5  + Written by Leandro Lucarella (2008).
6  +
7  + Placed under BOLA license <http://auriga.wearlab.de/~alb/bola/> which is
8  + basically public domain.
9  +
10  +/
11
12 module ev.d;
13
14 import ev.c;
15 import std.string;
16
17 enum: uint
18 {
19         UNDEF    = EV_UNDEF,
20         NONE     = EV_NONE,
21         READ     = EV_READ,
22         WRITE    = EV_WRITE,
23         IOFDSET  = EV_IOFDSET,
24         TIMEOUT  = EV_TIMEOUT,
25         PERIODIC = EV_PERIODIC,
26         SIGNAL   = EV_SIGNAL,
27         CHILD    = EV_CHILD,
28         STAT     = EV_STAT,
29         IDLE     = EV_IDLE,
30         PREPARE  = EV_PREPARE,
31         CHECK    = EV_CHECK,
32         EMBED    = EV_EMBED,
33         FORK     = EV_FORK,
34         ERROR    = EV_ERROR,
35 }
36
37 enum: uint
38 {
39         AUTO       = EVFLAG_AUTO,
40         NOENV      = EVFLAG_NOENV,
41         FORKCHECK  = EVFLAG_FORKCHECK,
42         SELECT     = EVBACKEND_SELECT,
43         POLL       = EVBACKEND_POLL,
44         EPOLL      = EVBACKEND_EPOLL,
45         KQUEUE     = EVBACKEND_KQUEUE,
46         DEVPOLL    = EVBACKEND_DEVPOLL,
47         PORT       = EVBACKEND_PORT,
48 }
49
50 enum
51 {
52         NONBLOCK = EVLOOP_NONBLOCK,
53         ONESHOT  = EVLOOP_ONESHOT,
54 }
55
56 enum Unloop
57 {
58         CANCEL = EVUNLOOP_CANCEL,
59         ONE    = EVUNLOOP_ONE,
60         ALL    = EVUNLOOP_ALL,
61 }
62
63 alias ev_tstamp tstamp;
64
65 alias ev_statdata statdata;
66
67 int version_major()
68 {
69         return ev_version_major();
70 }
71
72 int version_minor()
73 {
74         return ev_version_minor();
75 }
76
77 uint supported_backends()
78 {
79         return ev_supported_backends();
80 }
81
82 uint recommended_backends()
83 {
84         return ev_recommended_backends();
85 }
86
87 uint embeddable_backends()
88 {
89         return ev_embeddable_backends();
90 }
91
92 tstamp time()
93 {
94         return ev_time();
95 }
96
97 void sleep(tstamp delay)
98 {
99         void ev_sleep(tstamp delay);
100 }
101
102 private extern(C) void* allocator_thunk(alias Fn)(void* ptr, int size)
103 {
104         return Fn(ptr, size);
105 }
106
107 // Fn is void* function(void* ptr, int size)
108 void set_allocator(alias Fn)()
109 {
110         ev_set_allocator(&allocator_thunk!(Fn));
111 }
112
113 debug (ev_d_set_allocator)
114 {
115         import std.stdio;
116         unittest
117         {
118                 static void* alloc(void* ptr, int size)
119                 {
120                         writefln("alloc(", ptr, ", ", size, ")");
121                         return null;
122                 }
123                 set_allocator!(alloc)();
124                 ev.d.loop;
125         }
126 }
127
128 private extern(C) void syserr_thunk(alias Fn)(char* msg)
129 {
130         string m = toString(msg);
131         Fn(m);
132 }
133
134 // Fn is void function(string msg)
135 void set_syserr_cb(alias Fn)()
136 {
137         ev_set_syserr_cb(&syserr_thunk!(Fn));
138 }
139
140 unittest
141 {
142         static void syserr(string msg)
143         {
144         }
145         set_syserr_cb!(syserr)();
146 }
147
148
149 private alias extern (C) void function(int, void*) once_callback_t;
150
151 interface ILoop
152 {
153
154         alias void delegate(ILoop, int, int) OnceCallback;
155
156         ev_loop_t* ptr();
157
158         void fork();
159
160         tstamp now();
161
162         uint backend();
163
164         uint count();
165
166         void loop(int flags = 0);
167
168         void unloop(Unloop how = Unloop.ONE);
169
170         void ioCollectInterval(tstamp interval);
171
172         void timeoutCollectInterval(tstamp interval);
173
174         void addref();
175
176         void unref();
177
178         void once(int fd, int events, tstamp timeout,
179                         once_callback_t cb, void* arg = null);
180
181         void once(int fd, int events, tstamp timeout, OnceCallback cb);
182
183         void once(int fd, int events, OnceCallback cb);
184
185         void once(tstamp timeout, OnceCallback cb);
186
187         void feedFdEvent(int fd, int revents);
188
189         void feedSignalEvent(int signum);
190
191 }
192
193 private struct OnceData
194 {
195         ILoop loop;
196         int fd;
197         ILoop.OnceCallback cb;
198 }
199
200 private extern(C) void once_thunk(int revents, void* arg)
201 {
202         auto d = cast (OnceData*) arg;
203         d.cb(d.loop, d.fd, revents);
204 }
205
206 template MLoop()
207 {
208
209         ev_loop_t* ptr()
210         {
211                 return _ptr;
212         }
213
214         tstamp now()
215         {
216                 return ev_now(ptr);
217         }
218
219         uint backend()
220         {
221                 return ev_backend(ptr);
222         }
223
224         uint count()
225         {
226                 return ev_loop_count(ptr);
227         }
228
229         void loop(int flags = 0)
230         {
231                 ev_loop(ptr, flags);
232         }
233
234         void unloop(Unloop how = Unloop.ONE)
235         {
236                 ev_unloop(ptr, how);
237         }
238
239         void ioCollectInterval(tstamp interval)
240         {
241                 ev_set_io_collect_interval(ptr, interval);
242         }
243
244         void timeoutCollectInterval(tstamp interval)
245         {
246                 ev_set_timeout_collect_interval(ptr, interval);
247         }
248
249         void addref()
250         {
251                 ev_ref(ptr);
252         }
253
254         void unref()
255         {
256                 ev_unref(ptr);
257         }
258
259         void once(int fd, int events, tstamp timeout,
260                         once_callback_t cb, void* arg = null)
261         {
262                 ev_once(ptr, fd, events, timeout, cb, arg);
263         }
264
265         void once(int fd, int events, tstamp timeout, OnceCallback cb)
266         {
267                 auto d = new OnceData;
268                 d.cb = cb;
269                 d.fd = fd;
270                 d.loop = this;
271                 once(fd, events, timeout, &once_thunk, d);
272         }
273
274         void once(int fd, int events, OnceCallback cb)
275         {
276                 once(fd, events, -1.0, cb);
277         }
278
279         void once(tstamp timeout, OnceCallback cb)
280         {
281                 once(-1, NONE, timeout, cb);
282         }
283
284         void feedFdEvent(int fd, int revents)
285         {
286                 return ev_feed_fd_event(ptr, fd, revents);
287         }
288
289         void feedSignalEvent(int signum)
290         {
291                 return ev_feed_signal_event(ptr, signum);
292         }
293
294         private ev_loop_t* _ptr;
295
296 }
297
298 class Loop: ILoop
299 {
300
301         mixin MLoop;
302
303         this(uint flags = AUTO)
304         {
305                 _ptr = ev_loop_new(flags);
306         }
307
308         ~this()
309         {
310                 ev_loop_destroy(ptr);
311         }
312
313         void fork()
314         {
315                 ev_loop_fork(ptr);
316         }
317
318         unittest
319         {
320                 auto loop = new Loop;
321                 assert (loop.count == 0);
322                 loop.fork;
323         }
324
325 }
326
327 private class DefaultLoop: ILoop
328 {
329
330         mixin MLoop;
331
332         this(uint flags = AUTO)
333         {
334                 _ptr = ev_default_loop(flags);
335         }
336
337         ~this()
338         {
339                 ev_default_destroy();
340         }
341
342         void fork()
343         {
344                 ev_default_fork();
345         }
346
347         void destroy()
348         {
349                 delete ev.d._loop;
350                 ev.d._loop = null;
351         }
352
353         debug (ev_d_DefaultLoop) import std.stdio;
354         unittest
355         {
356                 debug (ev_d_DefaultLoop) writefln("BEGIN UNITTEST");
357                 auto loop = new DefaultLoop;
358                 debug (ev_d_DefaultLoop) writefln("loop.count = ", loop.count);
359                 loop.fork;
360                 class C
361                 {
362                         void ev(ILoop loop, int fd, int revents)
363                         {
364                                 debug (ev_d_DefaultLoop)
365                                 {
366                                         writefln("ev");
367                                         if (revents & READ) writefln("\tREAD");
368                                         if (revents & TIMEOUT) writefln("\tTIMEOUT");
369                                 }
370                         }
371                 }
372                 C c = new C;
373                 loop.once(0, READ, 2.0, &c.ev);
374                 loop.loop(ONESHOT);
375                 debug (ev_d_DefaultLoop) writefln("END UNITTEST");
376         }
377
378 }
379
380 private DefaultLoop _loop;
381
382 DefaultLoop loop(uint flags = AUTO)
383 {
384         if (!_loop) _loop = new DefaultLoop(flags);
385         return _loop;
386 }
387
388 interface IWatcher
389 {
390
391         void start();
392
393         void stop();
394
395         bool pending();
396
397         bool active();
398
399         int priority();
400
401         void priority(int);
402
403         ILoop loop();
404
405         void loop(ILoop);
406
407         void feed(int revents);
408
409 }
410
411 enum: bool
412 {
413         RO = false,
414         RW = true,
415 }
416
417 string property(string type, string proxy, string name, bool access = RW)()
418 {
419         string s = "\n\t" ~ type ~ " " ~ name ~ "() {\n"
420                 "\t\treturn cast (" ~ type ~ ") " ~ proxy ~ "." ~ name ~ ";\n"
421                 "\t}\n";
422         if (access == RW)
423         {
424                 s ~= "\n\tvoid " ~ name ~ "(" ~ type ~ " " ~ name ~ ") {\n"
425                         "\t\t" ~ proxy ~ "." ~ name ~ " =  cast (" ~ type ~ ") " ~ name ~ ";\n"
426                         "\t}\n";
427         }
428         return s;
429 }
430
431 string wproperty(string type, string name, bool access = RW)()
432 {
433         debug (ev_d_property)
434                 pragma(msg, "" ~ property!(type, "ptr", name, access));
435         return property!(type, "ptr", name, access);
436 }
437
438 string dproperty(string type, string name, bool access = RW)()
439 {
440         debug (ev_d_property)
441                 pragma(msg, "" ~ property!(type, "data", name, access));
442         return property!(type, "data", name, access);
443 }
444
445 template MWatcher(DWatcher, CWatcher, alias StartFunc, alias StopFunc,
446                 bool DefineData = true)
447 {
448
449         static if (DefineData) private struct WatcherData
450         {
451                 Callback cb;
452                 DWatcher watcher;
453         }
454
455         alias void delegate(DWatcher, int revents) Callback;
456
457         private extern(C) static void watcher_thunk(ev_loop_t* loop,
458                         CWatcher* watcher, int revents)
459         {
460                 auto d = cast (WatcherData*) watcher.data;
461                 d.cb(d.watcher, revents);
462         }
463
464         private void init(ILoop loop, Callback cb)
465         {
466                 ev_init(ptr, &watcher_thunk);
467                 auto d = new WatcherData;
468                 d.watcher = this;
469                 ptr.data = d;
470                 this.cb = cb;
471                 this.loop = loop;
472         }
473
474         ~this()
475         {
476                 if (active) stop;
477         }
478
479         bool pending()
480         {
481                 return ev_is_pending(ptr);
482         }
483
484         bool active()
485         {
486                 return ev_is_active(ptr);
487         }
488
489         int priority()
490         {
491                 return ev_priority(ptr);
492         }
493
494         void priority(int prio)
495         {
496                 ev_set_priority(ptr, prio);
497         }
498
499         CWatcher* ptr()
500         {
501                 return &_ptr;
502         }
503
504         ILoop loop()
505         {
506                 return _loop;
507         }
508
509         void loop(ILoop loop)
510         {
511                 bool a = active;
512                 if (a) stop;
513                 _loop = loop;
514                 if (a) start;
515         }
516
517         private ILoop _loop;
518
519         private CWatcher _ptr;
520
521         void start()
522         {
523                 StartFunc(loop.ptr, ptr);
524         }
525
526         void stop()
527         {
528                 StopFunc(loop.ptr, ptr);
529         }
530
531         void feed(int revents)
532         {
533                 ev_feed_event(loop.ptr, ptr, revents);
534         }
535
536         private mixin (wproperty!("WatcherData*", "data"));
537
538         mixin (dproperty!("Callback", "cb"));
539
540 }
541
542 class Io: IWatcher
543 {
544
545         mixin MWatcher!(Io, ev_io, ev_io_start, ev_io_stop);
546
547         this(int fd, int events, Callback cb, ILoop loop = ev.d.loop)
548         {
549                 init(loop, cb);
550                 ev_io_set(ptr, fd, events);
551         }
552
553         mixin (wproperty!("int", "fd", RO));
554
555         mixin (wproperty!("int", "events", RO));
556
557         debug (ev_d_Io)
558         {
559                 import std.stdio;
560         }
561         import std.c.unix.unix;
562         unittest
563         {
564                 auto w = new Io(0, READ, (Io w, int revents)
565                 {
566                         debug (ev_d_Io)
567                                 writefln("io callback: revents=", revents);
568                         char[4096] buff;
569                         int r = read(w.fd, buff.ptr, buff.length);
570                         debug (ev_d_Io)
571                                 writefln("\tread %d bytes: %s", r, buff);
572                         w.loop.unloop;
573                 });
574                 ev.d.loop.loop;
575         }
576
577 }
578
579 interface ITimer: IWatcher
580 {
581
582         void again();
583
584 }
585
586 template MTimer(alias AgainFunc)
587 {
588
589         void again()
590         {
591                 AgainFunc(loop.ptr, ptr);
592         }
593
594 }
595
596 class Timer: ITimer
597 {
598
599         mixin MWatcher!(Timer, ev_timer, ev_timer_start, ev_timer_stop);
600
601         mixin MTimer!(ev_timer_again);
602
603         this(tstamp after, tstamp repeat, Callback cb, ILoop loop = ev.d.loop)
604         {
605                 init(loop, cb);
606                 ev_timer_set(ptr, after, repeat);
607         }
608
609         this(tstamp after, Callback cb, ILoop loop = ev.d.loop)
610         {
611                 this(after, 0.0, cb, loop);
612         }
613
614         mixin (wproperty!("tstamp", "repeat"));
615
616         debug (ev_d_Timer)
617         {
618                 import std.stdio;
619                 import std.c.unix.unix;
620         }
621         unittest
622         {
623                 auto w = new Timer(1.0, (Timer w, int revents)
624                 {
625                         debug (ev_d_Timer)
626                                 writefln("timeout callback: revents=", revents);
627                         w.loop.unloop;
628                 });
629                 ev.d.loop.loop;
630         }
631
632 }
633
634 class Periodic: ITimer
635 {
636
637         mixin MWatcher!(Periodic, ev_periodic, ev_periodic_start,
638                         ev_periodic_stop, false);
639
640         mixin MTimer!(ev_periodic_again);
641
642         alias tstamp delegate(Periodic, tstamp) RescheduleCallback;
643
644         private struct WatcherData
645         {
646                 void delegate(Periodic, int) cb;
647                 RescheduleCallback reschedulecb;
648                 Periodic watcher;
649         }
650
651         private extern(C) static tstamp reschedule_thunk(ev_periodic* watcher,
652                         tstamp now)
653         {
654                 auto d = cast (WatcherData*) watcher.data;
655                 return d.reschedulecb(d.watcher, now);
656         }
657
658         this(tstamp at, tstamp interval, RescheduleCallback reschedulecb,
659                         Callback cb, ILoop loop = ev.d.loop)
660         {
661                 init(loop, cb);
662                 ev_periodic_set(ptr, at, interval, null);
663                 this.reschedulecb = reschedulecb;
664         }
665
666         void reschedulecb(RescheduleCallback reschedulecb)
667         {
668                 if (reschedulecb is null) {
669                         ptr.reschedule_cb = null;
670                         data.reschedulecb = null;
671                 }
672                 else {
673                         ptr.reschedule_cb = &reschedule_thunk;
674                         data.reschedulecb = reschedulecb;
675                 }
676         }
677
678         mixin (dproperty!("RescheduleCallback", "reschedulecb", RO));
679
680         mixin (wproperty!("tstamp", "offset"));
681
682         mixin (wproperty!("tstamp", "interval"));
683
684         mixin (wproperty!("tstamp", "at", RO));
685
686 }
687
688 class At: Periodic
689 {
690
691         this(tstamp time, Callback cb, ILoop loop = ev.d.loop)
692         {
693                 super(time, 0.0, null, cb, loop);
694         }
695
696 }
697
698 class Cron: Periodic
699 {
700
701         this(tstamp offset, tstamp interval, Callback cb,
702                         ILoop loop = ev.d.loop)
703         in {
704                 assert (interval > 0);
705         }
706         body {
707                 super(offset, interval, null, cb, loop);
708         }
709
710 }
711
712 class ManualCron: Periodic
713 {
714         this(RescheduleCallback reschedulecb, Callback cb,
715                         ILoop loop = ev.d.loop)
716         {
717                 super(0.0, 0.0, reschedulecb, cb, loop);
718         }
719 }
720
721 class Signal: IWatcher
722 {
723
724         mixin MWatcher!(Signal, ev_signal, ev_signal_start, ev_signal_stop);
725
726         this(int signum, Callback cb, ILoop loop = ev.d.loop)
727         {
728                 init(loop, cb);
729                 ev_signal_set(ptr, signum);
730         }
731
732         mixin (wproperty!("int", "signum", RO));
733
734 }
735
736 class Child: IWatcher
737 {
738
739         mixin MWatcher!(Child, ev_child, ev_child_start, ev_child_stop);
740
741         this(int pid, bool trace, Callback cb, ILoop loop = ev.d.loop)
742         {
743                 init(loop, cb);
744                 ev_child_set(ptr, pid, trace);
745         }
746
747         this(int pid, Callback cb, ILoop loop = ev.d.loop)
748         {
749                 this(pid, 0, cb, loop);
750         }
751
752         this(Callback cb, ILoop loop = ev.d.loop)
753         {
754                 this(0, 0, cb, loop);
755         }
756
757         mixin (wproperty!("int", "pid", RO));
758
759         mixin (wproperty!("int", "rpid"));
760
761         mixin (wproperty!("int", "rstatus"));
762
763 }
764
765 class Stat: IWatcher
766 {
767
768         import std.string: toString, toStringz;
769
770         mixin MWatcher!(Stat, ev_stat, ev_stat_start, ev_stat_stop);
771
772         this(string path, tstamp interval, Callback cb, ILoop loop = ev.d.loop)
773         {
774                 init(loop, cb);
775                 ev_stat_set(ptr, toStringz(path.dup), interval);
776         }
777
778         this(string path, Callback cb, ILoop loop = ev.d.loop)
779         {
780                 this(path, 0.0, cb, loop);
781         }
782
783         void stat()
784         {
785                 ev_stat_stat(loop.ptr, ptr);
786         }
787
788         mixin (wproperty!("statdata", "attr", RO));
789
790         mixin (wproperty!("statdata", "prev", RO));
791
792         mixin (wproperty!("tstamp", "interval", RO));
793
794         /+
795         void path(string path)
796         {
797                 ptr.path = toStringz(path.dup);
798         }
799         +/
800
801         string path()
802         {
803                 return toString(ptr.path).dup;
804         }
805
806 }
807
808 class Idle: IWatcher
809 {
810
811         mixin MWatcher!(Idle, ev_idle, ev_idle_start, ev_idle_stop);
812
813         this(Callback cb, ILoop loop = ev.d.loop)
814         {
815                 init(loop, cb);
816                 ev_idle_set(ptr);
817         }
818
819 }
820
821 class Prepare: IWatcher
822 {
823
824         mixin MWatcher!(Prepare, ev_prepare, ev_prepare_start, ev_prepare_stop);
825
826         this(Callback cb, ILoop loop = ev.d.loop)
827         {
828                 init(loop, cb);
829                 ev_prepare_set(ptr);
830         }
831
832 }
833
834 class Check: IWatcher
835 {
836
837         mixin MWatcher!(Check, ev_check, ev_check_start, ev_check_stop);
838
839         this(Callback cb, ILoop loop = ev.d.loop)
840         {
841                 init(loop, cb);
842                 ev_check_set(ptr);
843         }
844
845 }
846
847 class Embed: IWatcher
848 {
849
850         mixin MWatcher!(Embed, ev_embed, ev_embed_start, ev_embed_stop, false);
851
852         private struct WatcherData
853         {
854                 void delegate(Embed, int) cb;
855                 ILoop other;
856                 Embed watcher;
857         }
858
859         this(ILoop embeddedloop, Callback cb, ILoop loop = ev.d.loop)
860         {
861                 init(loop, cb);
862                 ev_embed_set(ptr, embeddedloop.ptr);
863                 this.other = embeddedloop;
864         }
865
866         void sweep()
867         {
868                 ev_embed_sweep(loop.ptr, ptr);
869         }
870
871         void other(ILoop other)
872         {
873                 data.other = other;
874                 ev_embed_set(ptr, other.ptr);
875         }
876
877         mixin (dproperty!("ILoop", "other", RO));
878
879 }
880
881 class Fork: IWatcher
882 {
883
884         mixin MWatcher!(Fork, ev_fork, ev_fork_start, ev_fork_stop);
885
886         this(Callback cb, ILoop loop = ev.d.loop)
887         {
888                 init(loop, cb);
889                 ev_fork_set(ptr);
890         }
891
892 }
893