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 }