1 module heaploop.looping; 2 3 import events; 4 import duv.c; 5 import duv.types; 6 import core.thread; 7 8 enum RunMode { 9 Default = 0, 10 Once, 11 NoWait 12 }; 13 14 class Loop { 15 private: 16 uv_loop_t * _loopPtr; 17 bool _custom; 18 static Loop _current; 19 public: 20 21 this(uv_loop_t * ptr, bool custom) { 22 _loopPtr = ptr; 23 _custom = custom; 24 } 25 26 @property bool isCustom() { 27 return _custom; 28 } 29 30 @property static Loop current() { 31 if(_current is null) { 32 _current = new Loop(uv_default_loop, false); 33 } 34 return _current; 35 } 36 void run(RunMode mode) { 37 uv_run(_loopPtr, cast(uv_run_mode)mode); 38 } 39 @property uv_loop_t* handle() { 40 return _loopPtr; 41 } 42 } 43 44 Action!void loop(RunMode mode = RunMode.Default) { 45 return new Action!void((trigger) { 46 trigger(); 47 Loop.current.run(RunMode.Default); 48 }); 49 } 50 51 interface Looper { 52 @property Loop loop(); 53 } 54 55 class OperationContext(T:Looper) { 56 private: 57 Fiber _fiber; 58 T _target; 59 public: 60 this(T target) { 61 _fiber = Fiber.getThis; 62 _target = target; 63 debug std.stdio.writeln("new OperationContext"); 64 } 65 ~this() { 66 debug std.stdio.writeln("OperationContext destroyed"); 67 } 68 int status; 69 duv_error error; 70 bool finish; 71 72 void update(int status) { 73 this.status = status; 74 error = duv_last_error(this.status, target.loop.handle); 75 } 76 77 void resume() { 78 debug std.stdio.writeln("Trying to resume while the fiber is in state ", _fiber.state); 79 if(_fiber.state != Fiber.State.HOLD) { 80 return; 81 } 82 _fiber.call; 83 } 84 85 @property bool hasError() pure nothrow { 86 return error.isError; 87 } 88 89 @property Fiber fiber() nothrow { 90 return _fiber; 91 } 92 93 @property target() nothrow { 94 return _target; 95 } 96 97 void yield() { 98 fiber.yield; 99 } 100 101 void completed() { 102 error.completed; 103 } 104 } 105 106 @property bool isError(int status) pure nothrow { 107 return status < 0; 108 } 109 110 @property bool isError(duv_error error) pure nothrow { 111 return error.name !is null; 112 } 113 114 void completed(duv_error error, string file = __FILE__, size_t line = __LINE__) { 115 if(error.isError) { 116 throw new LoopException(error.message, error.name, file, line); 117 } 118 } 119 120 class LoopException : Exception 121 { 122 private: 123 string _name; 124 125 public: 126 this(string msg, string name, string file = __FILE__, size_t line = __LINE__, Throwable next = null) { 127 super(msg, file, line, next); 128 _name = name; 129 } 130 131 @property string name() pure nothrow { 132 return _name; 133 } 134 135 override string toString() { 136 return std..string.format("%s: %s", this.name, this.msg); 137 } 138 139 } 140 141 alias void delegate(Check sender) CheckDelegate; 142 class Check : Handle { 143 private: 144 CheckDelegate _delegate; 145 bool _started; 146 protected: 147 override void initializeHandle() { 148 uv_check_init(loop.handle, this.handle); 149 } 150 public: 151 this(Loop loop = Loop.current) { 152 super(loop, uv_handle_type.CHECK); 153 } 154 155 void start(CheckDelegate del) { 156 stop(); 157 _started = true; 158 _delegate = del; 159 duv_check_start(this.handle, this, (h, c, s) { 160 Check self = cast(Check)c; 161 self._delegate(self); 162 }); 163 } 164 165 void stop() { 166 if(!_started) return; 167 _delegate = null; 168 duv_check_stop(handle); 169 _started = false; 170 } 171 172 @property { 173 174 uv_check_t* handle() { 175 return cast(uv_check_t*)super.handle; 176 } 177 178 bool started() { 179 return _started; 180 } 181 182 } 183 184 alias Handle.handle handle; 185 186 187 ~this() { 188 stop(); 189 } 190 191 static Check once(void delegate(Check check) del, Loop loop = Loop.current) { 192 auto check = new Check(loop); 193 check.start((c) { 194 scope (exit) c.stop; 195 scope (exit) c.close; 196 del(c); 197 }); 198 return check; 199 } 200 } 201 202 abstract class Handle : Looper { 203 private: 204 void * _handle; 205 Loop _loop; 206 bool _isOpen; 207 208 protected: 209 210 abstract void initializeHandle(); 211 212 void closeCleanup(bool async) { 213 214 } 215 216 void ensureOpen(string callerName = __FUNCTION__) { 217 if(!isOpen) { 218 throw new LoopException(std..string.format("%s requires the handle to be open", callerName), "CLOSED_HANDLE"); 219 } 220 } 221 222 223 public: 224 225 this(Loop loop, uv_handle_type type) { 226 _loop = loop; 227 _handle = duv__handle_alloc(type); 228 _isOpen = true; 229 this.initializeHandle(); 230 debug std.stdio.writeln("(Handle just initialized OPEN handle ", _handle, ")"); 231 } 232 233 ~this() { 234 debug std.stdio.writeln("Destroying handle"); 235 close(); // close the handle without waiting 236 } 237 238 @property { 239 Loop loop() pure nothrow { 240 return _loop; 241 } 242 bool isOpen() { 243 if(_isOpen) { 244 return !duv_is_closing(this.handle); 245 } 246 return _isOpen; 247 } 248 uv_handle_t* handle() pure nothrow { 249 return cast(uv_handle_t*)_handle; 250 } 251 } 252 void close(bool async=true) { 253 if(!isOpen) { 254 debug std.stdio.writeln("(tried to close but handle ", _handle, " was reported to be closed or closing already)"); 255 return; 256 } 257 _isOpen = false; 258 debug std.stdio.writeln("(about to CLOSE handle ", _handle, ") of type ", this); 259 closeCleanup(true); 260 debug std.stdio.writeln("closing handle async"); 261 262 duv_handle_close_async(this.handle); 263 debug std.stdio.writeln("closed handle async"); 264 _loop = null; 265 } 266 267 }