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