1 /*******************************************************************************
2 
3     I/O wait and retry callback manager
4 
5     Provides a simple means for trying a block of code and retrying it if
6     specified conditions occur. The default condition for retrying is if any
7     exceptions are thrown in the code block. Custom success conditions can be
8     easily provided as a delegate to the class' constructor.
9 
10     Usage example:
11 
12     ---
13 
14         import ocean.io.Retry;
15 
16         // Create retry object.
17         auto retry = new Retry;
18 
19         // Code to execute / retry.
20         void doSomething ( )
21         {
22             // whatever
23         }
24 
25         // Maximum number of times to retry code block before giving up.
26         const max_retries = 10;
27 
28         // Execute / retry code block
29         retry(max_retries, doSomething());
30 
31     ---
32 
33     Example using a custom delegate to determine success:
34 
35     ---
36 
37         import ocean.io.Retry;
38 
39         // Function to determine success of executed code block
40         bool decide_success ( lazy void dg )
41         {
42             try
43             {
44                 dg();
45             }
46             // Only retries on io exceptions
47             catch ( IOException e )
48             {
49                 return false;
50             }
51 
52             return true;
53         }
54 
55         // Create retry object.
56         auto retry = new Retry(&decide_success);
57 
58         // Code to execute / retry.
59         void doSomething ( )
60         {
61             // whatever
62         }
63 
64         // Maximum number of times to retry code block before giving up.
65         const max_retries = 10;
66 
67         // Execute / retry code block
68         retry(max_retries, doSomething());
69 
70     ---
71 
72     An extended class, WaitRetry, implements a retryer which pauses for a
73     specified length of time before each retry.
74 
75     Copyright:
76         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
77         All rights reserved.
78 
79     License:
80         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
81         Alternatively, this file may be distributed under the terms of the Tango
82         3-Clause BSD License (see LICENSE_BSD.txt for details).
83 
84 *******************************************************************************/
85 
86 module ocean.io.Retry;
87 
88 
89 
90 
91 import Ctime = core.sys.posix.time : nanosleep, timespec;
92 
93 import core.stdc.time : time_t;
94 
95 
96 
97 /*******************************************************************************
98 
99     Basic retrying class. Retries a specified block of code a specified number
100     of times before giving up. The 'code block' is passed as a lazy bool
101     parameter to the loop() / opCall() method.
102 
103     The failure condition for the block of code can be set by passing a delegate
104     to the constructor. The default failure condition is that any exception
105     thrown while executing the code block indicates failure.
106 
107     The behaviour of the class can be easily modified by inherting and
108     overriding the following methods:
109 
110         retry_decide: decides whether to keep retrying or to give up
111 
112         on_retry: called every time a retry occurs (does nothing by default)
113 
114 *******************************************************************************/
115 
116 class Retry
117 {
118     /***************************************************************************
119 
120         Alias for a delegate which executes a code block and decides if it has
121         succeeded or failed.
122 
123     ***************************************************************************/
124 
125     public alias bool delegate ( lazy void ) SuccessDecider;
126 
127 
128     /***************************************************************************
129 
130         Number of times the current code block has been retried.
131 
132     ***************************************************************************/
133 
134     private uint num_retries;
135 
136 
137     /***************************************************************************
138 
139         Maximum number of retries before giving up on a code block. Note that 0
140         represents unlimited retries.
141 
142     ***************************************************************************/
143 
144     private uint max_retries;
145 
146 
147     /***************************************************************************
148 
149         Delegate to execute a code block and decide whether it's succeeded.
150 
151     ***************************************************************************/
152 
153     private SuccessDecider success_decide;
154 
155 
156     /***************************************************************************
157 
158         Constructor.
159 
160         Params:
161             success_decide = delegate to execute a code block and decide whether
162                 it's succeeded (default to null, in which case the member
163                 default_success_decider is used)
164 
165     ***************************************************************************/
166 
167     public this ( scope SuccessDecider success_decide = null )
168     {
169         this.success_decide = success_decide ? success_decide : &this.default_success_decide;
170     }
171 
172 
173     /***************************************************************************
174 
175         Initiates the execution and possible retrying of a block of code.
176 
177         This method is also aliased as opCall.
178 
179         Note: if max_retries == 0, then no limit to the number of retries is set
180 
181         Params:
182             max_retries = maximum number of times to retry this code block
183                 before giving up
184             dg = code block to execute / retry
185 
186     ***************************************************************************/
187 
188     public void loop ( uint max_retries, lazy void dg )
189     {
190         this.max_retries = max_retries;
191         this.num_retries = 0;
192 
193         bool again;
194         do
195         {
196             auto success = this.success_decide(dg);
197             if ( success )
198             {
199                 again = false;
200             }
201             else
202             {
203                 this.num_retries++;
204                 again = this.retry_decide();
205 
206                 if ( again )
207                 {
208                     this.on_retry();
209                 }
210             }
211         }
212         while ( again );
213     }
214 
215     public alias loop opCall;
216 
217 
218     /***************************************************************************
219 
220         Decides whether to keep retrying or to give up.
221 
222         Returns:
223             true to continue retrying, false to give up
224 
225     ***************************************************************************/
226 
227     protected bool retry_decide ( )
228     {
229         return this.max_retries == 0 || this.num_retries <= this.max_retries;
230     }
231 
232 
233     /***************************************************************************
234 
235         Called before a retry is commenced. The base class behaviour does
236         nothing, but it can be overridden by derived classes to implement
237         special behaviour on retry.
238 
239     ***************************************************************************/
240 
241     protected void on_retry ( )
242     {
243     }
244 
245 
246     /***************************************************************************
247 
248         Default success decider delegate.
249 
250         Executes the provided code block and catches any exceptions thrown.
251         Success is defined as the catching of no exceptions.
252 
253         Params:
254             dg = code block to execute
255 
256         Returns:
257             true on success of code block, false on failure
258 
259     ***************************************************************************/
260 
261     private bool default_success_decide ( lazy void dg )
262     {
263         try
264         {
265             dg();
266             return true;
267         }
268         catch ( Exception e )
269         {
270             return false;
271         }
272     }
273 }
274 
275 
276 
277 /*******************************************************************************
278 
279     Retrying class which waits for a specified amount of time before each retry.
280 
281 *******************************************************************************/
282 
283 class WaitRetry : Retry
284 {
285     /***************************************************************************
286 
287         Number of milliseconds to wait before each retry.
288 
289     ***************************************************************************/
290 
291     private uint retry_wait_ms;
292 
293 
294     /***************************************************************************
295 
296         Constructor.
297 
298         Params:
299             retry_wait_ms = time (in ms) to wait before each retry
300             success_decide = delegate to execute a code block and decide whether
301                 it's succeeded (default to null, in which case the member
302                 default_success_decider is used)
303 
304     ***************************************************************************/
305 
306     public this ( uint retry_wait_ms, scope SuccessDecider success_decide = null )
307     {
308         this.retry_wait_ms = retry_wait_ms;
309 
310         super(success_decide);
311     }
312 
313 
314     /***************************************************************************
315 
316         Wait on retry.
317 
318     ***************************************************************************/
319 
320     override protected void on_retry ( )
321     {
322         sleep(this.retry_wait_ms);
323     }
324 
325 
326     /***************************************************************************
327 
328         Sleep in a multi-thread compatible way.
329         sleep() in multiple threads is not trivial because when several threads
330         simultaneously sleep and the first wakes up, the others will instantly
331         wake up, too. See nanosleep() man page
332 
333         http://www.kernel.org/doc/man-pages/online/pages/man2/nanosleep.2.html
334 
335         or
336 
337         http://www.opengroup.org/onlinepubs/007908799/xsh/nanosleep.html
338 
339         Params:
340             ms = milliseconds to sleep
341 
342     ***************************************************************************/
343 
344     public static void sleep ( time_t ms )
345     {
346         auto ts = Ctime.timespec(ms / 1_000, (ms % 1_000) * 1_000_000);
347 
348         while (Ctime.nanosleep(&ts, &ts)) {}
349     }
350 }
351