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 }