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 }