1 /*******************************************************************************
2 
3     Fiber-suspending timer event. Allows a fiber to be suspended for a fixed
4     time period.
5 
6     Copyright:
7         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
8         All rights reserved.
9 
10     License:
11         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
12         Alternatively, this file may be distributed under the terms of the Tango
13         3-Clause BSD License (see LICENSE_BSD.txt for details).
14 
15 *******************************************************************************/
16 
17 module ocean.io.select.client.FiberTimerEvent;
18 
19 
20 
21 
22 import ocean.core.MessageFiber;
23 
24 import ocean.core.Verify;
25 
26 import ocean.sys.TimerFD;
27 
28 import ocean.io.select.client.model.IFiberSelectClient;
29 
30 import ocean.meta.types.Qualifiers;
31 import core.stdc.math: modf;
32 
33 
34 
35 /*******************************************************************************
36 
37     Fiber-suspending timer event. Allows a fiber to be suspended for a fixed
38     time period.
39 
40 *******************************************************************************/
41 
42 public class FiberTimerEvent : IFiberSelectClient
43 {
44     /***************************************************************************
45 
46         Token used when suspending / resuming fiber.
47 
48     ***************************************************************************/
49 
50     static private MessageFiber.Token TimerFired;
51 
52 
53     /***************************************************************************
54 
55         Static ctor. Initialises fiber token.
56 
57     ***************************************************************************/
58 
59     static this ( )
60     {
61         TimerFired = MessageFiber.Token("timer_fired");
62     }
63 
64 
65     /***************************************************************************
66 
67         Timer fd.
68 
69     ***************************************************************************/
70 
71     private TimerFD timer;
72 
73 
74     /***************************************************************************
75 
76         Constructor. Initialises (but does not register) the timer fd.
77 
78         Params:
79             fiber = fiber instance to be suspended/resumed by the timer
80             realtime = true:  use a settable system-wide clock.
81                        false: use a non-settable clock that is not affected by
82                        discontinuous changes in the system clock (e.g., manual
83                        changes to system time).
84 
85     ***************************************************************************/
86 
87     public this ( SelectFiber fiber, bool realtime = false )
88     {
89         super(fiber);
90 
91         this.timer = new TimerFD(realtime);
92     }
93 
94 
95     /***************************************************************************
96 
97         Returs:
98             the epoll events to register for.
99 
100     ***************************************************************************/
101 
102     public override Event events ( )
103     {
104         return Event.EPOLLIN;
105     }
106 
107 
108     /***************************************************************************
109 
110         Required by ISelectable interface.
111 
112         Returns:
113             file descriptor used to manage custom event
114 
115     ***************************************************************************/
116 
117     public override Handle fileHandle ( )
118     {
119         return this.timer.fileHandle;
120     }
121 
122 
123     /***************************************************************************
124 
125         Sets the timer to a number of seconds and milliseconds approximating the
126         floating point value specified, registers it, and suspends the fiber
127         until it fires.
128 
129         Params:
130             s = number of seconds to suspend fiber for
131 
132         In:
133             s must be at least 0, which implies it must not be NaN. +∞ is
134             tolerated and uses the highest possible timer value.
135 
136     ***************************************************************************/
137 
138     public void wait ( double s )
139     {
140         verify(s >= 0); // tolerate +∞
141         double int_s;
142         auto ms = cast(uint)(modf(s, &int_s) * 1000);
143         this.wait(cast(uint)int_s, ms);
144     }
145 
146     /***************************************************************************
147 
148         Sets the timer to the specified number of seconds and milliseconds,
149         registers it, and suspends the fiber until it fires. If both seconds and
150         milliseconds are 0, the fiber is not suspended and the event is not
151         registered with epoll -- no pause occurs.
152 
153         Params:
154             s = number of seconds to suspend fiber for
155             ms = number of additional milliseconds to suspend fiber for
156 
157     ***************************************************************************/
158 
159     public void wait ( uint s, uint ms = 0 )
160     {
161         if ( s == 0 && ms == 0 ) return;
162 
163         this.timer.set(s, ms, 0, 0);
164         this.fiber.register(this);
165         this.fiber.suspend(TimerFired, this, this.fiber.Message(true));
166     }
167 
168 
169     /***************************************************************************
170 
171         Handles events which occurred for the timer event fd. Resumes the fiber.
172 
173         (Implements an abstract super class method.)
174 
175         Params:
176             events = events which occurred for the fd
177 
178         Returns:
179             false if the fiber is finished or true if it keeps going
180 
181     ***************************************************************************/
182 
183     public override bool handle ( Event events )
184     {
185         verify(this.fiber.waiting);
186         this.timer.handle();
187 
188         SelectFiber.Message message = this.fiber.resume(TimerFired, this);
189 
190         // FIXME: this should actually always return false, as we always want
191         // the timer to be one-shot. However, there is a fundamental bug with
192         // the way the messages are handled. The problem is that
193         // IFiberSelectClient.finalize() does not know whether the fiber is
194         // still in use (suspended with no client registered) or whether it
195         // should be killed. This will need to be revisited and fixed.
196         return (message.active == message.active.num)? message.num != 0 : false;
197     }
198 
199 
200     /***************************************************************************
201 
202         Returns:
203             identifier string for this instance, including the remaining time
204 
205     ***************************************************************************/
206 
207     debug
208     {
209         private mstring time_buffer;
210         import ocean.core.Array : copy;
211         import ocean.text.convert.Formatter;
212 
213         public override cstring id ( )
214         {
215             this.time_buffer.copy(super.id());
216             auto time = this.timer.time();
217 
218             sformat(this.time_buffer, ": {}s {}ns",
219                 time.it_value.tv_sec, time.it_value.tv_nsec);
220             return this.time_buffer;
221         }
222     }
223 
224 }