1 /******************************************************************************* 2 3 Epoll-based event scheduler. 4 5 Multiplexes multiple, logical timers into a single timer fd (see 6 ocean.io.select.client.TimerEvent). 7 8 copyright: Copyright (c) 2011 dunnhumby Germany GmbH. 9 All rights reserved. 10 11 version: August 2011 : Initial release 12 13 authors: Gavin Norman 14 15 Requires linking with libebtree: 16 17 --- 18 19 -L-lebtree 20 21 --- 22 23 Usage example: 24 See documented unittest of class, below 25 26 *******************************************************************************/ 27 28 module ocean.io.select.client.TimerSet; 29 30 31 32 33 import ocean.util.container.pool.ObjectPool; 34 35 import ocean.core.Verify; 36 37 import ocean.task.IScheduler; 38 import ocean.io.select.EpollSelectDispatcher; 39 40 import ocean.io.select.timeout.TimerEventTimeoutManager; 41 42 import ocean.time.timeout.model.ITimeoutClient; 43 44 import ocean.meta.types.Qualifiers; 45 import ocean.util.container.map.model.IAllocator; 46 debug import ocean.text.convert.Formatter; 47 48 49 /******************************************************************************* 50 51 Timer set class template. 52 53 Each event is added to the timer set along with a data item (see template 54 parameters). The data item is stored internally to the timer set, along with 55 the event delegate, as a convenience to the end-user, who thus does not need 56 to maintain an external pool of the data items associated with each event. 57 58 Internally, the timer set works using a single timer event, which is 59 registered to epoll with the time until the soonest scheduled event. When 60 the last scheduled event fires, the timer event is unregistered from epoll. 61 62 Params: 63 EventData = type of data to be stored along with each event 64 65 TODO: could probably be adapted to a version which allows simple events 66 without attached data, if we need that. 67 68 *******************************************************************************/ 69 70 public class TimerSet ( EventData ) : TimerEventTimeoutManager 71 { 72 /*************************************************************************** 73 74 Alias for a delegate which is called when a scheduled event fires. 75 76 ***************************************************************************/ 77 78 public alias void delegate ( ref EventData data ) EventFiredDg; 79 80 81 /*************************************************************************** 82 83 Alias for a delegate which is called when a scheduled event is 84 registered, allowing the user to setup the required data for the event. 85 86 ***************************************************************************/ 87 88 public alias void delegate ( ref EventData data ) EventSetupDg; 89 90 91 /*************************************************************************** 92 93 Interface to a scheduled event. 94 95 ***************************************************************************/ 96 97 public interface IEvent 98 { 99 /*********************************************************************** 100 101 Unregisters this event. 102 103 ***********************************************************************/ 104 105 void unregister ( ); 106 } 107 108 /*************************************************************************** 109 110 Internal event class. 111 112 ***************************************************************************/ 113 114 private class Event : ITimeoutClient, IEvent 115 { 116 /*********************************************************************** 117 118 Instance identifier, used by id() method in debug. 119 120 ***********************************************************************/ 121 122 debug static int id_num_; 123 124 debug int id_num; 125 126 127 /*********************************************************************** 128 129 Index of this event in the event pool (required by Pool). 130 131 ***********************************************************************/ 132 133 public size_t object_pool_index; 134 135 136 /*********************************************************************** 137 138 Data associated with this event. 139 140 ***********************************************************************/ 141 142 public EventData data; 143 144 145 /*********************************************************************** 146 147 Delegate to call when this event fires. 148 149 ***********************************************************************/ 150 151 public EventFiredDg fired_dg; 152 153 154 /*********************************************************************** 155 156 Registration of this event in the timeout manager. 157 158 ***********************************************************************/ 159 160 private ExpiryRegistration expiry_registration; 161 162 163 /*********************************************************************** 164 165 Constructor. 166 167 ***********************************************************************/ 168 169 public this ( ) 170 { 171 debug this.id_num = id_num_++; 172 173 this.expiry_registration = new ExpiryRegistration(this); 174 } 175 176 177 /*********************************************************************** 178 179 Registers this event to fire in the specified number of 180 microseconds. 181 182 Params: 183 schedule_us = (minimum) microseconds before event will fire 184 185 ***********************************************************************/ 186 187 public void register ( ulong schedule_us ) 188 { 189 this.expiry_registration.register(schedule_us); 190 } 191 192 193 /*********************************************************************** 194 195 Unregisters this event. 196 197 ***********************************************************************/ 198 199 public void unregister ( ) 200 { 201 this.expiry_registration.drop(); 202 this.outer.events.recycle(this); 203 } 204 205 206 /*********************************************************************** 207 208 ITimeoutClient interface method. Invoked when the client times out. 209 Calls the fired delegate and returns this event to the event pool. 210 211 ***********************************************************************/ 212 213 public void timeout ( ) 214 { 215 auto fired_dg = this.fired_dg; 216 auto data = this.data; 217 this.outer.events.recycle(this); 218 fired_dg(data); 219 } 220 221 222 /*********************************************************************** 223 224 String identifier for debugging. 225 226 ***********************************************************************/ 227 228 debug 229 { 230 private mstring id_buf; 231 232 protected cstring id ( ) 233 { 234 this.id_buf.length = 0; 235 assumeSafeAppend(this.id_buf); 236 sformat(this.id_buf, "Scheduler.Event {}", this.id_num); 237 return this.id_buf; 238 } 239 } 240 } 241 242 243 /*************************************************************************** 244 245 Epoll select dispatcher used to manage the scheduler. Passed as a 246 reference to the constructor. 247 248 ***************************************************************************/ 249 250 private EpollSelectDispatcher epoll; 251 252 253 /*************************************************************************** 254 255 Re-usable pool of scheduled events. 256 257 ***************************************************************************/ 258 259 protected ObjectPool!(Event) events; 260 261 /*************************************************************************** 262 263 Constructor. 264 265 Params: 266 epoll = epoll select dispatcher to use. If null is supplied, the 267 global `theScheduler.epoll()` instance will be used instead 268 (see `schedule()` / `stopTimeout()`) 269 max_events = limit on the number of events which can be managed by 270 the scheduler at one time. (0 = no limit) 271 allocator = use this bucket element allocator for the expiry 272 registration to ISelectClient map. If it is null the default map 273 allocator (BucketElementGCAllocator) is used. 274 275 ***************************************************************************/ 276 277 public this ( EpollSelectDispatcher epoll = null, uint max_events = 0, 278 IAllocator allocator = null ) 279 { 280 super(allocator); 281 282 this.epoll = epoll; 283 284 this.events = new ObjectPool!(Event); 285 286 if ( max_events ) 287 { 288 this.events.setLimit(max_events); 289 } 290 } 291 292 293 /*************************************************************************** 294 295 Registers a new event with the scheduler. 296 297 Params: 298 setup_dg = delegate called to initialise event's associated data 299 fired_dg = delegate called when event fires 300 schedule_us = (minimum) microseconds before event will fire 301 302 Throws: 303 The pool throws a LimitExceededException if the event pool is full 304 and the new event cannot be scheduled. 305 306 Returns: 307 the newly scheduled event 308 309 ***************************************************************************/ 310 311 public IEvent schedule ( scope EventSetupDg setup_dg, scope EventFiredDg fired_dg, 312 ulong schedule_us ) 313 out ( event ) 314 { 315 assert(event !is null); 316 } 317 do 318 { 319 verify(setup_dg !is null, typeof(this).stringof ~ ".schedule: event setup delegate is null"); 320 verify(fired_dg !is null, typeof(this).stringof ~ ".schedule: event fired delegate is null"); 321 322 auto event = this.events.get(new Event); 323 event.fired_dg = fired_dg; 324 325 setup_dg(event.data); 326 327 if ( schedule_us ) 328 { 329 event.register(schedule_us); 330 if (this.epoll is null) 331 theScheduler.epoll().register(this.select_client); 332 else 333 this.epoll.register(this.select_client); 334 } 335 else 336 { 337 event.timeout(); 338 } 339 340 return event; 341 } 342 343 344 /*************************************************************************** 345 346 Returns: 347 number of currently scheduled events 348 349 Note: this method is aliased as 'length' 350 351 ***************************************************************************/ 352 353 public size_t scheduled_events ( ) 354 { 355 return this.events.length; 356 } 357 358 public alias scheduled_events length; 359 360 361 /*************************************************************************** 362 363 Unregisters all registered events (thus calls stopTimeout()). 364 365 ***************************************************************************/ 366 367 public void clear ( ) 368 { 369 scope iterator = this.events..new BusyItemsIterator; 370 foreach ( event; iterator ) 371 { 372 event.unregister(); 373 } 374 this.events.clear(); 375 } 376 377 /*************************************************************************** 378 379 Disables the timer event and unregisters it from epoll. 380 381 ***************************************************************************/ 382 383 override protected void stopTimeout ( ) 384 { 385 super.stopTimeout(); 386 if (this.epoll is null) 387 theScheduler.epoll().unregister(this.select_client); 388 else 389 this.epoll.unregister(this.select_client); 390 } 391 } 392 393 version (unittest) 394 { 395 import ocean.io.Stdout; 396 import ocean.io.select.EpollSelectDispatcher; 397 } 398 399 /// Usage example: 400 unittest 401 { 402 void timerSetTest ( ) 403 { 404 // Struct associated with each event. 405 struct Params 406 { 407 int x; 408 } 409 410 // Delegate called when an event fired. Receives the associated struct. 411 void fired ( ref Params p ) 412 { 413 Stdout.formatln("{} fired", p.x); 414 } 415 416 // Construct required objects. 417 auto epoll = new EpollSelectDispatcher; 418 auto timer_set = new TimerSet!(Params)(epoll); 419 420 // Schedule some events. 421 timer_set.schedule((ref Params p){p.x = 0;}, &fired, 2_000_000); 422 timer_set.schedule((ref Params p){p.x = 1;}, &fired, 4_000_000); 423 timer_set.schedule((ref Params p){p.x = 2;}, &fired, 6_000_000); 424 425 // Set everything going by starting the epoll event loop. 426 Stdout.formatln("Starting eventloop"); 427 epoll.eventLoop; 428 Stdout.formatln("Event loop finished"); 429 } 430 } 431 432 unittest 433 { 434 // create instance to check if it compiles 435 class Dummy { } 436 TimerSet!(Dummy) timer_set; 437 }