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.GenericObjectPool; 18 19 import hunt.pool.impl.AbandonedConfig; 20 import hunt.pool.impl.BaseGenericObjectPool; 21 import hunt.pool.impl.BaseObjectPoolConfig; 22 import hunt.pool.impl.DefaultPooledObject; 23 import hunt.pool.impl.DefaultPooledObjectInfo; 24 import hunt.pool.impl.EvictionConfig; 25 import hunt.pool.impl.EvictionPolicy; 26 import hunt.pool.impl.GenericObjectPoolConfig; 27 import hunt.pool.impl.GenericObjectPoolMXBean; 28 import hunt.pool.impl.LinkedBlockingDeque; 29 30 31 import hunt.pool.ObjectPool; 32 import hunt.pool.PoolUtils; 33 import hunt.pool.PooledObject; 34 import hunt.pool.PooledObjectFactory; 35 import hunt.pool.PooledObjectState; 36 import hunt.pool.SwallowedExceptionListener; 37 import hunt.pool.TrackedUse; 38 import hunt.pool.UsageTracking; 39 40 import hunt.Boolean; 41 import hunt.collection; 42 import hunt.concurrency.atomic.AtomicHelper; 43 import hunt.concurrency.thread; 44 import hunt.concurrency.SimpleObjectLock; 45 import hunt.Exceptions; 46 import hunt.logging.ConsoleLogger; 47 import hunt.util.StringBuilder; 48 import hunt.util.DateTime; 49 50 import core.time; 51 import std.algorithm; 52 import std.math; 53 54 /** 55 * A configurable {@link ObjectPool} implementation. 56 * <p> 57 * When coupled with the appropriate {@link PooledObjectFactory}, 58 * <code>GenericObjectPool</code> provides robust pooling functionality for 59 * arbitrary objects.</p> 60 * <p> 61 * Optionally, one may configure the pool to examine and possibly evict objects 62 * as they sit idle in the pool and to ensure that a minimum number of idle 63 * objects are available. This is performed by an "idle object eviction" thread, 64 * which runs asynchronously. Caution should be used when configuring this 65 * optional feature. Eviction runs contend with client threads for access to 66 * objects in the pool, so if they run too frequently performance issues may 67 * result.</p> 68 * <p> 69 * The pool can also be configured to detect and remove "abandoned" objects, 70 * i.e. objects that have been checked out of the pool but neither used nor 71 * returned before the configured 72 * {@link AbandonedConfig#getRemoveAbandonedTimeout() removeAbandonedTimeout}. 73 * Abandoned object removal can be configured to happen when 74 * <code>borrowObject</code> is invoked and the pool is close to starvation, or 75 * it can be executed by the idle object evictor, or both. If pooled objects 76 * implement the {@link TrackedUse} interface, their last use will be queried 77 * using the <code>getLastUsed</code> method on that interface; otherwise 78 * abandonment is determined by how long an object has been checked out from 79 * the pool.</p> 80 * <p> 81 * Implementation note: To prevent possible deadlocks, care has been taken to 82 * ensure that no call to a factory method will occur within a synchronization 83 * block. See POOL-125 and DBCP-44 for more information.</p> 84 * <p> 85 * This class is intended to be thread-safe.</p> 86 * 87 * @see GenericKeyedObjectPool 88 * 89 * @param <T> Type of element pooled in this pool. 90 * 91 */ 92 class GenericObjectPool(T) : BaseGenericObjectPool, 93 ObjectPool!(T), UsageTracking!(T) { // GenericObjectPoolMXBean, 94 95 /** 96 * Creates a new <code>GenericObjectPool</code> using defaults from 97 * {@link GenericObjectPoolConfig}. 98 * 99 * @param factory The object factory to be used to create object instances 100 * used by this pool 101 */ 102 this(PooledObjectFactory!(T) factory) { 103 this(factory, new GenericObjectPoolConfig()); 104 } 105 106 /** 107 * Creates a new <code>GenericObjectPool</code> using a specific 108 * configuration. 109 * 110 * @param factory The object factory to be used to create object instances 111 * used by this pool 112 * @param config The configuration to use for this pool instance. The 113 * configuration is used by value. Subsequent changes to 114 * the configuration object will not be reflected in the 115 * pool. 116 */ 117 this(PooledObjectFactory!(T) factory, 118 GenericObjectPoolConfig config) { 119 120 makeObjectCountLock = new Object(); 121 super(config, ONAME_BASE, config.getJmxNamePrefix()); 122 // allObjects = new ConcurrentHashMap<>(); 123 allObjects = new HashMap!(IdentityWrapper!(T), PooledObject!(T))(); 124 125 if (factory is null) { 126 // jmxUnregister(); // tidy up 127 throw new IllegalArgumentException("factory may not be null"); 128 } 129 this.factory = factory; 130 131 idleObjects = new LinkedBlockingDeque!(IPooledObject)(config.getFairness()); 132 133 setConfig(config); 134 } 135 136 /** 137 * Creates a new <code>GenericObjectPool</code> that tracks and destroys 138 * objects that are checked out, but never returned to the pool. 139 * 140 * @param factory The object factory to be used to create object instances 141 * used by this pool 142 * @param config The base pool configuration to use for this pool instance. 143 * The configuration is used by value. Subsequent changes to 144 * the configuration object will not be reflected in the 145 * pool. 146 * @param abandonedConfig Configuration for abandoned object identification 147 * and removal. The configuration is used by value. 148 */ 149 this(PooledObjectFactory!(T) factory, 150 GenericObjectPoolConfig config, AbandonedConfig abandonedConfig) { 151 this(factory, config); 152 setAbandonedConfig(abandonedConfig); 153 } 154 155 /** 156 * Returns the cap on the number of "idle" instances in the pool. If maxIdle 157 * is set too low on heavily loaded systems it is possible you will see 158 * objects being destroyed and almost immediately new objects being created. 159 * This is a result of the active threads momentarily returning objects 160 * faster than they are requesting them, causing the number of idle 161 * objects to rise above maxIdle. The best value for maxIdle for heavily 162 * loaded system will vary but the default is a good starting point. 163 * 164 * @return the maximum number of "idle" instances that can be held in the 165 * pool or a negative value if there is no limit 166 * 167 * @see #setMaxIdle 168 */ 169 // override 170 int getMaxIdle() { 171 return maxIdle; 172 } 173 174 /** 175 * Returns the cap on the number of "idle" instances in the pool. If maxIdle 176 * is set too low on heavily loaded systems it is possible you will see 177 * objects being destroyed and almost immediately new objects being created. 178 * This is a result of the active threads momentarily returning objects 179 * faster than they are requesting them, causing the number of idle 180 * objects to rise above maxIdle. The best value for maxIdle for heavily 181 * loaded system will vary but the default is a good starting point. 182 * 183 * @param maxIdle 184 * The cap on the number of "idle" instances in the pool. Use a 185 * negative value to indicate an unlimited number of idle 186 * instances 187 * 188 * @see #getMaxIdle 189 */ 190 void setMaxIdle(int maxIdle) { 191 this.maxIdle = maxIdle; 192 } 193 194 /** 195 * Sets the target for the minimum number of idle objects to maintain in 196 * the pool. This setting only has an effect if it is positive and 197 * {@link #getTimeBetweenEvictionRunsMillis()} is greater than zero. If this 198 * is the case, an attempt is made to ensure that the pool has the required 199 * minimum number of instances during idle object eviction runs. 200 * <p> 201 * If the configured value of minIdle is greater than the configured value 202 * for maxIdle then the value of maxIdle will be used instead. 203 * </p> 204 * 205 * @param minIdle 206 * The minimum number of objects. 207 * 208 * @see #getMinIdle() 209 * @see #getMaxIdle() 210 * @see #getTimeBetweenEvictionRunsMillis() 211 */ 212 void setMinIdle(int minIdle) { 213 this.minIdle = minIdle; 214 } 215 216 /** 217 * Returns the target for the minimum number of idle objects to maintain in 218 * the pool. This setting only has an effect if it is positive and 219 * {@link #getTimeBetweenEvictionRunsMillis()} is greater than zero. If this 220 * is the case, an attempt is made to ensure that the pool has the required 221 * minimum number of instances during idle object eviction runs. 222 * <p> 223 * If the configured value of minIdle is greater than the configured value 224 * for maxIdle then the value of maxIdle will be used instead. 225 * </p> 226 * 227 * @return The minimum number of objects. 228 * 229 * @see #setMinIdle(int) 230 * @see #setMaxIdle(int) 231 * @see #setTimeBetweenEvictionRunsMillis(long) 232 */ 233 // override 234 int getMinIdle() { 235 int maxIdleSave = getMaxIdle(); 236 if (this.minIdle > maxIdleSave) { 237 return maxIdleSave; 238 } 239 return minIdle; 240 } 241 242 /** 243 * Gets whether or not abandoned object removal is configured for this pool. 244 * 245 * @return true if this pool is configured to detect and remove 246 * abandoned objects 247 */ 248 // override 249 bool isAbandonedConfig() { 250 return abandonedConfig !is null; 251 } 252 253 /** 254 * Gets whether this pool identifies and logs any abandoned objects. 255 * 256 * @return {@code true} if abandoned object removal is configured for this 257 * pool and removal events are to be logged otherwise {@code false} 258 * 259 * @see AbandonedConfig#getLogAbandoned() 260 */ 261 // override 262 bool getLogAbandoned() { 263 AbandonedConfig ac = this.abandonedConfig; 264 return ac !is null && ac.getLogAbandoned(); 265 } 266 267 /** 268 * Gets whether a check is made for abandoned objects when an object is borrowed 269 * from this pool. 270 * 271 * @return {@code true} if abandoned object removal is configured to be 272 * activated by borrowObject otherwise {@code false} 273 * 274 * @see AbandonedConfig#getRemoveAbandonedOnBorrow() 275 */ 276 // override 277 bool getRemoveAbandonedOnBorrow() { 278 AbandonedConfig ac = this.abandonedConfig; 279 return ac !is null && ac.getRemoveAbandonedOnBorrow(); 280 } 281 282 /** 283 * Gets whether a check is made for abandoned objects when the evictor runs. 284 * 285 * @return {@code true} if abandoned object removal is configured to be 286 * activated when the evictor runs otherwise {@code false} 287 * 288 * @see AbandonedConfig#getRemoveAbandonedOnMaintenance() 289 */ 290 // override 291 bool getRemoveAbandonedOnMaintenance() { 292 AbandonedConfig ac = this.abandonedConfig; 293 return ac !is null && ac.getRemoveAbandonedOnMaintenance(); 294 } 295 296 /** 297 * Obtains the timeout before which an object will be considered to be 298 * abandoned by this pool. 299 * 300 * @return The abandoned object timeout in seconds if abandoned object 301 * removal is configured for this pool; int.max otherwise. 302 * 303 * @see AbandonedConfig#getRemoveAbandonedTimeout() 304 */ 305 // override 306 int getRemoveAbandonedTimeout() { 307 AbandonedConfig ac = this.abandonedConfig; 308 return ac !is null ? ac.getRemoveAbandonedTimeout() : int.max; 309 } 310 311 312 /** 313 * Sets the base pool configuration. 314 * 315 * @param conf the new configuration to use. This is used by value. 316 * 317 * @see GenericObjectPoolConfig 318 */ 319 void setConfig(GenericObjectPoolConfig conf) { 320 super.setConfig(conf); 321 setMaxIdle(conf.getMaxIdle()); 322 setMinIdle(conf.getMinIdle()); 323 setMaxTotal(conf.getMaxTotal()); 324 } 325 326 alias setConfig = BaseGenericObjectPool.setConfig; 327 328 /** 329 * Sets the abandoned object removal configuration. 330 * 331 * @param abandonedConfig the new configuration to use. This is used by value. 332 * 333 * @see AbandonedConfig 334 */ 335 void setAbandonedConfig(AbandonedConfig abandonedConfig) { 336 if (abandonedConfig is null) { 337 this.abandonedConfig = null; 338 } else { 339 this.abandonedConfig = new AbandonedConfig(); 340 this.abandonedConfig.setLogAbandoned(abandonedConfig.getLogAbandoned()); 341 // this.abandonedConfig.setLogWriter(abandonedConfig.getLogWriter()); 342 this.abandonedConfig.setRemoveAbandonedOnBorrow(abandonedConfig.getRemoveAbandonedOnBorrow()); 343 this.abandonedConfig.setRemoveAbandonedOnMaintenance(abandonedConfig.getRemoveAbandonedOnMaintenance()); 344 this.abandonedConfig.setRemoveAbandonedTimeout(abandonedConfig.getRemoveAbandonedTimeout()); 345 this.abandonedConfig.setUseUsageTracking(abandonedConfig.getUseUsageTracking()); 346 this.abandonedConfig.setRequireFullStackTrace(abandonedConfig.getRequireFullStackTrace()); 347 } 348 } 349 350 /** 351 * Obtains a reference to the factory used to create, destroy and validate 352 * the objects used by this pool. 353 * 354 * @return the factory 355 */ 356 PooledObjectFactory!(T) getFactory() { 357 return factory; 358 } 359 360 /** 361 * Equivalent to <code>{@link #borrowObject(long) 362 * borrowObject}({@link #getMaxWaitMillis()})</code>. 363 * <p> 364 * {@inheritDoc} 365 * </p> 366 */ 367 // override 368 T borrowObject(){ 369 return borrowObject(getMaxWaitMillis()); 370 } 371 372 /** 373 * Borrows an object from the pool using the specific waiting time which only 374 * applies if {@link #getBlockWhenExhausted()} is true. 375 * <p> 376 * If there is one or more idle instance available in the pool, then an 377 * idle instance will be selected based on the value of {@link #getLifo()}, 378 * activated and returned. If activation fails, or {@link #getTestOnBorrow() 379 * testOnBorrow} is set to <code>true</code> and validation fails, the 380 * instance is destroyed and the next available instance is examined. This 381 * continues until either a valid instance is returned or there are no more 382 * idle instances available. 383 * </p> 384 * <p> 385 * If there are no idle instances available in the pool, behavior depends on 386 * the {@link #getMaxTotal() maxTotal}, (if applicable) 387 * {@link #getBlockWhenExhausted()} and the value passed in to the 388 * <code>borrowMaxWaitMillis</code> parameter. If the number of instances 389 * checked out from the pool is less than <code>maxTotal,</code> a new 390 * instance is created, activated and (if applicable) validated and returned 391 * to the caller. If validation fails, a <code>NoSuchElementException</code> 392 * is thrown. 393 * </p> 394 * <p> 395 * If the pool is exhausted (no available idle instances and no capacity to 396 * create new ones), this method will either block (if 397 * {@link #getBlockWhenExhausted()} is true) or throw a 398 * <code>NoSuchElementException</code> (if 399 * {@link #getBlockWhenExhausted()} is false). The length of time that this 400 * method will block when {@link #getBlockWhenExhausted()} is true is 401 * determined by the value passed in to the <code>borrowMaxWaitMillis</code> 402 * parameter. 403 * </p> 404 * <p> 405 * When the pool is exhausted, multiple calling threads may be 406 * simultaneously blocked waiting for instances to become available. A 407 * "fairness" algorithm has been implemented to ensure that threads receive 408 * available instances in request arrival order. 409 * </p> 410 * 411 * @param borrowMaxWaitMillis The time to wait in milliseconds for an object 412 * to become available 413 * 414 * @return object instance from the pool 415 * 416 * @throws NoSuchElementException if an instance cannot be returned 417 * 418 * @throws Exception if an object instance cannot be returned due to an 419 * error 420 */ 421 T borrowObject(long borrowMaxWaitMillis) { 422 version(HUNT_DEBUG) { 423 import core.thread; 424 tracef("%s, Total: %d, Active: %d, Idle: %d, Waiters: %d, MaxWaitMillis: %d, Threads: %d", 425 typeid(T), getMaxTotal(), getNumActive(), getNumIdle(), getNumWaiters(), 426 borrowMaxWaitMillis, Thread.getAll().length); 427 428 scope(exit) { 429 infof("Total: %d, Active: %d, Idle: %d, Waiters: %d, Threads: %d", 430 getMaxTotal(), getNumActive(), getNumIdle(), getNumWaiters(), Thread.getAll().length); 431 } 432 } 433 434 assertOpen(); 435 436 AbandonedConfig ac = this.abandonedConfig; 437 if (ac !is null && ac.getRemoveAbandonedOnBorrow() && 438 (getNumIdle() < 2) && (getNumActive() > getMaxTotal() - 3) ) { 439 removeAbandoned(ac); 440 } 441 442 IPooledObject p = null; 443 444 // Get local copy of current config so it is consistent for entire 445 // method execution 446 bool blockWhenExhausted = getBlockWhenExhausted(); 447 448 bool isCreated; 449 long waitTime = DateTime.currentTimeMillis(); 450 451 while (p is null) { 452 isCreated = false; 453 p = idleObjects.pollFirst(); 454 if (p is null) { 455 p = create(); 456 if (p !is null) { 457 isCreated = true; 458 } 459 } 460 461 if (blockWhenExhausted) { 462 if (p is null) { 463 if (borrowMaxWaitMillis < 0) { 464 p = idleObjects.takeFirst(); 465 } else { 466 p = idleObjects.pollFirst(borrowMaxWaitMillis.msecs); 467 } 468 } 469 470 if (p is null) { 471 throw new NoSuchElementException("Timeout waiting for idle object"); 472 } 473 } else { 474 if (p is null) { 475 throw new NoSuchElementException("Pool exhausted"); 476 } 477 } 478 479 if (!p.allocate()) { 480 p = null; 481 } 482 483 if (p !is null) { 484 try { 485 factory.activateObject(p); 486 } catch (Exception e) { 487 try { 488 destroy(p); 489 } catch (Exception e1) { 490 // Ignore - activation failure is more important 491 } 492 p = null; 493 if (isCreated) { 494 NoSuchElementException nsee = new NoSuchElementException( 495 "Unable to activate object", e); 496 throw nsee; 497 } 498 } 499 500 if (p !is null && (getTestOnBorrow() || isCreated && getTestOnCreate())) { 501 bool validate = false; 502 Throwable validationThrowable = null; 503 504 try { 505 validate = factory.validateObject(p); 506 } catch (Throwable t) { 507 warning(t.msg); 508 version(HUNT_DEBUG) warning(t); 509 PoolUtils.checkRethrow(t); 510 validationThrowable = t; 511 } 512 513 if (!validate) { 514 try { 515 destroy(p); 516 destroyedByBorrowValidationCount.increment(); 517 } catch (Exception e) { 518 // Ignore - validation failure is more important 519 } 520 521 p = null; 522 if (isCreated) { 523 NoSuchElementException nsee = new NoSuchElementException( 524 "Unable to validate object", validationThrowable); 525 throw nsee; 526 } 527 } 528 } 529 } 530 } 531 532 updateStatsBorrow(p, DateTime.currentTimeMillis() - waitTime); 533 534 PooledObject!(T) pp = cast(PooledObject!(T))p; 535 T obj = pp.getObject(); 536 537 return obj; 538 } 539 540 /** 541 * {@inheritDoc} 542 * <p> 543 * If {@link #getMaxIdle() maxIdle} is set to a positive value and the 544 * number of idle instances has reached this value, the returning instance 545 * is destroyed. 546 * </p> 547 * <p> 548 * If {@link #getTestOnReturn() testOnReturn} == true, the returning 549 * instance is validated before being returned to the idle instance pool. In 550 * this case, if validation fails, the instance is destroyed. 551 * </p> 552 * <p> 553 * Exceptions encountered destroying objects for any reason are swallowed 554 * but notified via a {@link SwallowedExceptionListener}. 555 * </p> 556 */ 557 void returnObject(T obj) { 558 version(HUNT_POOL_DEBUG) { 559 infof("%s, object: %s, Active: %d/%d, Waiters: %d", 560 typeid(T), obj.toString(), getNumActive(), getMaxTotal(), getNumWaiters()); 561 } 562 563 PooledObject!(T) p = allObjects.get(new IdentityWrapper!T(obj)); 564 565 if (p is null) { 566 if (!isAbandonedConfig()) { 567 throw new IllegalStateException( 568 "Returned object not currently part of this pool"); 569 } 570 return; // Object was abandoned and removed 571 } 572 573 markReturningState(p); 574 575 long activeTime = p.getActiveTimeMillis(); 576 577 if (getTestOnReturn() && !factory.validateObject(p)) { 578 try { 579 destroy(p); 580 } catch (Exception e) { 581 swallowException(e); 582 } 583 try { 584 ensureIdle(1, false); 585 } catch (Exception e) { 586 swallowException(e); 587 } 588 updateStatsReturn(activeTime); 589 return; 590 } 591 592 try { 593 factory.passivateObject(p); 594 } catch (Exception e1) { 595 swallowException(e1); 596 try { 597 destroy(p); 598 } catch (Exception e) { 599 swallowException(e); 600 } 601 try { 602 ensureIdle(1, false); 603 } catch (Exception e) { 604 swallowException(e); 605 } 606 updateStatsReturn(activeTime); 607 return; 608 } 609 610 if (!p.deallocate()) { 611 throw new IllegalStateException( 612 "Object has already been returned to this pool or is invalid"); 613 } 614 615 int maxIdleSave = getMaxIdle(); 616 if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) { 617 try { 618 destroy(p); 619 } catch (Exception e) { 620 swallowException(e); 621 } 622 } else { 623 if (getLifo()) { 624 idleObjects.addFirst(p); 625 } else { 626 idleObjects.addLast(p); 627 } 628 if (isClosed()) { 629 // Pool closed while object was being added to idle objects. 630 // Make sure the returned object is destroyed rather than left 631 // in the idle object pool (which would effectively be a leak) 632 clear(); 633 } 634 } 635 updateStatsReturn(activeTime); 636 637 version(HUNT_DEBUG) { 638 infof("object: %s, Total: %d, Active: %d, Idle: %d, Waiters: %d", 639 (cast(Object)obj).toString(), getMaxTotal(), getNumActive(), 640 getNumIdle(), getNumWaiters()); 641 } 642 } 643 644 /** 645 * {@inheritDoc} 646 * <p> 647 * Activation of this method decrements the active count and attempts to 648 * destroy the instance. 649 * </p> 650 * 651 * @throws Exception if an exception occurs destroying the 652 * object 653 * @throws IllegalStateException if obj does not belong to this pool 654 */ 655 // override 656 void invalidateObject(T obj){ 657 PooledObject!(T) p = allObjects.get(new IdentityWrapper!T(obj)); 658 if (p is null) { 659 if (isAbandonedConfig()) { 660 return; 661 } 662 throw new IllegalStateException( 663 "Invalidated object not currently part of this pool"); 664 } 665 synchronized (p) { 666 if (p.getState() != PooledObjectState.INVALID) { 667 destroy(p); 668 } 669 } 670 ensureIdle(1, false); 671 } 672 673 /** 674 * Clears any objects sitting idle in the pool by removing them from the 675 * idle instance pool and then invoking the configured 676 * {@link PooledObjectFactory#destroyObject(PooledObject)} method on each 677 * idle instance. 678 * <p> 679 * Implementation notes: 680 * </p> 681 * <ul> 682 * <li>This method does not destroy or effect in any way instances that are 683 * checked out of the pool when it is invoked.</li> 684 * <li>Invoking this method does not prevent objects being returned to the 685 * idle instance pool, even during its execution. Additional instances may 686 * be returned while removed items are being destroyed.</li> 687 * <li>Exceptions encountered destroying idle instances are swallowed 688 * but notified via a {@link SwallowedExceptionListener}.</li> 689 * </ul> 690 */ 691 // override 692 void clear() { 693 IPooledObject p = idleObjects.poll(); 694 695 while (p !is null) { 696 try { 697 destroy(p); 698 } catch (Exception e) { 699 swallowException(e); 700 } 701 p = idleObjects.poll(); 702 } 703 } 704 705 // override 706 int getNumActive() { 707 return allObjects.size() - idleObjects.size(); 708 } 709 710 override 711 int getNumIdle() { 712 return idleObjects.size(); 713 } 714 715 /** 716 * Closes the pool. Once the pool is closed, {@link #borrowObject()} will 717 * fail with IllegalStateException, but {@link #returnObject(Object)} and 718 * {@link #invalidateObject(Object)} will continue to work, with returned 719 * objects destroyed on return. 720 * <p> 721 * Destroys idle instances in the pool by invoking {@link #clear()}. 722 * </p> 723 */ 724 override 725 void close() { 726 if (isClosed()) { 727 return; 728 } 729 730 synchronized (closeLock) { 731 if (isClosed()) { 732 return; 733 } 734 735 // Stop the evictor before the pool is closed since evict() calls 736 // assertOpen() 737 stopEvitor(); 738 739 closed = true; 740 // This clear removes any idle objects 741 clear(); 742 743 // jmxUnregister(); 744 745 // Release any threads that were waiting for an object 746 idleObjects.interuptTakeWaiters(); 747 } 748 } 749 750 /** 751 * {@inheritDoc} 752 * <p> 753 * Successive activations of this method examine objects in sequence, 754 * cycling through objects in oldest-to-youngest order. 755 * </p> 756 */ 757 override 758 void evict(){ 759 assertOpen(); 760 761 if (idleObjects.size() > 0) { 762 763 PooledObject!(T) underTest = null; 764 EvictionPolicy evictionPolicy = getEvictionPolicy(); 765 766 synchronized (evictionLock) { 767 EvictionConfig evictionConfig = new EvictionConfig( 768 getMinEvictableIdleTimeMillis(), 769 getSoftMinEvictableIdleTimeMillis(), 770 getMinIdle()); 771 772 bool testWhileIdle = getTestWhileIdle(); 773 774 for (int i = 0, m = getNumTests(); i < m; i++) { 775 if (evictionIterator is null || evictionIterator.empty()) { 776 evictionIterator = new EvictionIterator(idleObjects, getLifo()); 777 } 778 779 if (evictionIterator.empty()) { 780 // Pool exhausted, nothing to do here 781 return; 782 } 783 784 try { 785 underTest = cast(PooledObject!(T))evictionIterator.front(); 786 evictionIterator.popFront(); 787 } catch (NoSuchElementException nsee) { 788 // Object was borrowed in another thread 789 // Don't count this as an eviction test so reduce i; 790 i--; 791 evictionIterator = null; 792 continue; 793 } 794 795 if (!underTest.startEvictionTest()) { 796 // Object was borrowed in another thread 797 // Don't count this as an eviction test so reduce i; 798 i--; 799 continue; 800 } 801 802 // User provided eviction policy could throw all sorts of 803 // crazy exceptions. Protect against such an exception 804 // killing the eviction thread. 805 bool evict; 806 try { 807 if(evictionPolicy !is null) { 808 evict = evictionPolicy.evict(evictionConfig, underTest, idleObjects.size()); 809 } 810 } catch (Exception t) { 811 // Slightly convoluted as SwallowedExceptionListener 812 // uses Exception rather than Throwable 813 PoolUtils.checkRethrow(t); 814 swallowException(new Exception("swallowed exception", t)); 815 // Don't evict on error conditions 816 evict = false; 817 } 818 819 // version(HUNT_REDIS_DEBUG) tracef("evict=%s", evict); 820 821 if (evict) { 822 destroy(underTest); 823 destroyedByEvictorCount.increment(); 824 } else { 825 if (testWhileIdle) { 826 bool active = false; 827 try { 828 factory.activateObject(underTest); 829 active = true; 830 } catch (Exception e) { 831 destroy(underTest); 832 destroyedByEvictorCount.increment(); 833 } 834 if (active) { 835 if (!factory.validateObject(underTest)) { 836 destroy(underTest); 837 destroyedByEvictorCount.increment(); 838 } else { 839 try { 840 factory.passivateObject(underTest); 841 } catch (Exception e) { 842 destroy(underTest); 843 destroyedByEvictorCount.increment(); 844 } 845 } 846 } 847 } 848 if (!underTest.endEvictionTest(idleObjects)) { 849 // TODO - May need to add code here once additional 850 // states are used 851 } 852 } 853 854 // version(HUNT_REDIS_DEBUG) tracef("destroyedByEvictorCount = %d", destroyedByEvictorCount) ; 855 } 856 } 857 } 858 AbandonedConfig ac = this.abandonedConfig; 859 if (ac !is null && ac.getRemoveAbandonedOnMaintenance()) { 860 removeAbandoned(ac); 861 } 862 } 863 864 /** 865 * Tries to ensure that {@link #getMinIdle()} idle instances are available 866 * in the pool. 867 * 868 * @throws Exception If the associated factoryexception 869 */ 870 void preparePool(){ 871 if (getMinIdle() < 1) { 872 return; 873 } 874 ensureMinIdle(); 875 } 876 877 /** 878 * Attempts to create a new wrapped pooled object. 879 * <p> 880 * If there are {@link #getMaxTotal()} objects already in circulation 881 * or in process of being created, this method returns null. 882 * </p> 883 * 884 * @return The new wrapped pooled object 885 * 886 * @throws Exception if the object factory's {@code makeObject} fails 887 */ 888 private PooledObject!(T) create() { 889 int localMaxTotal = getMaxTotal(); 890 // This simplifies the code later in this method 891 if (localMaxTotal < 0) { 892 localMaxTotal = int.max; 893 } 894 895 long localStartTimeMillis = DateTime.currentTimeMillis(); 896 long localMaxWaitTimeMillis = max(getMaxWaitMillis(), 0); 897 898 SimpleObjectLock objectLock = new SimpleObjectLock(); 899 900 // Flag that indicates if create should: 901 // - TRUE: call the factory to create an object 902 // - FALSE: return null 903 // - null: loop and re-test the condition that determines whether to 904 // call the factory 905 Boolean isCreated = null; 906 while (isCreated is null) { 907 synchronized (makeObjectCountLock) { 908 long newCreateCount = createCount.increment(); 909 if (newCreateCount > localMaxTotal) { 910 // The pool is currently at capacity or in the process of 911 // making enough new objects to take it to capacity. 912 createCount.decrement(); 913 if (makeObjectCount == 0) { 914 // There are no makeObject() calls in progress so the 915 // pool is at capacity. Do not attempt to create a new 916 // object. Return and wait for an object to be returned 917 isCreated = Boolean.FALSE; 918 } else { 919 // There are makeObject() calls in progress that might 920 // bring the pool to capacity. Those calls might also 921 // fail so wait until they complete and then re-test if 922 // the pool is at capacity or not. 923 objectLock.wait(localMaxWaitTimeMillis.msecs); 924 } 925 } else { 926 // The pool is not at capacity. Create a new object. 927 makeObjectCount++; 928 isCreated = Boolean.TRUE; 929 } 930 } 931 932 // Do not block more if maxWaitTimeMillis is set. 933 if (isCreated is null && 934 (localMaxWaitTimeMillis > 0 && 935 DateTime.currentTimeMillis() - localStartTimeMillis >= localMaxWaitTimeMillis)) { 936 isCreated = Boolean.FALSE; 937 } 938 } 939 940 if (!isCreated.booleanValue()) { 941 return null; 942 } 943 944 PooledObject!(T) p; 945 try { 946 p = cast(PooledObject!(T))factory.makeObject(); 947 } catch (Throwable e) { 948 createCount.decrement(); 949 throw e; 950 } finally { 951 // FIXME: Needing refactor or cleanup -@Administrator at 2020-06-18T10:20:26+08:00 952 // Failed to build with LDC. Is it a bug or an error? 953 synchronized (makeObjectCountLock) { 954 makeObjectCount--; 955 } 956 objectLock.notifyAll(); 957 } 958 959 AbandonedConfig ac = this.abandonedConfig; 960 if (ac !is null && ac.getLogAbandoned()) { 961 p.setLogAbandoned(true); 962 // TODO: in 3.0, this can use the method defined on PooledObject 963 auto dpo = cast(DefaultPooledObject!(T)) p; 964 if (dpo !is null) { 965 dpo.setRequireFullStackTrace(ac.getRequireFullStackTrace()); 966 } 967 } 968 969 createdCount.increment(); 970 allObjects.put(new IdentityWrapper!T(p.getObject()), p); 971 return p; 972 } 973 974 /** 975 * Destroys a wrapped pooled object. 976 * 977 * @param toDestroy The wrapped pooled object to destroy 978 * 979 * @throws Exception If the factory fails to destroy the pooled object 980 * cleanly 981 */ 982 private void destroy(IPooledObject toDestroy){ 983 toDestroy.invalidate(); 984 idleObjects.remove(toDestroy); 985 986 auto pooledObj = cast(PooledObject!(T))toDestroy; 987 T obj = pooledObj.getObject(); 988 989 allObjects.remove(new IdentityWrapper!T(obj)); 990 try { 991 factory.destroyObject(toDestroy); 992 } finally { 993 destroyedCount.increment(); 994 createCount.decrement(); 995 } 996 997 if (idleObjects.isEmpty() && idleObjects.hasTakeWaiters()) { 998 // POOL-356. 999 // In case there are already threads waiting on something in the pool 1000 // (e.g. idleObjects.takeFirst(); then we need to provide them a fresh instance. 1001 // Otherwise they will be stuck forever (or until timeout) 1002 PooledObject!(T) freshPooled = create(); 1003 idleObjects.put(freshPooled); 1004 } 1005 } 1006 1007 override 1008 void ensureMinIdle(){ 1009 ensureIdle(getMinIdle(), true); 1010 } 1011 1012 /** 1013 * Tries to ensure that {@code idleCount} idle instances exist in the pool. 1014 * <p> 1015 * Creates and adds idle instances until either {@link #getNumIdle()} reaches {@code idleCount} 1016 * or the total number of objects (idle, checked out, or being created) reaches 1017 * {@link #getMaxTotal()}. If {@code always} is false, no instances are created unless 1018 * there are threads waiting to check out instances from the pool. 1019 * </p> 1020 * 1021 * @param idleCount the number of idle instances desired 1022 * @param always true means create instances even if the pool has no threads waiting 1023 * @throws Exception if the factory's makeObject throws 1024 */ 1025 private void ensureIdle(int idleCount, bool always){ 1026 if (idleCount < 1 || isClosed() || (!always && !idleObjects.hasTakeWaiters())) { 1027 return; 1028 } 1029 1030 while (idleObjects.size() < idleCount) { 1031 PooledObject!(T) p = create(); 1032 if (p is null) { 1033 // Can't create objects, no reason to think another call to 1034 // create will work. Give up. 1035 break; 1036 } 1037 if (getLifo()) { 1038 idleObjects.addFirst(p); 1039 } else { 1040 idleObjects.addLast(p); 1041 } 1042 } 1043 if (isClosed()) { 1044 // Pool closed while object was being added to idle objects. 1045 // Make sure the returned object is destroyed rather than left 1046 // in the idle object pool (which would effectively be a leak) 1047 clear(); 1048 } 1049 } 1050 1051 /** 1052 * Creates an object, and place it into the pool. addObject() is useful for 1053 * "pre-loading" a pool with idle objects. 1054 * <p> 1055 * If there is no capacity available to add to the pool, this is a no-op 1056 * (no exception, no impact to the pool). </p> 1057 */ 1058 // override 1059 void addObject(){ 1060 assertOpen(); 1061 if (factory is null) { 1062 throw new IllegalStateException("Cannot add objects without a factory."); 1063 } 1064 PooledObject!(T) p = create(); 1065 addIdleObject(p); 1066 } 1067 1068 /** 1069 * Adds the provided wrapped pooled object to the set of idle objects for 1070 * this pool. The object must already be part of the pool. If {@code p} 1071 * is null, this is a no-op (no exception, but no impact on the pool). 1072 * 1073 * @param p The object to make idle 1074 * 1075 * @throws Exception If the factory fails to passivate the object 1076 */ 1077 private void addIdleObject(PooledObject!(T) p){ 1078 if (p !is null) { 1079 factory.passivateObject(p); 1080 if (getLifo()) { 1081 idleObjects.addFirst(p); 1082 } else { 1083 idleObjects.addLast(p); 1084 } 1085 } 1086 } 1087 1088 /** 1089 * Calculates the number of objects to test in a run of the idle object 1090 * evictor. 1091 * 1092 * @return The number of objects to test for validity 1093 */ 1094 private int getNumTests() { 1095 int numTestsPerEvictionRun = getNumTestsPerEvictionRun(); 1096 if (numTestsPerEvictionRun >= 0) { 1097 return min(numTestsPerEvictionRun, idleObjects.size()); 1098 } 1099 return cast(int) (ceil(idleObjects.size() / 1100 abs(cast(float) numTestsPerEvictionRun))); 1101 } 1102 1103 /** 1104 * Recovers abandoned objects which have been checked out but 1105 * not used since longer than the removeAbandonedTimeout. 1106 * 1107 * @param ac The configuration to use to identify abandoned objects 1108 */ 1109 private void removeAbandoned(AbandonedConfig ac) { 1110 // Generate a list of abandoned objects to remove 1111 long now = DateTime.currentTimeMillis(); 1112 long timeout = now - (ac.getRemoveAbandonedTimeout() * 1000L); 1113 ArrayList!(PooledObject!(T)) toRemove = new ArrayList!(PooledObject!(T))(); 1114 1115 foreach(PooledObject!(T) pooledObject; allObjects.byValue()) { 1116 synchronized (pooledObject) { 1117 if (pooledObject.getState() == PooledObjectState.ALLOCATED && 1118 pooledObject.getLastUsedTime() <= timeout) { 1119 pooledObject.markAbandoned(); 1120 toRemove.add(pooledObject); 1121 } 1122 } 1123 } 1124 1125 // Now remove the abandoned objects 1126 foreach(PooledObject!(T) pooledObject; toRemove) { 1127 if (ac.getLogAbandoned()) { 1128 // pooledObject.printStackTrace(ac.getLogWriter()); 1129 warning("running here"); 1130 } 1131 try { 1132 invalidateObject(pooledObject.getObject()); 1133 } catch (Exception e) { 1134 // e.printStackTrace(); 1135 warning(e); 1136 } 1137 } 1138 } 1139 1140 1141 //--- Usage tracking support ----------------------------------------------- 1142 1143 // override 1144 void use(T pooledObject) { 1145 AbandonedConfig ac = this.abandonedConfig; 1146 if (ac !is null && ac.getUseUsageTracking()) { 1147 PooledObject!(T) wrapper = allObjects.get(new IdentityWrapper!T(pooledObject)); 1148 wrapper.use(); 1149 } 1150 } 1151 1152 1153 //--- JMX support ---------------------------------------------------------- 1154 1155 private string factoryType = null; 1156 1157 /** 1158 * Returns an estimate of the number of threads currently blocked waiting for 1159 * an object from the pool. This is intended for monitoring only, not for 1160 * synchronization control. 1161 * 1162 * @return The estimate of the number of threads currently blocked waiting 1163 * for an object from the pool 1164 */ 1165 // override 1166 int getNumWaiters() { 1167 // TODO: Tasks pending completion -@zhangxueping at 2020-03-31T16:01:28+08:00 1168 // to check this 1169 if (getBlockWhenExhausted()) { 1170 return idleObjects.getTakeQueueLength(); 1171 } 1172 return 0; 1173 } 1174 1175 /** 1176 * Returns the type - including the specific type rather than the generic - 1177 * of the factory. 1178 * 1179 * @return A string representation of the factory type 1180 */ 1181 // override 1182 string getFactoryType() { 1183 // Not thread safe. Accept that there may be multiple evaluations. 1184 if (factoryType is null) { 1185 StringBuilder result = new StringBuilder(); 1186 result.append(typeid(factory).name); 1187 result.append('<'); 1188 implementationMissing(false); 1189 // Class<?> pooledObjectType = 1190 // PoolImplUtils.getFactoryType(factory.getClass()); 1191 // result.append(pooledObjectType.getName()); 1192 result.append('>'); 1193 factoryType = result.toString(); 1194 } 1195 return factoryType; 1196 } 1197 1198 /** 1199 * Provides information on all the objects in the pool, both idle (waiting 1200 * to be borrowed) and active (currently borrowed). 1201 * <p> 1202 * Note: This is named listAllObjects so it is presented as an operation via 1203 * JMX. That means it won't be invoked unless the explicitly requested 1204 * whereas all attributes will be automatically requested when viewing the 1205 * attributes for an object in a tool like JConsole. 1206 * </p> 1207 * 1208 * @return Information grouped on all the objects in the pool 1209 */ 1210 // override 1211 Set!(DefaultPooledObjectInfo) listAllObjects() { 1212 Set!(DefaultPooledObjectInfo) result = 1213 new HashSet!(DefaultPooledObjectInfo)(allObjects.size()); 1214 foreach (PooledObject!(T) p ; allObjects.byValue()) { 1215 result.add(new DefaultPooledObjectInfo(p)); 1216 } 1217 return result; 1218 } 1219 1220 // --- configuration attributes -------------------------------------------- 1221 1222 private int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE; 1223 private int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE; 1224 private PooledObjectFactory!(T) factory; 1225 1226 1227 // --- internal attributes ------------------------------------------------- 1228 1229 /* 1230 * All of the objects currently associated with this pool in any state. It 1231 * excludes objects that have been destroyed. The size of 1232 * {@link #allObjects} will always be less than or equal to {@link 1233 * #_maxActive}. Map keys are pooled objects, values are the PooledObject 1234 * wrappers used internally by the pool. 1235 */ 1236 private Map!(IdentityWrapper!(T), PooledObject!(T)) allObjects; 1237 // new ConcurrentHashMap<>(); 1238 /* 1239 * The combined count of the currently created objects and those in the 1240 * process of being created. Under load, it may exceed {@link #_maxActive} 1241 * if multiple threads try and create a new object at the same time but 1242 * {@link #create()} will ensure that there are never more than 1243 * {@link #_maxActive} objects created at any one time. 1244 */ 1245 private shared long createCount = 0; // new AtomicLong(0); 1246 private long makeObjectCount = 0; 1247 private Object makeObjectCountLock; // = new Object(); 1248 private LinkedBlockingDeque!(IPooledObject) idleObjects; 1249 1250 // JMX specific attributes 1251 private enum string ONAME_BASE = "hunt.pool:type=GenericObjectPool,name="; 1252 1253 // Additional configuration properties for abandoned object tracking 1254 private AbandonedConfig abandonedConfig = null; 1255 1256 override 1257 protected void toStringAppendFields(StringBuilder builder) { 1258 super.toStringAppendFields(builder); 1259 builder.append(", factoryType="); 1260 builder.append(factoryType); 1261 builder.append(", maxIdle="); 1262 builder.append(maxIdle); 1263 builder.append(", minIdle="); 1264 builder.append(minIdle); 1265 builder.append(", factory="); 1266 builder.append((cast(Object)factory).toString()); 1267 builder.append(", allObjects="); 1268 builder.append(allObjects.toString()); 1269 builder.append(", createCount="); 1270 builder.append(createCount); 1271 builder.append(", idleObjects="); 1272 builder.append(idleObjects.toString()); 1273 builder.append(", abandonedConfig="); 1274 builder.append(abandonedConfig); 1275 } 1276 1277 }