1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 module hunt.pool.impl.BaseGenericObjectPool; 18 19 import hunt.pool.impl.BaseObjectPoolConfig; 20 import hunt.pool.impl.EvictionPolicy; 21 import hunt.pool.impl.EvictionTimer; 22 23 import hunt.pool.impl.GenericKeyedObjectPoolConfig; 24 25 // import java.io.PrintWriter; 26 // import java.io.StringWriter; 27 // import java.io.Writer; 28 // import java.lang.management.ManagementFactory; 29 // import java.lang.ref.WeakReference; 30 // import java.lang.reflect.InvocationTargetException; 31 // import java.util.Arrays; 32 // import java.util.Deque; 33 // import java.util.Iterator; 34 // import java.util.TimerTask; 35 import hunt.concurrency.Delayed; 36 import hunt.concurrency.Future; 37 import hunt.concurrency.atomic.AtomicHelper; 38 // import java.util.concurrent.TimeUnit; 39 // import java.util.concurrent.atomic.AtomicLong; 40 41 // import javax.management.InstanceAlreadyExistsException; 42 // import javax.management.InstanceNotFoundException; 43 // import javax.management.MBeanRegistrationException; 44 // import javax.management.MBeanServer; 45 // import javax.management.MalformedObjectNameException; 46 // import javax.management.NotCompliantMBeanException; 47 // import javax.management.ObjectName; 48 49 import hunt.pool.BaseObject; 50 import hunt.pool.PooledObject; 51 import hunt.pool.PooledObjectState; 52 import hunt.pool.SwallowedExceptionListener; 53 54 import hunt.collection; 55 import hunt.Exceptions; 56 import hunt.logging.ConsoleLogger; 57 import hunt.util.StringBuilder; 58 import hunt.util.Common; 59 import hunt.util.Runnable; 60 61 import core.time; 62 import std.conv; 63 import std.range; 64 65 /** 66 * Base class that provides common functionality for {@link GenericObjectPool} 67 * and {@link GenericKeyedObjectPool}. The primary reason this class exists is 68 * reduce code duplication between the two pool implementations. 69 * 70 * @param <T> Type of element pooled in this pool. 71 * 72 * This class is intended to be thread-safe. 73 * 74 */ 75 abstract class BaseGenericObjectPool : BaseObject { 76 77 // Constants 78 /** 79 * The size of the caches used to store historical data for some attributes 80 * so that rolling means may be calculated. 81 */ 82 enum int MEAN_TIMING_STATS_CACHE_SIZE = 100; 83 84 private enum string EVICTION_POLICY_TYPE_NAME = EvictionPolicy.stringof; 85 86 // Configuration attributes 87 private shared int maxTotal = 88 GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL; 89 private shared bool blockWhenExhausted = 90 BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED; 91 private shared long maxWaitMillis = 92 BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS; 93 private shared bool lifo = BaseObjectPoolConfig.DEFAULT_LIFO; 94 private bool fairness; 95 private shared bool testOnCreate = 96 BaseObjectPoolConfig.DEFAULT_TEST_ON_CREATE; 97 private shared bool testOnBorrow = 98 BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW; 99 private shared bool testOnReturn = 100 BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN; 101 private shared bool testWhileIdle = 102 BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE; 103 private shared long timeBetweenEvictionRunsMillis = 104 BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS; 105 private shared int numTestsPerEvictionRun = 106 BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN; 107 private shared long minEvictableIdleTimeMillis = 108 BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS; 109 private shared long softMinEvictableIdleTimeMillis = 110 BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS; 111 private EvictionPolicy evictionPolicy; 112 private shared long evictorShutdownTimeoutMillis = 113 BaseObjectPoolConfig.DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS; 114 115 116 // Internal (primarily state) attributes 117 Object closeLock; 118 shared bool closed = false; 119 Object evictionLock; 120 private Evictor evictor = null; // @GuardedBy("evictionLock") 121 EvictionIterator evictionIterator = null; // @GuardedBy("evictionLock") 122 /* 123 * Class loader for evictor thread to use since, in a JavaEE or similar 124 * environment, the context class loader for the evictor thread may not have 125 * visibility of the correct factory. See POOL-161. Uses a weak reference to 126 * avoid potential memory leaks if the Pool is discarded rather than closed. 127 */ 128 // private WeakReference!(ClassLoader) factoryClassLoader; 129 130 131 // Monitoring (primarily JMX) attributes 132 // private ObjectName objectName; 133 private string creationStackTrace; 134 private shared long borrowedCount = 0; // new AtomicLong(0); 135 private shared long returnedCount = 0; // new AtomicLong(0); 136 shared long createdCount = 0; // new AtomicLong(0); 137 shared long destroyedCount = 0; // new AtomicLong(0); 138 shared long destroyedByEvictorCount = 0; // new AtomicLong(0); 139 shared long destroyedByBorrowValidationCount = 0; // new AtomicLong(0); 140 private StatsStore activeTimes; 141 private StatsStore idleTimes; 142 private StatsStore waitTimes; 143 private shared long maxBorrowWaitTimeMillis = 0; // new AtomicLong(0L); 144 private SwallowedExceptionListener swallowedExceptionListener = null; 145 146 147 /** 148 * Handles JMX registration (if required) and the initialization required for 149 * monitoring. 150 * 151 * @param config Pool configuration 152 * @param jmxNameBase The default base JMX name for the new pool unless 153 * overridden by the config 154 * @param jmxNamePrefix Prefix to be used for JMX name for the new pool 155 */ 156 this(BaseObjectPoolConfig config, 157 string jmxNameBase, string jmxNamePrefix) { 158 159 evictionLock = new Object(); 160 closeLock = new Object(); 161 activeTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE); 162 idleTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE); 163 waitTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE); 164 165 166 // if (config.getJmxEnabled()) { 167 // this.objectName = jmxRegister(config, jmxNameBase, jmxNamePrefix); 168 // } else { 169 // this.objectName = null; 170 // } 171 172 // Populate the creation stack trace 173 this.creationStackTrace = getStackTrace(new Exception("default")); 174 175 // save the current TCCL (if any) to be used later by the evictor Thread 176 // ClassLoader cl = Thread.getThis().getContextClassLoader(); 177 // if (cl is null) { 178 // factoryClassLoader = null; 179 // } else { 180 // factoryClassLoader = new WeakReference<>(cl); 181 // } 182 // implementationMissing(false); 183 // FIXME: Needing refactor or cleanup -@zxp at 7/11/2019, 9:53:27 AM 184 // 185 186 fairness = config.getFairness(); 187 } 188 189 190 /** 191 * Returns the maximum number of objects that can be allocated by the pool 192 * (checked out to clients, or idle awaiting checkout) at a given time. When 193 * negative, there is no limit to the number of objects that can be 194 * managed by the pool at one time. 195 * 196 * @return the cap on the total number of object instances managed by the 197 * pool. 198 * 199 * @see #setMaxTotal 200 */ 201 int getMaxTotal() { 202 return maxTotal; 203 } 204 205 /** 206 * Sets the cap on the number of objects that can be allocated by the pool 207 * (checked out to clients, or idle awaiting checkout) at a given time. Use 208 * a negative value for no limit. 209 * 210 * @param maxTotal The cap on the total number of object instances managed 211 * by the pool. Negative values mean that there is no limit 212 * to the number of objects allocated by the pool. 213 * 214 * @see #getMaxTotal 215 */ 216 void setMaxTotal(int maxTotal) { 217 this.maxTotal = maxTotal; 218 } 219 220 /** 221 * Returns whether to block when the <code>borrowObject()</code> method is 222 * invoked when the pool is exhausted (the maximum number of "active" 223 * objects has been reached). 224 * 225 * @return <code>true</code> if <code>borrowObject()</code> should block 226 * when the pool is exhausted 227 * 228 * @see #setBlockWhenExhausted 229 */ 230 bool getBlockWhenExhausted() { 231 return blockWhenExhausted; 232 } 233 234 /** 235 * Sets whether to block when the <code>borrowObject()</code> method is 236 * invoked when the pool is exhausted (the maximum number of "active" 237 * objects has been reached). 238 * 239 * @param blockWhenExhausted <code>true</code> if 240 * <code>borrowObject()</code> should block 241 * when the pool is exhausted 242 * 243 * @see #getBlockWhenExhausted 244 */ 245 void setBlockWhenExhausted(bool blockWhenExhausted) { 246 this.blockWhenExhausted = blockWhenExhausted; 247 } 248 249 protected void setConfig(BaseObjectPoolConfig conf) { 250 setLifo(conf.getLifo()); 251 setMaxWaitMillis(conf.getMaxWaitMillis()); 252 setBlockWhenExhausted(conf.getBlockWhenExhausted()); 253 setTestOnCreate(conf.getTestOnCreate()); 254 setTestOnBorrow(conf.getTestOnBorrow()); 255 setTestOnReturn(conf.getTestOnReturn()); 256 setTestWhileIdle(conf.getTestWhileIdle()); 257 setNumTestsPerEvictionRun(conf.getNumTestsPerEvictionRun()); 258 setMinEvictableIdleTimeMillis(conf.getMinEvictableIdleTimeMillis()); 259 setTimeBetweenEvictionRunsMillis(conf.getTimeBetweenEvictionRunsMillis()); 260 setSoftMinEvictableIdleTimeMillis(conf.getSoftMinEvictableIdleTimeMillis()); 261 // TODO: Tasks pending completion -@zhangxueping at 2020-04-15T10:52:55+08:00 262 // 263 // EvictionPolicy policy = conf.getEvictionPolicy(); 264 // if(policy is null) { 265 // warning("policy is null"); 266 // } else { 267 // setEvictionPolicy(policy); 268 // } 269 setEvictorShutdownTimeoutMillis(conf.getEvictorShutdownTimeoutMillis()); 270 } 271 272 /** 273 * Returns the maximum amount of time (in milliseconds) the 274 * <code>borrowObject()</code> method should block before throwing an 275 * exception when the pool is exhausted and 276 * {@link #getBlockWhenExhausted} is true. When less than 0, the 277 * <code>borrowObject()</code> method may block indefinitely. 278 * 279 * @return the maximum number of milliseconds <code>borrowObject()</code> 280 * will block. 281 * 282 * @see #setMaxWaitMillis 283 * @see #setBlockWhenExhausted 284 */ 285 long getMaxWaitMillis() { 286 return maxWaitMillis; 287 } 288 289 /** 290 * Sets the maximum amount of time (in milliseconds) the 291 * <code>borrowObject()</code> method should block before throwing an 292 * exception when the pool is exhausted and 293 * {@link #getBlockWhenExhausted} is true. When less than 0, the 294 * <code>borrowObject()</code> method may block indefinitely. 295 * 296 * @param maxWaitMillis the maximum number of milliseconds 297 * <code>borrowObject()</code> will block or negative 298 * for indefinitely. 299 * 300 * @see #getMaxWaitMillis 301 * @see #setBlockWhenExhausted 302 */ 303 void setMaxWaitMillis(long maxWaitMillis) { 304 this.maxWaitMillis = maxWaitMillis; 305 } 306 307 /** 308 * Returns whether the pool has LIFO (last in, first out) behaviour with 309 * respect to idle objects - always returning the most recently used object 310 * from the pool, or as a FIFO (first in, first out) queue, where the pool 311 * always returns the oldest object in the idle object pool. 312 * 313 * @return <code>true</code> if the pool is configured with LIFO behaviour 314 * or <code>false</code> if the pool is configured with FIFO 315 * behaviour 316 * 317 * @see #setLifo 318 */ 319 bool getLifo() { 320 return lifo; 321 } 322 323 /** 324 * Returns whether or not the pool serves threads waiting to borrow objects fairly. 325 * True means that waiting threads are served as if waiting in a FIFO queue. 326 * 327 * @return <code>true</code> if waiting threads are to be served 328 * by the pool in arrival order 329 */ 330 bool getFairness() { 331 return fairness; 332 } 333 334 /** 335 * Sets whether the pool has LIFO (last in, first out) behaviour with 336 * respect to idle objects - always returning the most recently used object 337 * from the pool, or as a FIFO (first in, first out) queue, where the pool 338 * always returns the oldest object in the idle object pool. 339 * 340 * @param lifo <code>true</code> if the pool is to be configured with LIFO 341 * behaviour or <code>false</code> if the pool is to be 342 * configured with FIFO behaviour 343 * 344 * @see #getLifo() 345 */ 346 void setLifo(bool lifo) { 347 this.lifo = lifo; 348 } 349 350 /** 351 * Returns whether objects created for the pool will be validated before 352 * being returned from the <code>borrowObject()</code> method. Validation is 353 * performed by the <code>validateObject()</code> method of the factory 354 * associated with the pool. If the object fails to validate, then 355 * <code>borrowObject()</code> will fail. 356 * 357 * @return <code>true</code> if newly created objects are validated before 358 * being returned from the <code>borrowObject()</code> method 359 * 360 * @see #setTestOnCreate 361 * 362 */ 363 bool getTestOnCreate() { 364 return testOnCreate; 365 } 366 367 /** 368 * Sets whether objects created for the pool will be validated before 369 * being returned from the <code>borrowObject()</code> method. Validation is 370 * performed by the <code>validateObject()</code> method of the factory 371 * associated with the pool. If the object fails to validate, then 372 * <code>borrowObject()</code> will fail. 373 * 374 * @param testOnCreate <code>true</code> if newly created objects should be 375 * validated before being returned from the 376 * <code>borrowObject()</code> method 377 * 378 * @see #getTestOnCreate 379 * 380 */ 381 void setTestOnCreate(bool testOnCreate) { 382 this.testOnCreate = testOnCreate; 383 } 384 385 /** 386 * Returns whether objects borrowed from the pool will be validated before 387 * being returned from the <code>borrowObject()</code> method. Validation is 388 * performed by the <code>validateObject()</code> method of the factory 389 * associated with the pool. If the object fails to validate, it will be 390 * removed from the pool and destroyed, and a new attempt will be made to 391 * borrow an object from the pool. 392 * 393 * @return <code>true</code> if objects are validated before being returned 394 * from the <code>borrowObject()</code> method 395 * 396 * @see #setTestOnBorrow 397 */ 398 bool getTestOnBorrow() { 399 return testOnBorrow; 400 } 401 402 /** 403 * Sets whether objects borrowed from the pool will be validated before 404 * being returned from the <code>borrowObject()</code> method. Validation is 405 * performed by the <code>validateObject()</code> method of the factory 406 * associated with the pool. If the object fails to validate, it will be 407 * removed from the pool and destroyed, and a new attempt will be made to 408 * borrow an object from the pool. 409 * 410 * @param testOnBorrow <code>true</code> if objects should be validated 411 * before being returned from the 412 * <code>borrowObject()</code> method 413 * 414 * @see #getTestOnBorrow 415 */ 416 void setTestOnBorrow(bool testOnBorrow) { 417 this.testOnBorrow = testOnBorrow; 418 } 419 420 /** 421 * Returns whether objects borrowed from the pool will be validated when 422 * they are returned to the pool via the <code>returnObject()</code> method. 423 * Validation is performed by the <code>validateObject()</code> method of 424 * the factory associated with the pool. Returning objects that fail validation 425 * are destroyed rather then being returned the pool. 426 * 427 * @return <code>true</code> if objects are validated on return to 428 * the pool via the <code>returnObject()</code> method 429 * 430 * @see #setTestOnReturn 431 */ 432 bool getTestOnReturn() { 433 return testOnReturn; 434 } 435 436 /** 437 * Sets whether objects borrowed from the pool will be validated when 438 * they are returned to the pool via the <code>returnObject()</code> method. 439 * Validation is performed by the <code>validateObject()</code> method of 440 * the factory associated with the pool. Returning objects that fail validation 441 * are destroyed rather then being returned the pool. 442 * 443 * @param testOnReturn <code>true</code> if objects are validated on 444 * return to the pool via the 445 * <code>returnObject()</code> method 446 * 447 * @see #getTestOnReturn 448 */ 449 void setTestOnReturn(bool testOnReturn) { 450 this.testOnReturn = testOnReturn; 451 } 452 453 /** 454 * Returns whether objects sitting idle in the pool will be validated by the 455 * idle object evictor (if any - see 456 * {@link #setTimeBetweenEvictionRunsMillis(long)}). Validation is performed 457 * by the <code>validateObject()</code> method of the factory associated 458 * with the pool. If the object fails to validate, it will be removed from 459 * the pool and destroyed. 460 * 461 * @return <code>true</code> if objects will be validated by the evictor 462 * 463 * @see #setTestWhileIdle 464 * @see #setTimeBetweenEvictionRunsMillis 465 */ 466 bool getTestWhileIdle() { 467 return testWhileIdle; 468 } 469 470 /** 471 * Returns whether objects sitting idle in the pool will be validated by the 472 * idle object evictor (if any - see 473 * {@link #setTimeBetweenEvictionRunsMillis(long)}). Validation is performed 474 * by the <code>validateObject()</code> method of the factory associated 475 * with the pool. If the object fails to validate, it will be removed from 476 * the pool and destroyed. Note that setting this property has no effect 477 * unless the idle object evictor is enabled by setting 478 * <code>timeBetweenEvictionRunsMillis</code> to a positive value. 479 * 480 * @param testWhileIdle 481 * <code>true</code> so objects will be validated by the evictor 482 * 483 * @see #getTestWhileIdle 484 * @see #setTimeBetweenEvictionRunsMillis 485 */ 486 void setTestWhileIdle(bool testWhileIdle) { 487 this.testWhileIdle = testWhileIdle; 488 } 489 490 /** 491 * Returns the number of milliseconds to sleep between runs of the idle 492 * object evictor thread. When non-positive, no idle object evictor thread 493 * will be run. 494 * 495 * @return number of milliseconds to sleep between evictor runs 496 * 497 * @see #setTimeBetweenEvictionRunsMillis 498 */ 499 long getTimeBetweenEvictionRunsMillis() { 500 return timeBetweenEvictionRunsMillis; 501 } 502 503 /** 504 * Sets the number of milliseconds to sleep between runs of the idle object evictor thread. 505 * <ul> 506 * <li>When positive, the idle object evictor thread starts.</li> 507 * <li>When non-positive, no idle object evictor thread runs.</li> 508 * </ul> 509 * 510 * @param timeBetweenEvictionRunsMillis 511 * number of milliseconds to sleep between evictor runs 512 * 513 * @see #getTimeBetweenEvictionRunsMillis 514 */ 515 void setTimeBetweenEvictionRunsMillis( 516 long timeBetweenEvictionRunsMillis) { 517 this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; 518 startEvictor(timeBetweenEvictionRunsMillis); 519 } 520 521 /** 522 * Returns the maximum number of objects to examine during each run (if any) 523 * of the idle object evictor thread. When positive, the number of tests 524 * performed for a run will be the minimum of the configured value and the 525 * number of idle instances in the pool. When negative, the number of tests 526 * performed will be <code>ceil({@link #getNumIdle}/ 527 * abs({@link #getNumTestsPerEvictionRun}))</code> which means that when the 528 * value is <code>-n</code> roughly one nth of the idle objects will be 529 * tested per run. 530 * 531 * @return max number of objects to examine during each evictor run 532 * 533 * @see #setNumTestsPerEvictionRun 534 * @see #setTimeBetweenEvictionRunsMillis 535 */ 536 int getNumTestsPerEvictionRun() { 537 return numTestsPerEvictionRun; 538 } 539 540 /** 541 * Sets the maximum number of objects to examine during each run (if any) 542 * of the idle object evictor thread. When positive, the number of tests 543 * performed for a run will be the minimum of the configured value and the 544 * number of idle instances in the pool. When negative, the number of tests 545 * performed will be <code>ceil({@link #getNumIdle}/ 546 * abs({@link #getNumTestsPerEvictionRun}))</code> which means that when the 547 * value is <code>-n</code> roughly one nth of the idle objects will be 548 * tested per run. 549 * 550 * @param numTestsPerEvictionRun 551 * max number of objects to examine during each evictor run 552 * 553 * @see #getNumTestsPerEvictionRun 554 * @see #setTimeBetweenEvictionRunsMillis 555 */ 556 void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) { 557 this.numTestsPerEvictionRun = numTestsPerEvictionRun; 558 } 559 560 /** 561 * Returns the minimum amount of time an object may sit idle in the pool 562 * before it is eligible for eviction by the idle object evictor (if any - 563 * see {@link #setTimeBetweenEvictionRunsMillis(long)}). When non-positive, 564 * no objects will be evicted from the pool due to idle time alone. 565 * 566 * @return minimum amount of time an object may sit idle in the pool before 567 * it is eligible for eviction 568 * 569 * @see #setMinEvictableIdleTimeMillis 570 * @see #setTimeBetweenEvictionRunsMillis 571 */ 572 long getMinEvictableIdleTimeMillis() { 573 return minEvictableIdleTimeMillis; 574 } 575 576 /** 577 * Sets the minimum amount of time an object may sit idle in the pool 578 * before it is eligible for eviction by the idle object evictor (if any - 579 * see {@link #setTimeBetweenEvictionRunsMillis(long)}). When non-positive, 580 * no objects will be evicted from the pool due to idle time alone. 581 * 582 * @param minEvictableIdleTimeMillis 583 * minimum amount of time an object may sit idle in the pool 584 * before it is eligible for eviction 585 * 586 * @see #getMinEvictableIdleTimeMillis 587 * @see #setTimeBetweenEvictionRunsMillis 588 */ 589 void setMinEvictableIdleTimeMillis( 590 long minEvictableIdleTimeMillis) { 591 this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; 592 } 593 594 /** 595 * Returns the minimum amount of time an object may sit idle in the pool 596 * before it is eligible for eviction by the idle object evictor (if any - 597 * see {@link #setTimeBetweenEvictionRunsMillis(long)}), 598 * with the extra condition that at least <code>minIdle</code> object 599 * instances remain in the pool. This setting is overridden by 600 * {@link #getMinEvictableIdleTimeMillis} (that is, if 601 * {@link #getMinEvictableIdleTimeMillis} is positive, then 602 * {@link #getSoftMinEvictableIdleTimeMillis} is ignored). 603 * 604 * @return minimum amount of time an object may sit idle in the pool before 605 * it is eligible for eviction if minIdle instances are available 606 * 607 * @see #setSoftMinEvictableIdleTimeMillis 608 */ 609 long getSoftMinEvictableIdleTimeMillis() { 610 return softMinEvictableIdleTimeMillis; 611 } 612 613 /** 614 * Sets the minimum amount of time an object may sit idle in the pool 615 * before it is eligible for eviction by the idle object evictor (if any - 616 * see {@link #setTimeBetweenEvictionRunsMillis(long)}), 617 * with the extra condition that at least <code>minIdle</code> object 618 * instances remain in the pool. This setting is overridden by 619 * {@link #getMinEvictableIdleTimeMillis} (that is, if 620 * {@link #getMinEvictableIdleTimeMillis} is positive, then 621 * {@link #getSoftMinEvictableIdleTimeMillis} is ignored). 622 * 623 * @param softMinEvictableIdleTimeMillis 624 * minimum amount of time an object may sit idle in the pool 625 * before it is eligible for eviction if minIdle instances are 626 * available 627 * 628 * @see #getSoftMinEvictableIdleTimeMillis 629 */ 630 void setSoftMinEvictableIdleTimeMillis( 631 long softMinEvictableIdleTimeMillis) { 632 this.softMinEvictableIdleTimeMillis = softMinEvictableIdleTimeMillis; 633 } 634 635 /** 636 * Returns the name of the {@link EvictionPolicy} implementation that is 637 * used by this pool. 638 * 639 * @return The fully qualified class name of the {@link EvictionPolicy} 640 * 641 * @see #setEvictionPolicyClassName(string) 642 */ 643 string getEvictionPolicyClassName() { 644 return typeid(evictionPolicy).name; 645 } 646 647 /** 648 * Sets the eviction policy for this pool. 649 * 650 * @param evictionPolicy 651 * the eviction policy for this pool. 652 */ 653 void setEvictionPolicy(EvictionPolicy evictionPolicy) { 654 this.evictionPolicy = evictionPolicy; 655 } 656 657 /** 658 * Sets the name of the {@link EvictionPolicy} implementation that is used by this pool. The Pool will attempt to 659 * load the class using the given class loader. If that fails, use the class loader for the {@link EvictionPolicy} 660 * interface. 661 * 662 * @param evictionPolicyClassName 663 * the fully qualified class name of the new eviction policy 664 * @param classLoader 665 * the class loader to load the given {@code evictionPolicyClassName}. 666 * 667 * @see #getEvictionPolicyClassName() If loading the class using the given class loader fails, use the class loader for the 668 * {@link EvictionPolicy} interface. 669 */ 670 // void setEvictionPolicyClassName(string evictionPolicyClassName, ClassLoader classLoader) { 671 // // Getting epClass here and now best matches the caller's environment 672 // Class<?> epClass = EvictionPolicy.class; 673 // ClassLoader epClassLoader = epClass.getClassLoader(); 674 // try { 675 // try { 676 // setEvictionPolicy(evictionPolicyClassName, classLoader); 677 // } catch (ClassCastException | ClassNotFoundException e) { 678 // setEvictionPolicy(evictionPolicyClassName, epClassLoader); 679 // } 680 // } catch (ClassCastException e) { 681 // throw new IllegalArgumentException("Class " ~ evictionPolicyClassName ~ " from class loaders [" 682 // + classLoader ~ ", " ~ epClassLoader ~ "] do not implement " ~ EVICTION_POLICY_TYPE_NAME); 683 // } catch (ClassNotFoundException | InstantiationException | IllegalAccessException 684 // | InvocationTargetException | NoSuchMethodException e) { 685 // string exMessage = "Unable to create " ~ EVICTION_POLICY_TYPE_NAME ~ " instance of type " 686 // + evictionPolicyClassName; 687 // throw new IllegalArgumentException(exMessage, e); 688 // } 689 // } 690 691 // private void setEvictionPolicy(string className, ClassLoader classLoader) { 692 // Class<?> clazz = Class.forName(className, true, classLoader); 693 // Object policy = clazz.getConstructor().newInstance(); 694 // this.evictionPolicy = (EvictionPolicy!(T)) policy; 695 // } 696 697 /** 698 * Sets the name of the {@link EvictionPolicy} implementation that is used by this pool. The Pool will attempt to 699 * load the class using the thread context class loader. If that fails, the use the class loader for the 700 * {@link EvictionPolicy} interface. 701 * 702 * @param evictionPolicyClassName 703 * the fully qualified class name of the new eviction policy 704 *ctionPolicyClassName() 705 * @since 2.6.0 If loading the class using the thread context class loader fails, use the class loader for the 706 * {@link EvictionPolicy} interface. 707 */ 708 // void setEvictionPolicyClassName(string evictionPolicyClassName) { 709 // // setEvictionPolicyClassName(evictionPolicyClassName, Thread.getThis().getContextClassLoader()); 710 // implementationMissing(false); 711 // } 712 713 /** 714 * Gets the timeout that will be used when waiting for the Evictor to 715 * shutdown if this pool is closed and it is the only pool still using the 716 * the value for the Evictor. 717 * 718 * @return The timeout in milliseconds that will be used while waiting for 719 * the Evictor to shut down. 720 */ 721 long getEvictorShutdownTimeoutMillis() { 722 return evictorShutdownTimeoutMillis; 723 } 724 725 /** 726 * Sets the timeout that will be used when waiting for the Evictor to 727 * shutdown if this pool is closed and it is the only pool still using the 728 * the value for the Evictor. 729 * 730 * @param evictorShutdownTimeoutMillis the timeout in milliseconds that 731 * will be used while waiting for the 732 * Evictor to shut down. 733 */ 734 void setEvictorShutdownTimeoutMillis( 735 long evictorShutdownTimeoutMillis) { 736 this.evictorShutdownTimeoutMillis = evictorShutdownTimeoutMillis; 737 } 738 739 /** 740 * Closes the pool, destroys the remaining idle objects and, if registered 741 * in JMX, deregisters it. 742 */ 743 abstract void close(); 744 745 /** 746 * Has this pool instance been closed. 747 * @return <code>true</code> when this pool has been closed. 748 */ 749 bool isClosed() { 750 return closed; 751 } 752 753 /** 754 * <p>Perform <code>numTests</code> idle object eviction tests, evicting 755 * examined objects that meet the criteria for eviction. If 756 * <code>testWhileIdle</code> is true, examined objects are validated 757 * when visited (and removed if invalid); otherwise only objects that 758 * have been idle for more than <code>minEvicableIdleTimeMillis</code> 759 * are removed.</p> 760 * 761 * @throws Exception when there is a problem evicting idle objects. 762 */ 763 abstract void evict(); 764 765 /** 766 * Returns the {@link EvictionPolicy} defined for this pool. 767 * 768 * @since 2.6.0 Changed access from protected to public. 769 */ 770 EvictionPolicy getEvictionPolicy() { 771 return evictionPolicy; 772 } 773 774 /** 775 * Verifies that the pool is open. 776 * @throws IllegalStateException if the pool is closed. 777 */ 778 void assertOpen(){ 779 if (isClosed()) { 780 throw new IllegalStateException("Pool not open"); 781 } 782 } 783 784 /** 785 * <p>Starts the evictor with the given delay. If there is an evictor 786 * running when this method is called, it is stopped and replaced with a 787 * new evictor with the specified delay.</p> 788 * 789 * <p>This method needs to be final, since it is called from a constructor. 790 * See POOL-195.</p> 791 * 792 * @param delay time in milliseconds before start and between eviction runs 793 */ 794 void startEvictor(long delay) { 795 synchronized (evictionLock) { 796 EvictionTimer.cancel(evictor, evictorShutdownTimeoutMillis.msecs); 797 evictor = null; 798 evictionIterator = null; 799 if (delay > 0) { 800 evictor = new Evictor(); 801 EvictionTimer.schedule(evictor, delay, delay); 802 } 803 } 804 } 805 806 /** 807 * Stops the evictor. 808 */ 809 void stopEvitor() { 810 startEvictor(-1L); 811 } 812 /** 813 * Tries to ensure that the configured minimum number of idle instances are 814 * available in the pool. 815 * @throws Exception if an error occurs creating idle instances 816 */ 817 abstract void ensureMinIdle(); 818 819 820 // Monitoring (primarily JMX) related methods 821 822 /** 823 * Provides the name under which the pool has been registered with the 824 * platform MBean server or <code>null</code> if the pool has not been 825 * registered. 826 * @return the JMX name 827 */ 828 // ObjectName getJmxName() { 829 // return objectName; 830 // } 831 832 /** 833 * Provides the stack trace for the call that created this pool. JMX 834 * registration may trigger a memory leak so it is important that pools are 835 * deregistered when no longer used by calling the {@link #close()} method. 836 * This method is provided to assist with identifying code that creates but 837 * does not close it thereby creating a memory leak. 838 * @return pool creation stack trace 839 */ 840 string getCreationStackTrace() { 841 return creationStackTrace; 842 } 843 844 /** 845 * The total number of objects successfully borrowed from this pool over the 846 * lifetime of the pool. 847 * @return the borrowed object count 848 */ 849 long getBorrowedCount() { 850 return borrowedCount; 851 } 852 853 /** 854 * The total number of objects returned to this pool over the lifetime of 855 * the pool. This excludes attempts to return the same object multiple 856 * times. 857 * @return the returned object count 858 */ 859 long getReturnedCount() { 860 return returnedCount; 861 } 862 863 /** 864 * The total number of objects created for this pool over the lifetime of 865 * the pool. 866 * @return the created object count 867 */ 868 long getCreatedCount() { 869 return createdCount; 870 } 871 872 /** 873 * The total number of objects destroyed by this pool over the lifetime of 874 * the pool. 875 * @return the destroyed object count 876 */ 877 long getDestroyedCount() { 878 return destroyedCount; 879 } 880 881 /** 882 * The total number of objects destroyed by the evictor associated with this 883 * pool over the lifetime of the pool. 884 * @return the evictor destroyed object count 885 */ 886 long getDestroyedByEvictorCount() { 887 return destroyedByEvictorCount; 888 } 889 890 /** 891 * The total number of objects destroyed by this pool as a result of failing 892 * validation during <code>borrowObject()</code> over the lifetime of the 893 * pool. 894 * @return validation destroyed object count 895 */ 896 long getDestroyedByBorrowValidationCount() { 897 return destroyedByBorrowValidationCount; 898 } 899 900 /** 901 * The mean time objects are active for based on the last {@link 902 * #MEAN_TIMING_STATS_CACHE_SIZE} objects returned to the pool. 903 * @return mean time an object has been checked out from the pool among 904 * recently returned objects 905 */ 906 long getMeanActiveTimeMillis() { 907 return activeTimes.getMean(); 908 } 909 910 /** 911 * The mean time objects are idle for based on the last {@link 912 * #MEAN_TIMING_STATS_CACHE_SIZE} objects borrowed from the pool. 913 * @return mean time an object has been idle in the pool among recently 914 * borrowed objects 915 */ 916 long getMeanIdleTimeMillis() { 917 return idleTimes.getMean(); 918 } 919 920 /** 921 * The mean time threads wait to borrow an object based on the last {@link 922 * #MEAN_TIMING_STATS_CACHE_SIZE} objects borrowed from the pool. 923 * @return mean time in milliseconds that a recently served thread has had 924 * to wait to borrow an object from the pool 925 */ 926 long getMeanBorrowWaitTimeMillis() { 927 return waitTimes.getMean(); 928 } 929 930 /** 931 * The maximum time a thread has waited to borrow objects from the pool. 932 * @return maximum wait time in milliseconds since the pool was created 933 */ 934 long getMaxBorrowWaitTimeMillis() { 935 return maxBorrowWaitTimeMillis; 936 } 937 938 /** 939 * The number of instances currently idle in this pool. 940 * @return count of instances available for checkout from the pool 941 */ 942 abstract int getNumIdle(); 943 944 /** 945 * The listener used (if any) to receive notifications of exceptions 946 * unavoidably swallowed by the pool. 947 * 948 * @return The listener or <code>null</code> for no listener 949 */ 950 SwallowedExceptionListener getSwallowedExceptionListener() { 951 return swallowedExceptionListener; 952 } 953 954 /** 955 * The listener used (if any) to receive notifications of exceptions 956 * unavoidably swallowed by the pool. 957 * 958 * @param swallowedExceptionListener The listener or <code>null</code> 959 * for no listener 960 */ 961 void setSwallowedExceptionListener( 962 SwallowedExceptionListener swallowedExceptionListener) { 963 this.swallowedExceptionListener = swallowedExceptionListener; 964 } 965 966 /** 967 * Swallows an exception and notifies the configured listener for swallowed 968 * exceptions queue. 969 * 970 * @param swallowException exception to be swallowed 971 */ 972 void swallowException(Exception swallowException) { 973 SwallowedExceptionListener listener = getSwallowedExceptionListener(); 974 975 warning(swallowException); 976 977 if (listener is null) { 978 return; 979 } 980 981 try { 982 listener.onSwallowException(swallowException); 983 } catch (Throwable t) { 984 // Ignore. Enjoy the irony. 985 warning(t); 986 } 987 } 988 989 /** 990 * Updates statistics after an object is borrowed from the pool. 991 * @param p object borrowed from the pool 992 * @param waitTime time (in milliseconds) that the borrowing thread had to wait 993 */ 994 void updateStatsBorrow(IPooledObject p, long waitTime) { 995 AtomicHelper.increment(borrowedCount); 996 idleTimes.add(p.getIdleTimeMillis()); 997 waitTimes.add(waitTime); 998 999 // lock-free optimistic-locking maximum 1000 long currentMax; 1001 do { 1002 currentMax = maxBorrowWaitTimeMillis; 1003 if (currentMax >= waitTime) { 1004 break; 1005 } 1006 } while (!AtomicHelper.compareAndSet(maxBorrowWaitTimeMillis, currentMax, waitTime)); 1007 } 1008 1009 /** 1010 * Updates statistics after an object is returned to the pool. 1011 * @param activeTime the amount of time (in milliseconds) that the returning 1012 * object was checked out 1013 */ 1014 void updateStatsReturn(long activeTime) { 1015 AtomicHelper.increment(returnedCount); 1016 activeTimes.add(activeTime); 1017 } 1018 1019 /** 1020 * Marks the object as returning to the pool. 1021 * @param pooledObject instance to return to the keyed pool 1022 */ 1023 protected void markReturningState(IPooledObject pooledObject) { 1024 synchronized(pooledObject) { 1025 PooledObjectState state = pooledObject.getState(); 1026 if (state != PooledObjectState.ALLOCATED) { 1027 throw new IllegalStateException( 1028 "Object has already been returned to this pool or is invalid"); 1029 } 1030 pooledObject.markReturning(); // Keep from being marked abandoned 1031 } 1032 } 1033 1034 /** 1035 * Unregisters this pool's MBean. 1036 */ 1037 // void jmxUnregister() { 1038 // // if (objectName !is null) { 1039 // // try { 1040 // // ManagementFactory.getPlatformMBeanServer().unregisterMBean( 1041 // // objectName); 1042 // // } catch (MBeanRegistrationException | InstanceNotFoundException e) { 1043 // // swallowException(e); 1044 // // } 1045 // // } 1046 // implementationMissing(false); 1047 // } 1048 1049 /** 1050 * Registers the pool with the platform MBean server. 1051 * The registered name will be 1052 * <code>jmxNameBase + jmxNamePrefix + i</code> where i is the least 1053 * integer greater than or equal to 1 such that the name is not already 1054 * registered. Swallows MBeanRegistrationException, NotCompliantMBeanException 1055 * returning null. 1056 * 1057 * @param config Pool configuration 1058 * @param jmxNameBase default base JMX name for this pool 1059 * @param jmxNamePrefix name prefix 1060 * @return registered ObjectName, null if registration fails 1061 */ 1062 // private ObjectName jmxRegister(BaseObjectPoolConfig!(T) config, 1063 // string jmxNameBase, string jmxNamePrefix) { 1064 // ObjectName newObjectName = null; 1065 // MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); 1066 // int i = 1; 1067 // bool registered = false; 1068 // string base = config.getJmxNameBase(); 1069 // if (base is null) { 1070 // base = jmxNameBase; 1071 // } 1072 // while (!registered) { 1073 // try { 1074 // ObjectName objName; 1075 // // Skip the numeric suffix for the first pool in case there is 1076 // // only one so the names are cleaner. 1077 // if (i == 1) { 1078 // objName = new ObjectName(base + jmxNamePrefix); 1079 // } else { 1080 // objName = new ObjectName(base + jmxNamePrefix + i); 1081 // } 1082 // mbs.registerMBean(this, objName); 1083 // newObjectName = objName; 1084 // registered = true; 1085 // } catch (MalformedObjectNameException e) { 1086 // if (BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX.equals( 1087 // jmxNamePrefix) && jmxNameBase == base) { 1088 // // Shouldn't happen. Skip registration if it does. 1089 // registered = true; 1090 // } else { 1091 // // Must be an invalid name. Use the defaults instead. 1092 // jmxNamePrefix = 1093 // BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX; 1094 // base = jmxNameBase; 1095 // } 1096 // } catch (InstanceAlreadyExistsException e) { 1097 // // Increment the index and try again 1098 // i++; 1099 // } catch (MBeanRegistrationException | NotCompliantMBeanException e) { 1100 // // Shouldn't happen. Skip registration if it does. 1101 // registered = true; 1102 // } 1103 // } 1104 // return newObjectName; 1105 // implementationMissing(false); 1106 // return null; 1107 // } 1108 1109 /** 1110 * Gets the stack trace of an exception as a string. 1111 * @param e exception to trace 1112 * @return exception stack trace as a string 1113 */ 1114 private string getStackTrace(Exception e) { 1115 // Need the exception in string form to prevent the retention of 1116 // references to classes in the stack trace that could trigger a memory 1117 // leak in a container environment. 1118 // Writer w = new StringWriter(); 1119 // PrintWriter pw = new PrintWriter(w); 1120 // e.printStackTrace(pw); 1121 // return w.toString(); 1122 return e.toString(); 1123 } 1124 1125 // Inner classes 1126 1127 /** 1128 * The idle object evictor {@link TimerTask}. 1129 * 1130 * @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis 1131 */ 1132 class Evictor : Runnable { 1133 1134 private IFuture scheduledFuture; 1135 1136 /** 1137 * Run pool maintenance. Evict objects qualifying for eviction and then 1138 * ensure that the minimum number of idle instances are available. 1139 * Since the Timer that invokes Evictors is shared for all Pools but 1140 * pools may exist in different class loaders, the Evictor ensures that 1141 * any actions taken are under the class loader of the factory 1142 * associated with the pool. 1143 */ 1144 override 1145 void run() { 1146 // ClassLoader savedClassLoader = 1147 // Thread.getThis().getContextClassLoader(); 1148 try { 1149 // if (factoryClassLoader !is null) { 1150 // // Set the class loader for the factory 1151 // ClassLoader cl = factoryClassLoader.get(); 1152 // if (cl is null) { 1153 // // The pool has been dereferenced and the class loader 1154 // // GC'd. Cancel this timer so the pool can be GC'd as 1155 // // well. 1156 // cancel(); 1157 // return; 1158 // } 1159 // Thread.getThis().setContextClassLoader(cl); 1160 // } 1161 1162 // Evict from the pool 1163 try { 1164 evict(); 1165 } catch(Exception e) { 1166 swallowException(e); 1167 } catch(OutOfMemoryError oome) { 1168 // Log problem but give evictor thread a chance to continue 1169 // in case error is recoverable 1170 // oome.printStackTrace(System.err); 1171 warning(oome); 1172 } 1173 // Re-create idle instances. 1174 try { 1175 ensureMinIdle(); 1176 } catch (Exception e) { 1177 swallowException(e); 1178 } 1179 } finally { 1180 // Restore the previous CCL 1181 // Thread.getThis().setContextClassLoader(savedClassLoader); 1182 } 1183 } 1184 1185 1186 void setScheduledFuture(IFuture scheduledFuture) { 1187 this.scheduledFuture = scheduledFuture; 1188 } 1189 1190 1191 void cancel() { 1192 scheduledFuture.cancel(false); 1193 } 1194 } 1195 1196 /** 1197 * Maintains a cache of values for a single metric and reports 1198 * statistics on the cached values. 1199 */ 1200 private class StatsStore { 1201 1202 private long[] values; 1203 private int size; 1204 private int index; 1205 1206 /** 1207 * Create a StatsStore with the given cache size. 1208 * 1209 * @param size number of values to maintain in the cache. 1210 */ 1211 this(int size) { 1212 this.size = size; 1213 values = new long[size]; 1214 for (int i = 0; i < size; i++) { 1215 values[i] = -1; 1216 } 1217 } 1218 1219 /** 1220 * Adds a value to the cache. If the cache is full, one of the 1221 * existing values is replaced by the new value. 1222 * 1223 * @param value new value to add to the cache. 1224 */ 1225 void add(long value) { 1226 synchronized(this) { 1227 values[index] = value; 1228 index++; 1229 if (index == size) { 1230 index = 0; 1231 } 1232 } 1233 } 1234 1235 /** 1236 * Returns the mean of the cached values. 1237 * 1238 * @return the mean of the cache, truncated to long 1239 */ 1240 long getMean() { 1241 double result = 0; 1242 int counter = 0; 1243 for (int i = 0; i < size; i++) { 1244 long value = values[i]; 1245 if (value != -1) { 1246 counter++; 1247 result = result * ((counter - 1) / cast(double) counter) + 1248 value/cast(double) counter; 1249 } 1250 } 1251 return cast(long) result; 1252 } 1253 1254 override 1255 string toString() { 1256 StringBuilder builder = new StringBuilder(); 1257 builder.append("StatsStore [values="); 1258 builder.append(values.to!string()); 1259 builder.append(", size="); 1260 builder.append(size); 1261 builder.append(", index="); 1262 builder.append(index); 1263 builder.append("]"); 1264 return builder.toString(); 1265 } 1266 } 1267 1268 override 1269 protected void toStringAppendFields(StringBuilder builder) { 1270 builder.append("maxTotal="); 1271 builder.append(maxTotal); 1272 builder.append(", blockWhenExhausted="); 1273 builder.append(blockWhenExhausted); 1274 builder.append(", maxWaitMillis="); 1275 builder.append(maxWaitMillis); 1276 builder.append(", lifo="); 1277 builder.append(lifo); 1278 builder.append(", fairness="); 1279 builder.append(fairness); 1280 builder.append(", testOnCreate="); 1281 builder.append(testOnCreate); 1282 builder.append(", testOnBorrow="); 1283 builder.append(testOnBorrow); 1284 builder.append(", testOnReturn="); 1285 builder.append(testOnReturn); 1286 builder.append(", testWhileIdle="); 1287 builder.append(testWhileIdle); 1288 builder.append(", timeBetweenEvictionRunsMillis="); 1289 builder.append(timeBetweenEvictionRunsMillis); 1290 builder.append(", numTestsPerEvictionRun="); 1291 builder.append(numTestsPerEvictionRun); 1292 builder.append(", minEvictableIdleTimeMillis="); 1293 builder.append(minEvictableIdleTimeMillis); 1294 builder.append(", softMinEvictableIdleTimeMillis="); 1295 builder.append(softMinEvictableIdleTimeMillis); 1296 builder.append(", evictionPolicy="); 1297 builder.append((cast(Object)evictionPolicy).toString()); 1298 builder.append(", closeLock="); 1299 builder.append(closeLock); 1300 builder.append(", closed="); 1301 builder.append(closed); 1302 builder.append(", evictionLock="); 1303 builder.append(evictionLock); 1304 builder.append(", evictor="); 1305 builder.append(evictor); 1306 builder.append(", evictionIterator="); 1307 builder.append(evictionIterator); 1308 // builder.append(", factoryClassLoader="); 1309 // builder.append(factoryClassLoader); 1310 // builder.append(", oname="); 1311 // builder.append(objectName); 1312 builder.append(", creationStackTrace="); 1313 builder.append(creationStackTrace); 1314 builder.append(", borrowedCount="); 1315 builder.append(borrowedCount); 1316 builder.append(", returnedCount="); 1317 builder.append(returnedCount); 1318 builder.append(", createdCount="); 1319 builder.append(createdCount); 1320 builder.append(", destroyedCount="); 1321 builder.append(destroyedCount); 1322 builder.append(", destroyedByEvictorCount="); 1323 builder.append(destroyedByEvictorCount); 1324 builder.append(", destroyedByBorrowValidationCount="); 1325 builder.append(destroyedByBorrowValidationCount); 1326 builder.append(", activeTimes="); 1327 builder.append(activeTimes); 1328 builder.append(", idleTimes="); 1329 builder.append(idleTimes); 1330 builder.append(", waitTimes="); 1331 builder.append(waitTimes); 1332 builder.append(", maxBorrowWaitTimeMillis="); 1333 builder.append(maxBorrowWaitTimeMillis); 1334 builder.append(", swallowedExceptionListener="); 1335 builder.append((cast(Object)swallowedExceptionListener).toString()); 1336 } 1337 1338 1339 } 1340 1341 1342 /** 1343 * Wrapper for objects under management by the pool. 1344 * 1345 * GenericObjectPool and GenericKeyedObjectPool maintain references to all 1346 * objects under management using maps keyed on the objects. This wrapper 1347 * class ensures that objects can work as hash keys. 1348 * 1349 * @param <T> type of objects in the pool 1350 */ 1351 class IdentityWrapper(T) { 1352 /** Wrapped object */ 1353 private T instance; 1354 1355 /** 1356 * Create a wrapper for an instance. 1357 * 1358 * @param instance object to wrap 1359 */ 1360 this(T instance) { 1361 this.instance = instance; 1362 } 1363 1364 override 1365 size_t toHash() @trusted nothrow { 1366 return (cast(Object)instance).toHash(); 1367 } 1368 1369 override 1370 bool opEquals(Object other) { 1371 IdentityWrapper iw = cast(IdentityWrapper)other; 1372 if(iw is null) return false; 1373 1374 return iw.instance == instance; 1375 } 1376 1377 /** 1378 * @return the wrapped object 1379 */ 1380 T getObject() { 1381 return instance; 1382 } 1383 1384 override 1385 string toString() { 1386 StringBuilder builder = new StringBuilder(); 1387 builder.append("IdentityWrapper [instance="); 1388 builder.append(cast(Object)instance); 1389 builder.append("]"); 1390 return builder.toString(); 1391 } 1392 } 1393 1394 1395 1396 1397 /** 1398 * The idle object eviction iterator. Holds a reference to the idle objects. 1399 */ 1400 class EvictionIterator : InputRange!(IPooledObject) { 1401 1402 private Deque!(IPooledObject) idleObjects; 1403 private InputRange!(IPooledObject) idleObjectIterator; 1404 1405 /** 1406 * Create an EvictionIterator for the provided idle instance deque. 1407 * @param idleObjects underlying deque 1408 */ 1409 this(Deque!(IPooledObject) idleObjects, bool isLifo) { 1410 this.idleObjects = idleObjects; 1411 1412 if (isLifo) { 1413 idleObjectIterator = idleObjects.descendingIterator(); 1414 } else { 1415 idleObjectIterator = idleObjects.iterator(); 1416 } 1417 } 1418 1419 /** 1420 * Returns the idle object deque referenced by this iterator. 1421 * @return the idle object deque 1422 */ 1423 Deque!(IPooledObject) getIdleObjects() { 1424 return idleObjects; 1425 } 1426 1427 /** {@inheritDoc} */ 1428 // override 1429 bool empty() @property { 1430 return idleObjectIterator.empty(); 1431 } 1432 1433 /** {@inheritDoc} */ 1434 // override 1435 IPooledObject front() @property { 1436 return idleObjectIterator.front(); 1437 } 1438 1439 void popFront() { 1440 idleObjectIterator.popFront(); 1441 } 1442 1443 IPooledObject moveFront() { return idleObjectIterator.moveFront(); } 1444 1445 int opApply(scope int delegate(IPooledObject) dg) { 1446 return idleObjectIterator.opApply(dg); 1447 } 1448 1449 /// Ditto 1450 int opApply(scope int delegate(size_t, IPooledObject) dg) { 1451 return idleObjectIterator.opApply(dg); 1452 } 1453 1454 }