1 /******************************************************************************* 2 3 Pool of structs or classes. Adds the following features to the base class: 4 * Iteration over all items in the pool, or all busy or idle items. (See 5 further notes below.) 6 * get() and fill() methods which accept a lazy parameter for a new item 7 to be added to the pool, if needed. 8 * For structs, and classes with a default (paramaterless) constructor, 9 get() and fill() methods which automatically create new pool items, 10 without requiring them to be passed via a lazy argument. 11 * Implementation of the item index (required by IPool) as a size_t member 12 of the item type, called 'object_pool_index'. It is required that the 13 item type has this member. 14 15 Iteration can either be 'safe' or 'unsafe' (read-only). R/W (safe) iteration 16 operates on an internal copy of the real pool data, thus making it safe to 17 modify the pool during an iteration, but obviously entailing additional work 18 due to needing to copy the data. Read-only (unsafe) iteration iterates over 19 the actual items in the pool, meaning that it is not safe to modify the pool 20 while iterating. 21 22 Both types of iteration are handled by scope classes which must be newed to 23 get access to an iterator. The R/W (safe) iterator scope classes perform the 24 required copy of the set of items to be iterated over upon construction. As 25 the IAggregatePool instance contains a single buffer which is used to store 26 the iteration set for safe iterators, it is only possible for a single safe 27 iterator to be newed at a time. There are asserts in the code to enforce 28 this. (The advantage of having the safe iterator as a scope class is that it 29 can be newed, performing the required copy once, then used multiple times, 30 rather then doing the copy upon every iteration, as might be the case if a 31 simple opApply method existed.) 32 33 Iteration usage example (with ObjectPool): 34 35 --- 36 37 import ocean.util.container.pool.ObjectPool; 38 39 void main ( ) 40 { 41 class MyClass { size_t object_pool_index; } 42 43 auto pool = new ObjectPool!(MyClass); 44 45 // use pool 46 47 scope busy_items = pool.new BusyItemsIterator; 48 49 foreach ( busy_item; busy_items ) 50 { 51 // busy_item now iterates over the items in the pool that 52 // were busy when busy_items was created. 53 } 54 } 55 56 --- 57 58 Important note about newing pool iterators: 59 60 Pool iterators must be newed as shown in the example above, *not* like this: 61 62 foreach ( busy_item; pool.new BusyItemsIterator ) 63 64 This is because the iterators are declared as scope classes, meaning that 65 the compiler should enforce that they can *only* be newed as scope (i.e. on 66 the stack). Unfortunately the compiler doesn't always enforce this 67 requirement, and will allow a scope class to be allocated on the heap in 68 certain situations, one of these being the foreach situation shown above. 69 70 Note about ref iteration over pools: 71 72 If the pool items are structs, 'ref' iteration is required to make the 73 modification of the items iterated over permanent. For objects 'ref' should 74 not be used. 75 76 Copyright: 77 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 78 All rights reserved. 79 80 License: 81 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 82 Alternatively, this file may be distributed under the terms of the Tango 83 3-Clause BSD License (see LICENSE_BSD.txt for details). 84 85 *******************************************************************************/ 86 87 module ocean.util.container.pool.model.IAggregatePool; 88 89 90 91 92 import ocean.transition; 93 94 import ocean.util.container.pool.model.IPool; 95 import ocean.util.container.pool.model.IFreeList; 96 97 import ocean.core.Array: copyExtend; 98 99 100 101 /******************************************************************************* 102 103 Template to determine the internal item type of a free list. 104 105 Params: 106 T = item type to be stored in free list 107 108 *******************************************************************************/ 109 110 private template ItemType_ ( T ) 111 { 112 static if (is(T == class)) 113 { 114 alias T ItemType_; 115 } 116 else 117 { 118 alias T* ItemType_; 119 } 120 } 121 122 123 /******************************************************************************* 124 125 Base class for pools of aggregate types (classes or structs). The items' 126 index (required by the IPool base class) is implemented as a size_t member 127 named 'object_pool_index', which is expected to exist in the type stored in 128 the pool. 129 130 Note: certain methods are overridden and have asserts added to them to 131 support the iteration. These asserts are deliberately not placed in an in 132 contract, due to the way in contracts work -- in a class hierarchy only 133 *one* of the class' in contracts must pass, *not* all. In this case, as the 134 base class has no in contract, it would always pass, making any in contracts 135 added here irrelevant. 136 137 Params: 138 T = type stored in pool (must be a struct or a class) 139 140 *******************************************************************************/ 141 142 public abstract class IAggregatePool ( T ) : IPool, IFreeList!(ItemType_!(T)) 143 { 144 import ocean.core.Verify; 145 146 /*************************************************************************** 147 148 Asserts that T is either a struct or a class. 149 150 ***************************************************************************/ 151 152 static assert(is(T == class) || is(T == struct)); 153 154 /*************************************************************************** 155 156 Asserts that T has dynamic "object_pool_index" member of type size_t. 157 158 ***************************************************************************/ 159 160 static if (is (typeof (T.init.object_pool_index) I)) 161 { 162 static assert (!is (typeof (&(T.object_pool_index))), T.stringof ~ ".object_pool_index must be a dynamic member"); 163 164 static assert ( 165 is(I == size_t), 166 T.stringof ~ ".object_pool_index must be size_t, not " ~ I.stringof 167 ); 168 169 // WORKAROUND: because of DMD1 bug placing this condition in static assert 170 // directly causes it to fail even if condition is in fact true. Using 171 // intermediate constant fixes that 172 static immutable _assignable = is(typeof({ T t; t.object_pool_index = 4711; })); 173 static assert ( 174 _assignable, 175 T.stringof ~ ".object_pool_index must be assignable" 176 ); 177 } 178 else static assert (false, "need dynamic \"size_t " ~ T.stringof ~ ".object_pool_index\""); 179 180 /*************************************************************************** 181 182 Pool item instance type alias. 183 184 ***************************************************************************/ 185 186 public alias ItemType_!(T) ItemType; 187 188 /*************************************************************************** 189 190 D2 compiler refuses to resolve `Item` type from IPool implicitly which 191 may be a bug but is easily fixed by introducing explicit alias. 192 193 ***************************************************************************/ 194 195 public alias IPool.Item Item; 196 197 /*************************************************************************** 198 199 List of items (objects) in pool for safe iteration. items is copied into 200 this array on safe iterator instantiation. 201 202 ***************************************************************************/ 203 204 protected Item[] iteration_items; 205 206 /*************************************************************************** 207 208 true if a safe iterator instance exists currently, used for assertions 209 to ensure that only a single safe iterator can exist at a time (as it 210 uses the single buffer, iteration_items, above). 211 212 ***************************************************************************/ 213 214 protected bool safe_iterator_open = false; 215 216 /*************************************************************************** 217 218 Count of unsafe iterator instances which exist currently, used for 219 assertions to ensure that while an unsafe iterator exists the object 220 pool may not be modified. 221 222 ***************************************************************************/ 223 224 protected size_t unsafe_iterators_open = 0; 225 226 /*************************************************************************** 227 228 Takes an idle item from the pool or creates a new one if all item 229 are busy or the pool is empty. 230 231 Params: 232 new_item = expression that creates a new instance of T 233 234 Returns: 235 pool item 236 237 Throws: 238 LimitExceededException if limitation is enabled and all pool 239 items are busy. 240 241 ***************************************************************************/ 242 243 public ItemType get ( lazy ItemType new_item ) 244 out (item) 245 { 246 assert (item !is null); 247 } 248 body 249 { 250 return this.fromItem(super.get_(Item.from(new_item))); 251 } 252 253 /*************************************************************************** 254 255 Ensures that the pool contains at least the specified number of items. 256 Useful to pre-allocate a pool of a certain size. 257 258 Params: 259 num = minimum number of items desired in pool 260 new_item = expression that creates a new instance of T 261 262 Returns: 263 this 264 265 Throws: 266 LimitExceededException if the requested number of items exceeds 267 the previously specified limit. 268 269 ***************************************************************************/ 270 271 public typeof(this) fill ( size_t num, lazy ItemType new_item ) 272 { 273 super.fill_(num, this.toItem(new_item)); 274 return this; 275 } 276 277 /*************************************************************************** 278 279 get() and fill() requests without an expression which returns "new T" 280 exist in the class if type T can be newed without requiring any 281 constructor arguments. This is always the case when T is a struct, and 282 is also the case when T is a class with a constructor with no arguments. 283 284 ***************************************************************************/ 285 286 static if (is (typeof (new T))) 287 { 288 /*********************************************************************** 289 290 Takes an idle item from the pool or creates a new one if all item 291 are busy or the pool is empty. 292 293 Returns: 294 pool item 295 296 Throws: 297 LimitExceededException if limitation is enabled and all pool 298 items are busy. 299 300 ***********************************************************************/ 301 302 public ItemType get ( ) 303 out (item) 304 { 305 assert (item !is null); 306 } 307 body 308 { 309 return this.get(new T); 310 } 311 312 /*********************************************************************** 313 314 Ensures that the pool contains at least the specified number of 315 items. Useful to pre-allocate a pool of a certain size. 316 317 Params: 318 num = minimum number of items desired in pool 319 320 Returns: 321 this 322 323 ***********************************************************************/ 324 325 public typeof(this) fill ( size_t num ) 326 { 327 return this.fill(num, new T); 328 } 329 } 330 331 /*************************************************************************** 332 333 Puts item back to the pool. 334 335 Params: 336 item = item to put back 337 338 Returns: 339 this instance 340 341 ***************************************************************************/ 342 343 public void recycle ( ItemType item ) 344 { 345 super.recycle_(Item.from(item)); 346 } 347 348 /*************************************************************************** 349 350 Minimizes the number of items in the pool, removing idle items in excess 351 of the specified number. Only idle items will be removed, busy items are 352 not affected. The reset() method (if existing) of any removed items is 353 called before they are deleted. 354 355 Params: 356 num = maximum number of items desired in pool 357 358 Returns: 359 this 360 361 ***************************************************************************/ 362 363 public typeof(this) minimize ( size_t num = 0 ) 364 { 365 verify (!this.unsafe_iterators_open, "cannot minimize pool while iterating over items"); 366 367 if ( this.num_idle > num ) 368 { 369 this.truncate(this.num_idle - num); 370 } 371 372 return this; 373 } 374 375 /*************************************************************************** 376 377 Recycles all items in the pool. 378 379 This method is overridden simply in order to verify that an iteration is 380 not in progress. 381 382 Returns: 383 this instance 384 385 ***************************************************************************/ 386 387 override public typeof(this) clear ( ) 388 { 389 verify (!this.unsafe_iterators_open, "cannot clear pool while iterating over items"); 390 391 super.clear(); 392 return this; 393 } 394 395 /*************************************************************************** 396 397 Sets the limit of number of items in pool or disables limitation for 398 limit = unlimited. 399 400 This method is overridden simply in order to verify that an iteration is 401 not in progress. 402 403 Params: 404 limit = new limit of number of items in pool; unlimited disables 405 limitation 406 407 Returns: 408 limit 409 410 Throws: 411 LimitExceededException if the number of busy pool items exceeds 412 the desired limit. 413 414 ***************************************************************************/ 415 416 override public size_t setLimit ( size_t limit ) 417 { 418 verify (!this.safe_iterator_open, "cannot set the limit while iterating over items"); 419 verify (!this.unsafe_iterators_open, "cannot set the limit while iterating over items"); 420 421 return super.setLimit(limit); 422 } 423 424 /*************************************************************************** 425 426 Obtains the n-th pool item. n must be less than the value returned by 427 length(). 428 Caution: The item must not be recycled; while the item is in use, only 429 opIndex(), opApply(), length() and limit() may be called. 430 431 TODO: is this ever used? Seems rather obscure. 432 433 Params: 434 n = item index 435 436 Returns: 437 n-th pool item 438 439 ***************************************************************************/ 440 441 public ItemType opIndex ( size_t n ) 442 /+out (obj) 443 { 444 assert (obj !is null); 445 } 446 body+/ 447 { 448 return this.fromItem(super.opIndex_(n)); 449 } 450 451 /*************************************************************************** 452 453 Takes an idle item from the pool or creates a new one if all item are 454 busy or the pool is empty. 455 456 This method is overridden simply in order to verify that an iteration is 457 not in progress. 458 459 Params: 460 new_item = expression that creates a new Item instance 461 462 Returns: 463 pool item 464 465 Throws: 466 LimitExceededException if limitation is enabled and all pool items 467 are busy 468 469 ***************************************************************************/ 470 471 override protected Item get_ ( lazy Item new_item ) 472 { 473 verify (!this.unsafe_iterators_open, "cannot get from pool while iterating over items"); 474 475 return super.get_(new_item); 476 } 477 478 /*************************************************************************** 479 480 Puts item back to the pool. 481 482 This method is overridden simply in order to verify that an iteration is 483 not in progress. 484 485 Params: 486 item_in = item to put back 487 488 ***************************************************************************/ 489 490 override protected void recycle_ ( Item item_in ) 491 { 492 verify (!this.unsafe_iterators_open, "cannot recycle while iterating over items"); 493 494 super.recycle_(item_in); 495 } 496 497 /*************************************************************************** 498 499 Returns the member of the item union that is used by this instance. 500 501 Params: 502 item = item union instance 503 504 Returns: 505 the member of the item union that is used by this instance. 506 507 ***************************************************************************/ 508 509 protected static ItemType fromItem ( Item item ) 510 { 511 static if (is (ItemType == class)) 512 { 513 return cast (ItemType) item.obj; 514 } 515 else 516 { 517 return cast (ItemType) item.ptr; 518 } 519 } 520 521 /*************************************************************************** 522 523 Sets the member of the item union that is used by this instance. 524 525 Params: 526 item = item to set to an item union instance 527 528 Returns: 529 item union instance with the member set that is used by this 530 instance. 531 532 ***************************************************************************/ 533 534 protected static Item toItem ( ItemType item ) 535 { 536 Item item_out; 537 538 static if (is (ItemType == class)) 539 { 540 item_out.obj = item; 541 } 542 else 543 { 544 item_out.ptr = item; 545 } 546 547 return item_out; 548 } 549 550 /*************************************************************************** 551 552 Sets the object pool index to item. 553 554 Params: 555 item = item to set index 556 n = index to set item to 557 558 ***************************************************************************/ 559 560 protected override void setItemIndex ( Item item, size_t n ) 561 { 562 this.fromItem(item).object_pool_index = n; 563 } 564 565 /*************************************************************************** 566 567 Gets the object pool index of item. 568 569 Params: 570 item = item to get index from 571 572 Returns: 573 object pool index of item. 574 575 ***************************************************************************/ 576 577 protected override size_t getItemIndex ( Item item ) 578 { 579 return this.fromItem(item).object_pool_index; 580 } 581 582 /*************************************************************************** 583 584 Resets item. 585 586 Params: 587 item = item to reset 588 589 ***************************************************************************/ 590 591 abstract protected override void resetItem ( Item item ); 592 593 /*************************************************************************** 594 595 Deletes item and sets it to null. 596 597 Params: 598 item = item to delete 599 600 ***************************************************************************/ 601 602 protected override void deleteItem ( ref Item item ) 603 out 604 { 605 assert (this.isNull(item)); 606 } 607 body 608 { 609 import core.memory; 610 611 static if (is (ItemType == class)) 612 { 613 destroy(item.obj); 614 GC.free(cast(void*) item.obj); 615 item.obj = null; 616 } 617 else 618 { 619 destroy(item.ptr); 620 GC.free(cast(void*) item.ptr); 621 item.ptr = null; 622 } 623 } 624 625 /*************************************************************************** 626 627 Checks a and b for identity. 628 629 Params: 630 a = item to check for being identical to b 631 b = item to check for being identical to a 632 633 Returns: 634 true if a and b are identical or false otherwise. 635 636 ***************************************************************************/ 637 638 protected override bool isSame ( Item a, Item b ) 639 { 640 return this.fromItem(a) is this.fromItem(b); 641 } 642 643 /*************************************************************************** 644 645 Checks if item is null. 646 647 Params: 648 item = item to check for being null 649 650 Returns: 651 true if item is null or false otherwise. 652 653 ***************************************************************************/ 654 655 protected override bool isNull ( Item item ) 656 { 657 return this.fromItem(item) is null; 658 } 659 660 /*************************************************************************** 661 662 Base class for pool 'foreach' iterators. The constructor receives a 663 slice of the items to be iterated over. 664 665 Note that the iterators pass the pool items as type T to the foreach 666 delegate, not as type ItemType. This is because, when ref iterating over 667 a pool of structs, we want the references to be to the structs in the 668 pool themselves, not the pointer to the structs in the pool. 669 670 ***************************************************************************/ 671 672 protected abstract scope class IItemsIterator 673 { 674 protected Item[] iteration_items; 675 676 /*********************************************************************** 677 678 Constructor 679 680 Params: 681 iteration_items = items to be iterated over (sliced) 682 683 ***********************************************************************/ 684 685 protected this ( Item[] iteration_items ) 686 { 687 this.iteration_items = iteration_items; 688 } 689 690 /*********************************************************************** 691 692 'foreach' iteration over items[start .. end] 693 694 ***********************************************************************/ 695 696 public int opApply ( scope int delegate ( ref T item ) dg ) 697 { 698 int ret = 0; 699 700 foreach ( ref item; this.iteration_items ) 701 { 702 static if (is (T == class)) 703 { 704 verify (item.obj !is null); 705 706 T item_out = cast (T) item.obj; 707 708 ret = dg(item_out); 709 } 710 else 711 { 712 verify (item.ptr !is null); 713 714 ret = dg(*cast (T*) item.ptr); 715 } 716 717 if ( ret ) 718 { 719 break; 720 } 721 } 722 723 return ret; 724 } 725 726 /*********************************************************************** 727 728 'foreach' iteration over items[start .. end], with index (0-based) 729 730 ***********************************************************************/ 731 732 public int opApply ( scope int delegate ( ref size_t i, ref T item ) dg ) 733 { 734 int ret = 0; 735 size_t i = 0; 736 737 foreach ( ref item; this ) 738 { 739 ret = dg(i, item); 740 if ( ret ) 741 { 742 break; 743 } 744 i++; 745 } 746 747 return ret; 748 } 749 } 750 751 /*************************************************************************** 752 753 Provides 'foreach' iteration over items[start .. end]. During 754 iteration all methods of PoolCore may be called except limit_(). 755 756 The iteration is actually over a copy of the items in the pool which 757 are specified in the constructor. Thus the pool may be modified 758 while iterating. However, the list of items iterated over is not 759 updated to changes made by get(), clear() and recycle(). 760 761 During iteration all Pool methods may be called except the limit setter. 762 However, as indicated, the list of items iterated over is not updated to 763 changes made by get(), recycle() and clear(). 764 765 ***************************************************************************/ 766 767 protected abstract scope class SafeItemsIterator : IItemsIterator 768 { 769 /*********************************************************************** 770 771 Constructor 772 773 Params: 774 start = start item index 775 end = end item index (excluded like array slice end index) 776 777 In: 778 No instance of this class may exist. 779 780 ***********************************************************************/ 781 782 protected this ( size_t start, size_t end ) 783 { 784 verify (!this.outer.safe_iterator_open); 785 786 this.outer.safe_iterator_open = true; 787 auto slice = this.outer.items[start .. end]; 788 enableStomping(this.outer.iteration_items); 789 this.outer.iteration_items.length = slice.length; 790 slice = (this.outer.iteration_items[] = slice[]); 791 super(slice); 792 } 793 794 /*********************************************************************** 795 796 Destructor 797 798 ***********************************************************************/ 799 800 ~this ( ) 801 { 802 this.outer.safe_iterator_open = false; 803 } 804 } 805 806 /*************************************************************************** 807 808 Provides 'foreach' iteration over items[start .. end]. During 809 iteration only read-only methods of PoolCore may be called. 810 811 The unsafe iterator is more efficient as it does not require the 812 copy of the items being iterated, which the safe iterator performs. 813 814 ***************************************************************************/ 815 816 protected abstract scope class UnsafeItemsIterator : IItemsIterator 817 { 818 /*********************************************************************** 819 820 Constructor 821 822 Params: 823 start = start item index 824 end = end item index (excluded like array slice end index) 825 826 ***********************************************************************/ 827 828 protected this ( size_t start, size_t end ) 829 { 830 this.outer.unsafe_iterators_open++; 831 super(this.outer.items[start .. end]); 832 } 833 834 /*********************************************************************** 835 836 Destructor 837 838 ***********************************************************************/ 839 840 ~this ( ) 841 { 842 this.outer.unsafe_iterators_open--; 843 } 844 } 845 846 /*************************************************************************** 847 848 Iterator classes, each one provides 'foreach' iteration over a subset 849 if the items in the pool: 850 851 - AllItemsIterator iterates over all items in the pool, 852 - BusyItemsIterator iterates over the items that are busy on 853 instantiation, 854 - IdleItemsIteratoriterates over the items that are idle on 855 instantiation. 856 857 ***************************************************************************/ 858 859 /*************************************************************************** 860 861 Provides safe 'foreach' iteration over all items in the pool. 862 863 ***************************************************************************/ 864 865 public scope class AllItemsIterator : SafeItemsIterator 866 { 867 this ( ) 868 { 869 super(0, this.outer.items.length); 870 } 871 } 872 873 /*************************************************************************** 874 875 Provides unsafe 'foreach' iteration over all items in the pool. 876 877 ***************************************************************************/ 878 879 public scope class ReadOnlyAllItemsIterator : UnsafeItemsIterator 880 { 881 this ( ) 882 { 883 super(0, this.outer.items.length); 884 } 885 } 886 887 /*************************************************************************** 888 889 Provides safe 'foreach' iteration over the busy items in the pool. 890 891 ***************************************************************************/ 892 893 public scope class BusyItemsIterator : SafeItemsIterator 894 { 895 this ( ) 896 { 897 super(0, this.outer.num_busy_); 898 } 899 } 900 901 /*************************************************************************** 902 903 Provides unsafe 'foreach' iteration over the busy items in the pool. 904 905 ***************************************************************************/ 906 907 public scope class ReadOnlyBusyItemsIterator : UnsafeItemsIterator 908 { 909 this ( ) 910 { 911 super(0, this.outer.num_busy_); 912 } 913 } 914 915 /*************************************************************************** 916 917 Provides safe 'foreach' iteration over the idle items in the pool. 918 919 ***************************************************************************/ 920 921 public scope class IdleItemsIterator : SafeItemsIterator 922 { 923 this ( ) 924 { 925 super(this.outer.num_busy_, this.outer.items.length); 926 } 927 } 928 929 /*************************************************************************** 930 931 Provides unsafe 'foreach' iteration over the idle items in the pool. 932 933 ***************************************************************************/ 934 935 public scope class ReadOnlyIdleItemsIterator : UnsafeItemsIterator 936 { 937 this ( ) 938 { 939 super(this.outer.num_busy_, this.outer.items.length); 940 } 941 } 942 } 943 944 945 946 version ( UnitTest ) 947 { 948 import ocean.core.Test; 949 950 /*************************************************************************** 951 952 Agrregate pool tester base class. Tests all methods of IAggregatePool. 953 Derived from the free list tester (as an IAggregatePool is an 954 IFreeList). 955 956 Params: 957 T = type of item stored in pool 958 959 ***************************************************************************/ 960 961 abstract class IAggregatePoolTester ( T ) : FreeListTester!(ItemType_!(T)) 962 { 963 /*********************************************************************** 964 965 Pool being tested. 966 967 ***********************************************************************/ 968 969 private alias IAggregatePool!(T) Pool; 970 971 private Pool pool; 972 973 974 /*********************************************************************** 975 976 Constructor. 977 978 Params: 979 pool = pool to test 980 981 ***********************************************************************/ 982 983 public this ( Pool pool ) 984 { 985 super(pool); 986 this.pool = pool; 987 } 988 989 990 /*********************************************************************** 991 992 Unittest for internal pool. Runs the IFreeList test, then a series 993 of additional tests to check the features of the IAggregatePool. 994 995 ***********************************************************************/ 996 997 override public void test ( ) 998 { 999 // Test IFreeList features 1000 super.test(); 1001 1002 this.pool.clear(); 1003 this.pool.minimize(); 1004 1005 // Test ILimitable features 1006 this.limitTest(); 1007 1008 this.pool.clear(); 1009 this.pool.minimize(); 1010 1011 // Test iterators 1012 this.iteratorTest(); 1013 } 1014 1015 1016 /*********************************************************************** 1017 1018 Checks that the contents of the pool match the expected values. 1019 1020 Params: 1021 expected_busy = expected number of busy items 1022 expected_idle = expected number of idle items 1023 1024 ***********************************************************************/ 1025 1026 override protected void lengthCheck ( size_t expected_busy, 1027 size_t expected_idle ) 1028 { 1029 .test!("==")(this.pool.num_busy, expected_busy, "AggregatePool busy items wrong"); 1030 .test!("==")(this.pool.length, expected_busy + expected_idle, "AggregatePool length was wrong"); 1031 1032 super.lengthCheck(expected_busy, expected_idle); 1033 } 1034 1035 1036 /*********************************************************************** 1037 1038 Tests the limit features of IAggregatePool. 1039 1040 ***********************************************************************/ 1041 1042 private void limitTest ( ) 1043 { 1044 // Check that initially not limited 1045 this.limitCheck(false); 1046 1047 // Set limit 1048 this.pool.setLimit(this.num_items); 1049 this.limitCheck(true, this.num_items); 1050 1051 // Get items up to limit 1052 size_t busy_count, idle_count; 1053 for ( int i; i < this.num_items; i++ ) 1054 { 1055 this.pool.get(this.newItem()); 1056 this.lengthCheck(++busy_count, idle_count); 1057 } 1058 .test!("==")(idle_count, 0, "idle count mismatch"); 1059 1060 // Check that getting another item is prevented 1061 bool get_prevented; 1062 try 1063 { 1064 this.pool.get(this.newItem()); 1065 } 1066 catch ( LimitExceededException e ) 1067 { 1068 get_prevented = true; 1069 } 1070 .test(get_prevented, "AggregatePool limitation failed"); 1071 this.lengthCheck(busy_count, idle_count); 1072 1073 // Recycle all items (clear) 1074 this.pool.clear(); 1075 idle_count = busy_count; 1076 busy_count = 0; 1077 this.lengthCheck(busy_count, idle_count); 1078 1079 // Reduce limit 1080 this.pool.setLimit(this.num_items / 2); 1081 idle_count = this.num_items / 2; 1082 this.limitCheck(true, this.num_items / 2); 1083 this.lengthCheck(busy_count, idle_count); 1084 1085 // Remove limit 1086 this.pool.setLimit(this.pool.unlimited); 1087 this.limitCheck(false); 1088 1089 // Get items beyond old limit 1090 for ( int i; i < this.num_items * 2; i++ ) 1091 { 1092 auto item = this.pool.get(this.newItem()); 1093 this.setItem(item, i); 1094 if ( idle_count ) idle_count--; 1095 this.lengthCheck(++busy_count, idle_count); 1096 } 1097 .test!("==")(idle_count, 0, "idle count mismatch"); 1098 } 1099 1100 1101 /*********************************************************************** 1102 1103 Tests the iteration features of IAggregatePool. 1104 1105 ***********************************************************************/ 1106 1107 private void iteratorTest ( ) 1108 { 1109 // Get some items 1110 size_t busy_count, idle_count; 1111 for ( int i; i < this.num_items; i++ ) 1112 { 1113 auto item = this.pool.get(this.newItem()); 1114 this.setItem(item, i); 1115 this.lengthCheck(++busy_count, idle_count); 1116 } 1117 .test!("==")(idle_count, 0, "idle count mismatch"); 1118 1119 // Check safe busy items iterator 1120 { 1121 size_t count; 1122 scope it = this.pool..new BusyItemsIterator; 1123 foreach ( i, ref item; it ) 1124 { 1125 this.checkIteratorItem(item, i); 1126 count++; 1127 } 1128 .test!("==")(count, this.pool.num_busy, "iterator count wrong"); 1129 } 1130 1131 // Check read-only busy items iterator 1132 { 1133 size_t count; 1134 scope it = this.pool..new ReadOnlyBusyItemsIterator; 1135 foreach ( i, ref item; it ) 1136 { 1137 this.checkIteratorItem(item, i); 1138 count++; 1139 } 1140 .test!("==")(count, this.pool.num_busy, "iterator count wrong"); 1141 } 1142 1143 { 1144 // Recycle the second half of the items 1145 // Note that after recycling, the order of the items in the 1146 // second half of the pool are expected to be reversed. 1147 size_t count; 1148 scope it = this.pool..new BusyItemsIterator; 1149 foreach ( i, ref item; it ) 1150 { 1151 if ( i >= this.num_items / 2 ) 1152 { 1153 static if ( is(T == class) ) 1154 { 1155 this.pool.recycle(item); 1156 } 1157 else 1158 { 1159 static assert(is(T == struct)); 1160 auto item_p = &item; 1161 this.pool.recycle(item_p); 1162 } 1163 } 1164 count++; 1165 } 1166 .test!("==")(count, this.pool.length, "iterator count wrong"); 1167 } 1168 1169 // Check safe idle items iterator 1170 { 1171 size_t count; 1172 scope it = this.pool..new IdleItemsIterator; 1173 foreach ( i, ref item; it ) 1174 { 1175 this.checkIteratorItem(item, this.num_items - (i + 1)); 1176 count++; 1177 } 1178 .test!("==")(count, this.pool.num_idle, "iterator count wrong"); 1179 } 1180 1181 // Check read-only idle items iterator 1182 { 1183 size_t count; 1184 scope it = this.pool..new ReadOnlyIdleItemsIterator; 1185 foreach ( i, ref item; it ) 1186 { 1187 this.checkIteratorItem(item, this.num_items - (i + 1)); 1188 count++; 1189 } 1190 .test!("==")(count, this.pool.num_idle, "iterator count wrong"); 1191 } 1192 1193 // Check safe all items iterator 1194 { 1195 size_t count; 1196 scope it = this.pool..new AllItemsIterator; 1197 foreach ( i, ref item; it ) 1198 { 1199 auto num = i >= this.num_items / 2 1200 ? this.num_items - ((i - this.num_items / 2) + 1) 1201 : i; 1202 this.checkIteratorItem(item, num); 1203 count++; 1204 } 1205 .test!("==")(count, this.pool.length, "iterator count wrong"); 1206 } 1207 1208 // Check read-only all items iterator 1209 { 1210 size_t count; 1211 scope it = this.pool..new ReadOnlyAllItemsIterator; 1212 foreach ( i, ref item; it ) 1213 { 1214 auto num = i >= this.num_items / 2 1215 ? this.num_items - ((i - this.num_items / 2) + 1) 1216 : i; 1217 this.checkIteratorItem(item, num); 1218 count++; 1219 } 1220 .test!("==")(count, this.pool.length, "iterator count wrong"); 1221 } 1222 1223 // TODO: the iterator checks could be expanded to also check that 1224 // it's not possible to write while performing a read-only iteration 1225 } 1226 1227 1228 /*********************************************************************** 1229 1230 Checks the limitation state of the pool is as expected. 1231 1232 Params: 1233 limited = whether the pool is expected to be limited or not 1234 limit = expected limit (if limited is false, this value is 1235 ignored) 1236 1237 ***********************************************************************/ 1238 1239 private void limitCheck ( bool limited, size_t limit = 0 ) 1240 { 1241 .test!("==")(this.pool.is_limited, limited, "AggregatePool limit flag wrong"); 1242 .test!("==")(this.pool.limit, (limited ? limit : this.pool.unlimited), 1243 "AggregatePool limit wrong"); 1244 } 1245 1246 1247 /*********************************************************************** 1248 1249 Checks the value of the passed item (from an iterator) against the 1250 value which can be deterministically derived from the passed 1251 integer. The method should throw TestException on failure. 1252 1253 Params: 1254 item = iterated item to check value of 1255 i = integer to determine contents of item 1256 1257 ***********************************************************************/ 1258 1259 private void checkIteratorItem ( ref T item, size_t i ) 1260 { 1261 static if ( is(T == class) ) 1262 { 1263 this.checkItem(item, i); 1264 } 1265 else 1266 { 1267 static assert(is(T == struct)); 1268 auto item_p = &item; 1269 this.checkItem(item_p, i); 1270 } 1271 } 1272 } 1273 } 1274