1 module yu.timer.eventlooptimer;
2 
3 import core.memory;
4 import core.sys.posix.time;
5 
6 import std.socket : socket_t;
7 
8 import yu.eventloop;
9 import yu.exception : yuCathException;
10 
11 @trusted final class EventLoopTimer : EventCallInterface {
12     this(EventLoop loop) {
13         _loop = loop;
14         _event = AsyncEvent(AsynType.TIMER, this);
15     }
16 
17     ~this() {
18         if (isActive) {
19             onClose();
20         }
21     }
22 
23     pragma(inline, true) @property bool isActive() nothrow {
24         return _event.isActive;
25     }
26 
27     pragma(inline) void setCallBack(CallBack cback) {
28         _callBack = cback;
29     }
30 
31     bool start(ulong msesc) {
32         if (isActive() || msesc <= 0)
33             return false;
34         static if (IOMode == IOMode.kqueue || CustomTimer) {
35             _event.timeOut = cast(long) msesc;
36         } else static if (IOMode == IOMode.epoll) {
37             import yu.eventloop.selector.epoll;
38 
39             //  _timeout = msesc;
40             auto fd = cast(socket_t) timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
41             _event = AsyncEvent(AsynType.TIMER, this, fd, true);
42             itimerspec its;
43             ulong sec, nsec;
44             sec = msesc / 1000;
45             nsec = (msesc % 1000) * 1_000_000;
46             its.it_value.tv_sec = cast(typeof(its.it_value.tv_sec)) sec;
47             its.it_value.tv_nsec = cast(typeof(its.it_value.tv_nsec)) nsec;
48             its.it_interval.tv_sec = its.it_value.tv_sec;
49             its.it_interval.tv_nsec = its.it_value.tv_nsec;
50             int err = timerfd_settime(_event.fd, 0, &its, null);
51             if (err == -1) {
52                 import core.sys.posix.unistd;
53 
54                 close(_event.fd);
55                 return false;
56             }
57         }
58         return _loop.addEvent(&_event);
59     }
60 
61     pragma(inline) void stop() nothrow {
62         onClose();
63     }
64 
65 protected:
66     override void onRead() nothrow {
67         static if (IOMode == IO_MODE.epoll) {
68             import core.sys.posix.unistd;
69 
70             ulong value;
71             read(_event.fd, &value, 8);
72         }
73         if (_callBack) {
74             _callBack();
75         } else {
76             onClose();
77         }
78     }
79 
80     override void onWrite() nothrow {
81     }
82 
83     override void onClose() nothrow {
84         if (!isActive)
85             return;
86         _loop.delEvent(&_event);
87         static if (IOMode == IO_MODE.epoll) {
88             import core.sys.posix.unistd;
89 
90             close(_event.fd);
91         }
92     }
93 
94 private:
95     // ulong _timeout;
96     CallBack _callBack;
97     AsyncEvent _event;
98     EventLoop _loop;
99 }
100 
101 unittest {
102     import std.stdio;
103     import std.datetime;
104     import yu.memory.gc;
105     import yu.exception;
106 
107     EventLoop loop = new EventLoop();
108 
109     EventLoopTimer tm = new EventLoopTimer(loop);
110 
111     int cout = -1;
112     ulong time;
113 
114     void timeout() nothrow {
115         yuCathException((){
116             writeln("time  : ", Clock.currTime().toSimpleString());
117             ++cout;
118             if (cout == 0) {
119                 time = Clock.currTime().toUnixTime!long();
120                 return;
121             }
122 
123             ++time;
124             assert(time == Clock.currTime().toUnixTime!long());
125 
126             if (cout > 5) {
127                 writeln("loop stop!!!");
128                 tm.stop();
129                 loop.stop();
130             }
131         }());
132     }
133 
134     tm.setCallBack(&timeout);
135 
136     tm.start(1000);
137 
138     loop.run();
139 
140     gcFree(tm);
141     gcFree(loop);
142 }