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 moduleocean.util.container.pool.model.IPool;
38 39 40 41 42 importocean.transition;
43 44 importocean.util.container.pool.model.IPoolInfo;
45 importocean.util.container.pool.model.ILimitable;
46 47 importocean.core.Array: copy;
48 importocean.core.Exception;
49 50 51 52 /*******************************************************************************
53 54 Core pool implementation.
55 56 *******************************************************************************/57 58 publicabstractclassIPool : IPoolInfo, ILimitable59 {
60 importocean.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 protectedunionItem70 {
71 /***********************************************************************
72 73 Object to store class instances in the pool
74 75 ***********************************************************************/76 77 Objectobj;
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 statictypeof (this) from ( Objectobj )
97 {
98 typeof (this) item;
99 100 item.obj = obj;
101 102 returnitem;
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 statictypeof (this) from ( void* ptr )
115 {
116 typeof (this) item;
117 118 item.ptr = ptr;
119 120 returnitem;
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 privatesize_tlimit_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 publicboollimited = false;
141 142 /***************************************************************************
143 144 List of items (objects) in pool, busy items first
145 146 ***************************************************************************/147 148 protectedItem[] items;
149 150 /***************************************************************************
151 152 Number of busy items in pool
153 154 ***************************************************************************/155 156 protectedsize_tnum_busy_ = 0;
157 158 /***************************************************************************
159 160 Reused exception instance
161 162 ***************************************************************************/163 164 privateLimitExceededExceptionlimit_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 publicthis ( )
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 overridepublicsize_tlength ( )
194 {
195 returnthis.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 overridepublicsize_tnum_busy ( )
208 {
209 returnthis.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 overridepublicsize_tnum_idle ( )
222 {
223 returnthis.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 overridepublicsize_tlimit ( )
237 {
238 returnthis.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 overridepublicboolis_limited ( )
249 {
250 returnthis.limited;
251 }
252 253 /***************************************************************************
254 255 Recycles all items in the pool.
256 257 Returns:
258 this instance
259 260 ***************************************************************************/261 262 publictypeof(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 returnthis;
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 overridepublicsize_tsetLimit ( size_tlimit )
294 out295 {
296 debug (ObjectPoolConsistencyCheck) foreach (item; this.items)
297 {
298 assert (item.ptr !isnull);
299 }
300 }
301 body302 {
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 returnlimit;
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 publicboolisBusy ( Itemitem )
335 {
336 returnthis.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 protectedtypeof(this) fill_ ( size_tnum, lazyItemnew_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 autoold_len = this.items.length;
366 this.items.length = num;
367 enableStomping(this.items);
368 369 foreach ( refitem; this.items[old_len .. $] )
370 {
371 item = new_item();
372 verify (!this.isNull(item));
373 }
374 }
375 376 returnthis;
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 protectedItemget_ ( lazyItemnew_item )
397 out (_item_out)
398 {
399 autoitem_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 body422 {
423 Itemitem;
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 else432 {
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 returnitem;
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 protectedItemopIndex_ ( size_tn )
465 {
466 returnthis.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 protectedvoidrecycle_ ( Itemitem_in )
479 {
480 verify (this.num_busy_ > 0, "nothing is busy so there is nothing to recycle");
481 482 size_tindex = 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 abstractprotectedvoidsetItemIndex ( Itemitem, size_tn );
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 abstractprotectedsize_tgetItemIndex ( Itemitem );
530 531 /***************************************************************************
532 533 Resets item.
534 535 Params:
536 item = item to reset
537 538 ***************************************************************************/539 540 abstractprotectedvoidresetItem ( Itemitem );
541 542 /***************************************************************************
543 544 Deletes item and sets it to null.
545 546 Params:
547 item = item to delete
548 549 ***************************************************************************/550 551 abstractprotectedvoiddeleteItem ( refItemitem );
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 abstractprotectedboolisSame ( Itema, Itemb );
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 abstractprotectedboolisNull ( Itemitem );
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 protectedvoidtruncate ( size_tremove )
593 {
594 verify(remove <= this.num_idle);
595 596 foreach ( refitem; 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 enableStomping(this.items);
604 }
605 }
606 607 /*******************************************************************************
608 609 LimitExceededException class
610 611 *******************************************************************************/612 613 publicclassLimitExceededException : Exception614 {
615 mixinReusableExceptionImplementation!();
616 617 /***************************************************************************
618 619 Limit which was exceeded when this instance has been thrown
620 621 ***************************************************************************/622 623 size_tlimit;
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 voidcheck ( IPoolpool, boolok, lazycstringmsg, istringfile, longline )
642 {
643 this.limit = pool.items.length;
644 this.enforce(ok, msg, file, line);
645 }
646 }