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