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