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 = 0x00, 17 IN = 0x01, 18 OUT = 0x02, 19 ERR = 0x04, 20 CONN = 0x08, 21 HUP = 0x10, 22 TMO = 0x20, 23 USER = 0x40, 24 IMMED= 0x80, 25 ALL = 0x7f 26 } 27 private immutable string[int] _names; 28 29 shared static this() { 30 _names = [ 31 0:"NONE", 32 1:"IN", 33 2:"OUT", 34 4:"ERR", 35 8:"CONN", 36 16:"HUP", 37 32:"TMO", 38 64:"USER" 39 ]; 40 } 41 42 alias HandlerDelegate = void delegate(AppEvent) @safe; 43 alias SigHandlerDelegate = void delegate(int) @safe; 44 alias FileHandlerFunction = void function(int, AppEvent) @safe; 45 //alias NotificationHandler = void delegate(Notification) @safe; 46 alias FileHandlerDelegate = void delegate(int, AppEvent) @safe; 47 48 string appeventToString(AppEvent ev) @safe pure { 49 import std.format; 50 import std.range; 51 52 string[] a; 53 with(AppEvent) { 54 foreach(e; [IN,OUT,ERR,CONN,HUP,TMO]) { 55 if ( ev & e ) { 56 a ~= _names[e]; 57 } 58 } 59 } 60 return a.join("|"); 61 } 62 63 class NotFoundException : Exception { 64 this(string msg, string file = __FILE__, size_t line = __LINE__) @safe { 65 super(msg, file, line); 66 } 67 } 68 69 class NotImplementedException : Exception { 70 this(string msg, string file = __FILE__, size_t line = __LINE__) @safe { 71 super(msg, file, line); 72 } 73 } 74 75 //final class FileDescriptor { 76 // package { 77 // immutable int _fileno; 78 // HandlerDelegate _handler; 79 // AppEvent _polling; 80 // } 81 // this(int fileno) nothrow @safe { 82 // _fileno = fileno; 83 // } 84 // override string toString() const @safe { 85 // import std.format: format; 86 // return appeventToString(_polling); 87 // //return "FileDescriptor: filehandle: %d, events: %s".format(_fileno, appeventToString(_polling)); 88 // } 89 //} 90 91 //class CanPoll { 92 // union Id { 93 // int fd = -1; 94 // } 95 // Id id; 96 //} 97 98 abstract class EventHandler { 99 abstract void eventHandler(AppEvent) @safe; 100 } 101 102 abstract class FileEventHandler { 103 abstract void eventHandler(int, AppEvent) @safe; 104 } 105 106 final class Timer { 107 private static ulong timer_id = 1; 108 package { 109 immutable ulong _id; 110 immutable SysTime _expires; 111 immutable HandlerDelegate _handler; 112 immutable string _file; 113 immutable int _line; 114 } 115 int opCmp(in Timer other) const nothrow pure @safe { 116 int timeCmp = _expires.opCmp(other._expires); 117 if ( timeCmp != 0 ) { 118 return timeCmp; 119 } 120 return _id < other._id ? -1 : 1; 121 } 122 123 bool eq(const Timer b) const pure nothrow @safe { 124 return this._id == b._id && this._expires == b._expires && this._handler == b._handler; 125 } 126 127 this(Duration d, HandlerDelegate h, string f = __FILE__, int l = __LINE__) @safe { 128 if ( d == Duration.max ) { 129 _expires = SysTime.max; 130 } else { 131 _expires = Clock.currTime + d; 132 } 133 _handler = h; 134 _id = timer_id; 135 _file = f; 136 _line = l; 137 timer_id++; 138 } 139 this(SysTime e, HandlerDelegate h, string f = __FILE__, int l = __LINE__) @safe { 140 enforce(e != SysTime.init, "Unintialized expires for new timer"); 141 enforce(h != HandlerDelegate.init, "Unitialized handler for new Timer"); 142 _expires = e; 143 _handler = h; 144 _file = f; 145 _line = l; 146 _id = timer_id++; 147 } 148 override string toString() const @trusted { 149 import std.format: format; 150 return "timer: expires: %s, id: %d, addr %X (%s:%d)".format(_expires, _id, cast(void*)this, _file, _line); 151 } 152 } 153 154 final class Signal { 155 private static ulong signal_id = 1; 156 package { 157 immutable int _signum; 158 immutable ulong _id; 159 immutable SigHandlerDelegate _handler; 160 } 161 162 this(int signum, SigHandlerDelegate h) { 163 _signum = signum; 164 _handler = h; 165 _id = signal_id++; 166 } 167 int opCmp(in Signal other) const nothrow pure @safe { 168 if ( _signum == other._signum ) { 169 return _id < other._id ? -1 : 1; 170 } 171 return _signum < other._signum ? -1 : 1; 172 } 173 override string toString() const @trusted { 174 import std.format: format; 175 return "signal: signum: %d, id: %d".format(_signum, _id); 176 } 177 } 178 179 struct IORequest { 180 size_t to_read = 0; 181 bool allowPartialInput = true; 182 immutable(ubyte)[] output; 183 184 void delegate(IOResult) @safe callback; 185 186 } 187 188 struct IOResult { 189 immutable(ubyte)[] input; // what we received 190 immutable(ubyte)[] output; // updated output slice 191 bool timedout; // if we timedout 192 bool error; // if there was an error 193 string toString() const @trusted { 194 import std.format; 195 return "in:[%(%02X %)], out:[%(%02X %)], tmo: %s, error: %s".format(input, output, timedout?"yes":"no", error?"yes":"no"); 196 } 197 } 198 199 struct CircBuff(T) { 200 enum Size = 512; 201 private 202 { 203 ushort start=0, length = 0; 204 T[Size] queue; 205 } 206 207 invariant 208 { 209 assert(length<=Size); 210 assert(start<Size); 211 } 212 213 auto get() @safe 214 in 215 { 216 assert(!empty); 217 } 218 out 219 { 220 assert(!full); 221 } 222 do 223 { 224 enforce(!empty); 225 auto v = queue[start]; 226 length--; 227 start = (++start) % Size; 228 return v; 229 } 230 231 void put(T v) @safe 232 in 233 { 234 assert(!full); 235 } 236 out 237 { 238 assert(!empty); 239 } 240 do 241 { 242 enforce(!full); 243 queue[(start + length)%Size] = v; 244 length++; 245 } 246 bool empty() const @safe @property @nogc nothrow { 247 return length == 0; 248 } 249 bool full() const @safe @property @nogc nothrow { 250 return length == Size; 251 } 252 } 253 254 alias Broadcast = Flag!"broadcast"; 255 256 //struct NotificationDelivery { 257 // Notification _n; 258 // Broadcast _broadcast; 259 //} 260 261 //class Notification { 262 // import containers; 263 // 264 // private SList!(void delegate(Notification) @safe) _subscribers; 265 // 266 // void handler(Broadcast broadcast = Yes.broadcast) @trusted { 267 // debug tracef("Handle %s".format(broadcast)); 268 // if ( broadcast ) 269 // { 270 // debug tracef("subscribers %s".format(_subscribers.range)); 271 // foreach(s; _subscribers.range) { 272 // debug tracef("subscriber %s".format(&s)); 273 // s(this); 274 // debug tracef("subscriber %s - done".format(&s)); 275 // } 276 // } else 277 // { 278 // auto s = _subscribers.front; 279 // s(this); 280 // } 281 // } 282 // 283 // void subscribe(void delegate(Notification) @safe s) @safe @nogc nothrow { 284 // _subscribers ~= s; 285 // } 286 // 287 // void unsubscribe(void delegate(Notification) @safe s) @safe @nogc { 288 // _subscribers.remove(s); 289 // } 290 //} 291 //@safe unittest { 292 // import std.stdio; 293 // class TestNotification : Notification { 294 // int _v; 295 // this(int v) { 296 // _v = v; 297 // } 298 // } 299 // 300 // auto ueq = CircBuff!Notification(); 301 // assert(ueq.empty); 302 // assert(!ueq.full); 303 // foreach(i;0..ueq.Size) { 304 // auto ue = new TestNotification(i); 305 // ueq.put(ue); 306 // } 307 // assert(ueq.full); 308 // foreach(n;0..ueq.Size) { 309 // auto i = ueq.get(); 310 // assert(n==(cast(TestNotification)i)._v); 311 // } 312 // assert(ueq.empty); 313 // foreach(i;0..ueq.Size) { 314 // auto ue = new TestNotification(i); 315 // ueq.put(ue); 316 // } 317 // assert(ueq.full); 318 // foreach(n;0..ueq.Size) { 319 // auto i = ueq.get(); 320 // assert(n==(cast(TestNotification)i)._v); 321 // } 322 // // 323 // int testvar; 324 // void d1(Notification n) @safe { 325 // testvar++; 326 // auto v = cast(TestNotification)n; 327 // } 328 // void d2(Notification n) { 329 // testvar--; 330 // } 331 // auto n1 = new TestNotification(1); 332 // n1.subscribe(&d1); 333 // n1.subscribe(&d2); 334 // n1.subscribe(&d1); 335 // n1.handler(Yes.broadcast); 336 // assert(testvar==1); 337 // n1.unsubscribe(&d2); 338 // n1.handler(Yes.broadcast); 339 // assert(testvar==3); 340 //} 341 342 //class Subscription { 343 // NotificationChannel _channel; 344 // void delegate() @safe _handler; 345 // shared this(shared NotificationChannel c, void delegate() @safe h) @safe nothrow { 346 // _channel = c; 347 // _handler = h; 348 // } 349 //} 350 // 351 //private shared int shared_notifications_id; 352 //shared class SharedNotification { 353 // import containers; 354 // import core.atomic; 355 // private { 356 // int _n_id; 357 // //private shared SList!Subscription _subscribers; 358 // private Subscription[] _subscribers; 359 // } 360 // 361 // shared this() @safe { 362 // _n_id = shared_notifications_id; 363 // atomicOp!"+="(shared_notifications_id, 1); 364 // } 365 // 366 // void handler(Broadcast broadcast = Yes.broadcast) @safe { 367 // if ( broadcast ) 368 // { 369 // //foreach(s; _subscribers) { 370 // // s(this); 371 // //} 372 // } else 373 // { 374 // //auto s = _subscribers.front; 375 // //s(this); 376 // } 377 // } 378 // 379 // void subscribe(NotificationChannel c, void delegate() @safe s) @safe nothrow shared { 380 // _subscribers ~= new shared Subscription(c, s); 381 // } 382 // 383 // void unsubscribe(NotificationChannel c, void delegate() @safe s) @safe { 384 // //_subscribers.remove(Subscription(c, s)); 385 // } 386 //} 387 388 389 @safe unittest { 390 //info("testing shared notifications"); 391 //auto sna = new shared SharedNotification(); 392 //auto snb = new shared SharedNotification(); 393 //assert(sna._n_id == 0); 394 //assert(snb._n_id == 1); 395 //shared NotificationChannel c = new shared NotificationChannel; 396 //shared SharedNotification sn = new shared SharedNotification(); 397 //sn.subscribe(c, delegate void() {}); 398 //info("testing shared notifications - done"); 399 }