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.EvictionTimer;
18 
19 import hunt.pool.impl.BaseGenericObjectPool;
20 
21 import hunt.concurrency.thread;
22 import hunt.concurrency.Delayed;
23 import hunt.concurrency.Future;
24 import hunt.concurrency.ScheduledThreadPoolExecutor;
25 import hunt.concurrency.ThreadFactory;
26 
27 import hunt.Exceptions;
28 import hunt.logging.ConsoleLogger;
29 import hunt.util.Common;
30 import hunt.util.Runnable;
31 import hunt.util.StringBuilder;
32 
33 import core.thread;
34 import core.time;
35 
36 /**
37  * Provides a shared idle object eviction timer for all pools.
38  * <p>
39  * This class is currently implemented using {@link ScheduledThreadPoolExecutor}. This implementation may change in any
40  * future release. This class keeps track of how many pools are using it. If no pools are using the timer, it is
41  * cancelled. This prevents a thread being left running which, in application server environments, can lead to memory
42  * leads and/or prevent applications from shutting down or reloading cleanly.
43  * </p>
44  * <p>
45  * This class has package scope to prevent its inclusion in the pool public API. The class declaration below should
46  * *not* be changed to public.
47  * </p>
48  * <p>
49  * This class is intended to be thread-safe.
50  * </p>
51  *
52  */
53 class EvictionTimer {
54 
55     /** Executor instance */
56     private __gshared ScheduledThreadPoolExecutor executor; //@GuardedBy("EvictionTimer.class")
57 
58     /** Prevent instantiation */
59     private this() {
60         // Hide the default constructor
61     }
62 
63 
64     /**
65      */
66     override
67     string toString() {
68         StringBuilder builder = new StringBuilder();
69         builder.append("EvictionTimer []");
70         return builder.toString();
71     }
72 
73 
74     /**
75      * Add the specified eviction task to the timer. Tasks that are added with a
76      * call to this method *must* call {@link #cancel(TimerTask)} to cancel the
77      * task to prevent memory and/or thread leaks in application server
78      * environments.
79      *
80      * @param task      Task to be scheduled
81      * @param delay     Delay in milliseconds before task is executed
82      * @param period    Time in milliseconds between executions
83      */
84     static void schedule(BaseGenericObjectPool.Evictor task, long delay, long period) {
85         if (executor is null) {
86             executor = new ScheduledThreadPoolExecutor(1, new EvictorThreadFactory());
87             executor.setRemoveOnCancelPolicy(true);
88         }
89         IFuture scheduledFuture =
90                 executor.scheduleWithFixedDelay(task, delay.msecs, period.msecs);
91         task.setScheduledFuture(scheduledFuture);
92     }
93 
94     /**
95      * Remove the specified eviction task from the timer.
96      *
97      * @param evictor      Task to be cancelled
98      * @param timeout   If the associated executor is no longer required, how
99      *                  long should this thread wait for the executor to
100      *                  terminate?
101      * @param unit      The units for the specified timeout
102      */
103     static synchronized void cancel(BaseGenericObjectPool.Evictor evictor, Duration timeout) {
104         if (evictor !is null) {
105             evictor.cancel();
106         }
107         if (executor !is null && executor.getQueue().isEmpty()) {
108             executor.shutdown();
109             try {
110                 // executor.awaitTermination(timeout);
111                 // TODO: Tasks pending completion -@zhangxueping at 2019-12-06T16:11:54+08:00
112                 // 
113             } catch (InterruptedException e) {
114                 version(HUNT_DEBUG) warning(e.msg);
115                 // Swallow
116                 // Significant API changes would be required to propagate this
117             }
118             executor.setCorePoolSize(0);
119             executor = null;
120         }
121     }
122 
123     /**
124      * Thread factory that creates a daemon thread, with the context class loader from this class.
125      */
126     private static class EvictorThreadFactory : ThreadFactory {
127 
128         Thread newThread(Runnable runnable) {
129             ThreadEx thread = new ThreadEx(null, runnable, "commons-pool-evictor-thread");
130             thread.setDaemon(true); // POOL-363 - Required for applications using Runtime.addShutdownHook(). --joshlandin 03.27.2019
131             // AccessController.doPrivileged(new PrivilegedAction!(Void)() {
132             //     override
133             //     Void run() {
134             //         thread.setContextClassLoader(EvictorThreadFactory.class.getClassLoader());
135             //         return null;
136             //     }
137             // });
138 
139             return thread;
140         }
141     }
142 }