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 }