1 module hio.events;
2 
3 import std.datetime;
4 import std.exception;
5 import std.container;
6 import std.experimental.logger;
7 import std.typecons;
8 import core.sync.mutex;
9 import std.format;
10 
11 import hio.common;
12 
13 import nbuff;
14 
15 enum AppEvent : int {
16     NONE =               0x0000,
17     IN   =               0x0001,
18     OUT  =               0x0002,
19     ERR  =               0x0004,
20     CONN =               0x0008,
21     HUP  =               0x0010,
22     TMO  =               0x0020,
23     USER =               0x0040,
24     IMMED=               0x0080,
25     SHUTDOWN =           0x0100,
26     EXT_EPOLLEXCLUSIVE = 0x1000, // linux/epoll specific 
27     ALL  =               0x0fff,
28 }
29 private immutable string[int] _names;
30 
31 shared static this() {
32     _names = [
33         0:"NONE",
34         1:"IN",
35         2:"OUT",
36         4:"ERR",
37         8:"CONN",
38        16:"HUP",
39        32:"TMO",
40        64:"USER",
41        0x80:"IMMED",
42        0x100:"SHUTDOWN",
43     ];
44 }
45 
46 ///
47 alias HandlerDelegate = void delegate(AppEvent) @safe;
48 alias SigHandlerDelegate = void delegate(int) @safe;
49 alias FileHandlerFunction = void function(int, AppEvent) @safe;
50 //alias NotificationHandler = void delegate(Notification) @safe;
51 alias FileHandlerDelegate = void delegate(int, AppEvent) @safe;
52 alias IOCallback = void delegate(ref IOResult) @safe;
53 
54 string appeventToString(AppEvent ev) @safe pure {
55     import std.format;
56     import std.range;
57 
58     string[] a;
59     with(AppEvent) {
60         foreach(e; [IN,OUT,ERR,CONN,HUP,TMO,SHUTDOWN]) {
61             if ( ev & e ) {
62                 a ~= _names[e];
63             }
64         }
65     }
66     return a.join("|");
67 }
68 
69 class LoopShutdownException: Exception
70 {
71     this(string msg, string file = __FILE__, size_t line = __LINE__) @safe {
72         super(msg, file, line);
73     }
74 }
75 class NotFoundException : Exception {
76     this(string msg, string file = __FILE__, size_t line = __LINE__) @safe {
77         super(msg, file, line);
78     }
79 }
80 
81 class NotImplementedException : Exception {
82     this(string msg, string file = __FILE__, size_t line = __LINE__) @safe {
83         super(msg, file, line);
84     }
85 }
86 
87 //final class FileDescriptor {
88 //    package {
89 //        immutable int   _fileno;
90 //        HandlerDelegate _handler;
91 //        AppEvent        _polling;
92 //    }
93 //    this(int fileno) nothrow @safe {
94 //        _fileno = fileno;
95 //    }
96 //    override string toString() const @safe {
97 //        import std.format: format;
98 //        return appeventToString(_polling);
99 //        //return "FileDescriptor: filehandle: %d, events: %s".format(_fileno, appeventToString(_polling));
100 //    }
101 //}
102 
103 //class CanPoll {
104 //    union Id {
105 //        int     fd = -1;
106 //    }
107 //    Id  id;
108 //}
109 
110 abstract class EventHandler {
111     abstract void eventHandler(AppEvent) @safe;
112     string        describe();
113 }
114 
115 abstract class FileEventHandler {
116     abstract void eventHandler(int, AppEvent) @safe;
117     abstract string describe();
118 }
119 
120 final class Timer {
121     private static ulong timer_id = 1;
122     package {
123         SysTime                   _expires;
124         bool                      _armed;
125         immutable ulong           _id;
126         immutable HandlerDelegate _handler;
127         immutable string          _file;
128         immutable int             _line;
129         version(unittest)
130         {
131             Duration              _delay;
132         }
133     }
134     int opCmp(in Timer other) const nothrow pure @safe {
135         int timeCmp = _expires.opCmp(other._expires);
136         if ( timeCmp != 0 ) {
137             return timeCmp;
138         }
139         return _id < other._id ? -1 : 1;
140     }
141 
142     bool eq(const Timer b) const pure nothrow @safe {
143         return this._id == b._id && this._expires == b._expires && this._handler == b._handler;
144     }
145     auto id() pure @nogc nothrow
146     {
147         return _id;
148     }
149     this(Duration d, HandlerDelegate h, string f = __FILE__, int l =  __LINE__) @safe {
150         if ( d == Duration.max ) {
151             _expires = SysTime.max;
152         } else {
153             _expires = Clock.currTime + d;
154         }
155         _handler = h;
156         _id = timer_id;
157         _file = f;
158         _line = l;
159         timer_id++;
160         version(unittest)
161         {
162             _delay = d;
163         }
164     }
165     this(SysTime e, HandlerDelegate h, string f = __FILE__, int l =  __LINE__) @safe {
166         enforce(e != SysTime.init, "Unintialized expires for new timer");
167         enforce(h != HandlerDelegate.init, "Unitialized handler for new Timer");
168         _expires = e;
169         _handler = h;
170         _file = f;
171         _line = l;
172         _id = timer_id++;
173     }
174     auto rearm(Duration d)
175     {
176         assert(!_armed);
177         if ( d == Duration.max ) {
178             _expires = SysTime.max;
179         } else {
180             _expires = Clock.currTime + d;
181         }
182         version(unittest)
183         {
184             _delay = d;
185         }
186     }
187     alias describe = toString;
188     override string toString() const @trusted {
189         import std.format: format;
190         version(unittest)
191         {
192             return "timer: expires: %s(%s), id: %d, addr %X (%s:%d)".format(_expires, _delay, _id, cast(void*)this, _file, _line);
193         }
194         else
195         {
196             return "timer: expires: %s, id: %d, addr %X (%s:%d)".format(_expires, _id, cast(void*)this, _file, _line);
197         }
198     }
199 }
200 
201 final class Signal {
202     private static ulong signal_id = 1;
203     package {
204         immutable int   _signum;
205         immutable ulong _id;
206         immutable SigHandlerDelegate _handler;
207     }
208 
209     this(int signum, SigHandlerDelegate h) {
210         _signum = signum;
211         _handler = h;
212         _id = signal_id++;
213     }
214     int opCmp(in Signal other) const nothrow pure @safe {
215         if ( _signum == other._signum ) {
216             return _id < other._id ? -1 : 1;
217         }
218         return _signum < other._signum ? -1 : 1;
219     }
220     override string toString() const @trusted {
221         import std.format: format;
222         return "signal: signum: %d, id: %d".format(_signum, _id);
223     }
224 }
225 
226 
227 struct IORequest {
228     size_t              to_read = 0;
229     bool                allowPartialInput = true;
230     Nbuff               output;
231 
232     IOCallback          callback;
233 }
234 
235 struct IOResult {
236     NbuffChunk          input;
237     Nbuff               output;     // updated output slice
238     bool                timedout;   // if we timedout
239     bool                error;      // if there was an error
240     string toString() const @trusted {
241         import std.format;
242         return "in:%s, out:%s, tmo: %s, error: %s".format(input, output, timedout?"yes":"no", error?"yes":"no");
243     }
244 }
245 
246