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