1 /******************************************************************************* 2 3 Base class for all types of pool. Provides the following features: 4 * A set of items, each either busy or idle. 5 * Idle items can be got from the pool, thus becoming busy, using get(). 6 * Busy items can be returned to the pool, thus becoming idle, with 7 recycle(). 8 * The total number of items in the pool, as well as the number of busy 9 or idle items can be queried. 10 * The entire pool can be emptied, returning all items to the idle state, 11 with clear(). 12 * A limit can be applied to the pool, which prevents more than the 13 specified number of items from being created. 14 * A specified number of items can be pre-allocated in the pool using the 15 fill() method. 16 17 Each item in the pool has an index, which allows a simple lookup of an item 18 to its position in the internal array of items. The item index is defined by 19 the abstract methods setItemIndex() and getItemIndex() (i.e. in the base 20 class there is no definition of how this item index is implemented). 21 22 Note that the IPool class is abstract, and provides only the internal 23 framework required for getting and recycling pool items (see get_() and 24 recycle_()). 25 26 Copyright: 27 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 28 All rights reserved. 29 30 License: 31 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 32 Alternatively, this file may be distributed under the terms of the Tango 33 3-Clause BSD License (see LICENSE_BSD.txt for details). 34 35 *******************************************************************************/ 36 37 module ocean.util.container.pool.model.IPool; 38 39 40 41 42 import ocean.meta.types.Qualifiers; 43 44 import ocean.util.container.pool.model.IPoolInfo; 45 import ocean.util.container.pool.model.ILimitable; 46 47 import ocean.core.Array: copy; 48 import ocean.core.Exception; 49 50 51 52 /******************************************************************************* 53 54 Core pool implementation. 55 56 *******************************************************************************/ 57 58 public abstract class IPool : IPoolInfo, ILimitable 59 { 60 import ocean.core.Verify; 61 62 /*************************************************************************** 63 64 Pool item union. The list of pool items is an array of Item; the 65 subclass specifies which member is actually used. 66 67 ***************************************************************************/ 68 69 protected union Item 70 { 71 /*********************************************************************** 72 73 Object to store class instances in the pool 74 75 ***********************************************************************/ 76 77 Object obj; 78 79 /*********************************************************************** 80 81 Pointer to store struct instances in the pool 82 83 ***********************************************************************/ 84 85 void* ptr; 86 87 /*********************************************************************** 88 89 Creates an instance of this type from an object. 90 91 Params: 92 obj = object to store in union 93 94 ***********************************************************************/ 95 96 static typeof (this) from ( Object obj ) 97 { 98 typeof (this) item; 99 100 item.obj = obj; 101 102 return item; 103 } 104 105 /*********************************************************************** 106 107 Creates an instance of this type from a pointer. 108 109 Params: 110 ptr = pointer to store in union 111 112 ***********************************************************************/ 113 114 static typeof (this) from ( void* ptr ) 115 { 116 typeof (this) item; 117 118 item.ptr = ptr; 119 120 return item; 121 } 122 } 123 124 /*************************************************************************** 125 126 Maximum number of items allowed when the pool is limited. This value has 127 no meaning if this.limited is false. 128 129 ***************************************************************************/ 130 131 private size_t limit_max; 132 133 /*************************************************************************** 134 135 May be set to true at any time to limit the number of items in pool to 136 the current number or to false to disable limitation. 137 138 ***************************************************************************/ 139 140 public bool limited = false; 141 142 /*************************************************************************** 143 144 List of items (objects) in pool, busy items first 145 146 ***************************************************************************/ 147 148 protected Item[] items; 149 150 /*************************************************************************** 151 152 Number of busy items in pool 153 154 ***************************************************************************/ 155 156 protected size_t num_busy_ = 0; 157 158 /*************************************************************************** 159 160 Reused exception instance 161 162 ***************************************************************************/ 163 164 private LimitExceededException limit_exception; 165 166 /*************************************************************************/ 167 168 invariant () 169 { 170 assert (this.num_busy_ <= this.items.length); 171 } 172 173 /*************************************************************************** 174 175 Constructor 176 177 ***************************************************************************/ 178 179 public this ( ) 180 { 181 this.limit_exception = new .LimitExceededException; 182 } 183 184 /*************************************************************************** 185 186 Returns the number of items in pool. 187 188 Returns: 189 the number of items in pool 190 191 ***************************************************************************/ 192 193 override public size_t length ( ) 194 { 195 return this.items.length; 196 } 197 198 /*************************************************************************** 199 200 Returns the number of busy items in pool. 201 202 Returns: 203 the number of busy items in pool 204 205 ***************************************************************************/ 206 207 override public size_t num_busy ( ) 208 { 209 return this.num_busy_; 210 } 211 212 /*************************************************************************** 213 214 Returns the number of idle items in pool. 215 216 Returns: 217 the number of idle items in pool 218 219 ***************************************************************************/ 220 221 override public size_t num_idle ( ) 222 { 223 return this.items.length - this.num_busy_; 224 } 225 226 /*************************************************************************** 227 228 Returns the limit of number of items in pool or unlimited if currently 229 unlimited. 230 231 Returns: 232 the limit of number of items in pool or 0 if currently unlimited 233 234 ***************************************************************************/ 235 236 override public size_t limit ( ) 237 { 238 return this.limited? this.limit_max : this.unlimited; 239 } 240 241 /*************************************************************************** 242 243 Returns: 244 true if the number of items in the pool is limited, false otherwise 245 246 ***************************************************************************/ 247 248 override public bool is_limited ( ) 249 { 250 return this.limited; 251 } 252 253 /*************************************************************************** 254 255 Recycles all items in the pool. 256 257 Returns: 258 this instance 259 260 ***************************************************************************/ 261 262 public typeof(this) clear ( ) 263 { 264 foreach (item; this.items[0..this.num_busy_]) 265 { 266 this.resetItem(item); 267 } 268 269 this.num_busy_ = 0; 270 271 return this; 272 } 273 274 /*************************************************************************** 275 276 Sets the limit of number of items in pool or disables limitation for 277 limit = unlimited. When limiting the pool, any excess idle items are 278 reset and deleted. 279 280 Params: 281 limit = new limit of number of items in pool; unlimited disables 282 limitation 283 284 Returns: 285 new limit 286 287 Throws: 288 LimitExceededException if the number of busy pool items exceeds 289 the desired limit. 290 291 ***************************************************************************/ 292 293 override public size_t setLimit ( size_t limit ) 294 out 295 { 296 debug (ObjectPoolConsistencyCheck) foreach (item; this.items) 297 { 298 assert (item.ptr !is null); 299 } 300 } 301 do 302 { 303 this.limited = limit != this.unlimited; 304 305 if ( this.limited ) 306 { 307 this.limit_exception.check(this, this.num_busy_ <= limit, 308 "pool already contains more busy items than requested limit", 309 __FILE__, __LINE__); 310 311 this.limit_max = limit; 312 313 if ( limit < this.items.length ) 314 { 315 this.truncate(this.items.length - limit); 316 } 317 } 318 319 return limit; 320 } 321 322 /*************************************************************************** 323 324 Checks if item is currently busy. 325 326 Params: 327 item = item to check 328 329 Returns: 330 true if item is currently busy, false otherwise. 331 332 ***************************************************************************/ 333 334 public bool isBusy ( Item item ) 335 { 336 return this.getItemIndex(item) < this.num_busy_; 337 } 338 339 /*************************************************************************** 340 341 Ensures that the pool contains at least the specified number of items. 342 Useful to pre-allocate a pool of a certain size. 343 344 Params: 345 num = minimum number of items desired in pool 346 new_item = expression that creates a new Item instance 347 348 Returns: 349 this 350 351 Throws: 352 LimitExceededException if the requested number of items exceeds 353 the previously specified limit. 354 355 ***************************************************************************/ 356 357 protected typeof(this) fill_ ( size_t num, lazy Item new_item ) 358 { 359 if ( this.items.length < num ) 360 { 361 this.limit_exception.check(this, num <= limit, 362 "cannot fill pool to larger than specified limit", __FILE__, 363 __LINE__); 364 365 auto old_len = this.items.length; 366 this.items.length = num; 367 assumeSafeAppend(this.items); 368 369 foreach ( ref item; this.items[old_len .. $] ) 370 { 371 item = new_item(); 372 verify (!this.isNull(item)); 373 } 374 } 375 376 return this; 377 } 378 379 /*************************************************************************** 380 381 Takes an idle item from the pool or creates a new one if all items are 382 busy or the pool is empty. 383 384 Params: 385 new_item = expression that creates a new Item instance 386 387 Returns: 388 pool item 389 390 Throws: 391 LimitExceededException if limitation is enabled and all pool items 392 are busy 393 394 ***************************************************************************/ 395 396 protected Item get_ ( lazy Item new_item ) 397 out (_item_out) 398 { 399 auto item_out = cast(Item) _item_out; 400 401 assert (!this.isNull(item_out)); 402 403 assert (this.isSame(item_out, this.items[this.num_busy_ - 1])); 404 405 debug (ObjectPoolConsistencyCheck) 406 { 407 foreach (item; this.items[0 .. this.num_busy_ - 1]) 408 { 409 assert (!this.isSame(item, item_out)); 410 } 411 412 if (this.num_busy_ < this.items.length) 413 { 414 foreach (item; this.items[this.num_busy_ + 1 .. $]) 415 { 416 assert (!this.isSame(item, item_out)); 417 } 418 } 419 } 420 } 421 do 422 { 423 Item item; 424 425 if (this.num_busy_ < this.items.length) 426 { 427 item = this.items[this.num_busy_]; 428 429 verify (!this.isNull(item)); 430 } 431 else 432 { 433 this.limit_exception.check(this, this.num_busy_ < this.limit, 434 "limit reached: no free items", __FILE__, __LINE__); 435 436 item = new_item(); 437 438 this.items ~= item; 439 440 verify (!this.isNull(item)); 441 } 442 443 this.setItemIndex(item, this.num_busy_++); 444 445 return item; 446 } 447 448 /*************************************************************************** 449 450 Obtains the n-th pool item. n must be less than the value returned by 451 length(). 452 453 Caution: The item must not be recycled; while the item is in use, only 454 opIndex(), opApply() and length() may be called. 455 456 Params: 457 n = item index 458 459 Returns: 460 n-th pool item 461 462 ***************************************************************************/ 463 464 protected Item opIndex_ ( size_t n ) 465 { 466 return this.items[n]; 467 } 468 469 /*************************************************************************** 470 471 Puts item back to the pool. 472 473 Params: 474 item_in = item to put back 475 476 ***************************************************************************/ 477 478 protected void recycle_ ( Item item_in ) 479 { 480 verify (this.num_busy_ > 0, "nothing is busy so there is nothing to recycle"); 481 482 size_t index = this.getItemIndex(item_in); 483 484 verify (index < this.items.length, 485 "index of recycled item out of range"); 486 487 verify (this.isSame(item_in, this.items[index]), "wrong index in recycled item"); 488 489 verify (index < this.num_busy_, "recycled item is idle"); 490 491 Item* item = this.items.ptr + index, 492 first_idle_item = this.items.ptr + --this.num_busy_; 493 494 this.resetItem(item_in); 495 496 *item = *first_idle_item; 497 498 *first_idle_item = item_in; 499 500 this.setItemIndex(*item, index); 501 502 this.setItemIndex(*first_idle_item, this.num_busy_); 503 } 504 505 /*************************************************************************** 506 507 Sets the object pool index to item. 508 509 Params: 510 item = item to set index 511 n = index to set item to 512 513 ***************************************************************************/ 514 515 abstract protected void setItemIndex ( Item item, size_t n ); 516 517 /*************************************************************************** 518 519 Gets the object pool index of item. 520 521 Params: 522 item = item to get index from 523 524 Returns: 525 object pool index of item. 526 527 ***************************************************************************/ 528 529 abstract protected size_t getItemIndex ( Item item ); 530 531 /*************************************************************************** 532 533 Resets item. 534 535 Params: 536 item = item to reset 537 538 ***************************************************************************/ 539 540 abstract protected void resetItem ( Item item ); 541 542 /*************************************************************************** 543 544 Deletes item and sets it to null. 545 546 Params: 547 item = item to delete 548 549 ***************************************************************************/ 550 551 abstract protected void deleteItem ( ref Item item ); 552 553 /*************************************************************************** 554 555 Checks a and b for identity. 556 557 Params: 558 a = item to check for being indentical to b 559 b = item to check for being indentical to a 560 561 Returns: 562 true if a and b are identical or false otherwise. 563 564 ***************************************************************************/ 565 566 abstract protected bool isSame ( Item a, Item b ); 567 568 /*************************************************************************** 569 570 Checks if item is null. 571 572 Params: 573 item = item to check for being null 574 575 Returns: 576 true if item is null or false otherwise. 577 578 ***************************************************************************/ 579 580 abstract protected bool isNull ( Item item ); 581 582 /*************************************************************************** 583 584 Removes idle items from the pool. Excess idle items are reset and 585 deleted. 586 587 Params: 588 remove = number of idle items to remove from pool 589 590 ***************************************************************************/ 591 592 protected void truncate ( size_t remove ) 593 { 594 verify(remove <= this.num_idle); 595 596 foreach ( ref item; this.items[this.items.length - remove .. $] ) 597 { 598 this.resetItem(item); 599 this.deleteItem(item); 600 } 601 602 this.items.length = this.items.length - remove; 603 assumeSafeAppend(this.items); 604 } 605 } 606 607 /******************************************************************************* 608 609 LimitExceededException class 610 611 *******************************************************************************/ 612 613 public class LimitExceededException : Exception 614 { 615 mixin ReusableExceptionImplementation!(); 616 617 /*************************************************************************** 618 619 Limit which was exceeded when this instance has been thrown 620 621 ***************************************************************************/ 622 623 size_t limit; 624 625 /*************************************************************************** 626 627 Throws this instance if ok is false 628 629 Params: 630 pool = instance that's throwing the exception 631 ok = condition to check if limitation is enabled 632 msg = message 633 file = source code file 634 line = source code line 635 636 Throws: 637 this instance if ok is false 638 639 ***************************************************************************/ 640 641 void check ( IPool pool, bool ok, lazy cstring msg, istring file, long line ) 642 { 643 this.limit = pool.items.length; 644 this.enforce(ok, msg, file, line); 645 } 646 }