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