| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.pool3.impl; |
| |
| import java.lang.ref.WeakReference; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.time.Duration; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map.Entry; |
| import java.util.concurrent.ScheduledFuture; |
| import java.util.concurrent.ScheduledThreadPoolExecutor; |
| import java.util.concurrent.ThreadFactory; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * Provides a shared idle object eviction timer for all pools. |
| * <p> |
| * This class is currently implemented using {@link ScheduledThreadPoolExecutor}. This implementation may change in any |
| * future release. This class keeps track of how many pools are using it. If no pools are using the timer, it is |
| * canceled. This prevents a thread being left running which, in application server environments, can lead to memory |
| * leads and/or prevent applications from shutting down or reloading cleanly. |
| * </p> |
| * <p> |
| * This class has package scope to prevent its inclusion in the pool public API. The class declaration below should |
| * *not* be changed to public. |
| * </p> |
| * <p> |
| * This class is intended to be thread-safe. |
| * </p> |
| * |
| * @since 2.0 |
| */ |
| final class EvictionTimer { |
| |
| /** |
| * Thread factory that creates a daemon thread, with the context class loader from this class. |
| */ |
| private static final class EvictorThreadFactory implements ThreadFactory { |
| |
| @Override |
| public Thread newThread(final Runnable runnable) { |
| final Thread thread = new Thread(null, runnable, "commons-pool-evictor"); |
| thread.setDaemon(true); // POOL-363 - Required for applications using Runtime.addShutdownHook(). |
| AccessController.doPrivileged((PrivilegedAction<Void>) () -> { |
| thread.setContextClassLoader(EvictorThreadFactory.class.getClassLoader()); |
| return null; |
| }); |
| |
| return thread; |
| } |
| } |
| |
| /** |
| * Task that removes references to abandoned tasks and shuts |
| * down the executor if there are no live tasks left. |
| */ |
| private static final class Reaper implements Runnable { |
| @Override |
| public void run() { |
| synchronized (EvictionTimer.class) { |
| /* |
| * Need to use iterator over TASK_MAP so entries can be removed when iterating without triggering a |
| * ConcurrentModificationException. |
| */ |
| final Iterator<Entry<WeakReference<BaseGenericObjectPool<?, ?>.Evictor>, WeakRunner<BaseGenericObjectPool<?, ?>.Evictor>>> iterator = |
| TASK_MAP.entrySet().iterator(); |
| while (iterator.hasNext()) { |
| final Entry<WeakReference<BaseGenericObjectPool<?, ?>.Evictor>, WeakRunner<BaseGenericObjectPool<?, ?>.Evictor>> entry = iterator.next(); |
| if (entry.getKey().get() == null) { |
| executor.remove(entry.getValue()); |
| iterator.remove(); |
| } |
| } |
| if (TASK_MAP.isEmpty() && executor != null) { |
| executor.shutdown(); |
| executor.setCorePoolSize(0); |
| executor = null; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Runnable that runs the referent of a weak reference. When the referent is no |
| * no longer reachable, run is no-op. |
| * @param <R> The kind of Runnable. |
| */ |
| private static final class WeakRunner<R extends Runnable> implements Runnable { |
| |
| private final WeakReference<R> ref; |
| |
| /** |
| * Constructs a new instance to track the given reference. |
| * |
| * @param ref the reference to track. |
| */ |
| private WeakRunner(final WeakReference<R> ref) { |
| this.ref = ref; |
| } |
| |
| @Override |
| public void run() { |
| final Runnable task = ref.get(); |
| if (task != null) { |
| task.run(); |
| } else { |
| synchronized (EvictionTimer.class) { |
| executor.remove(this); |
| TASK_MAP.remove(ref); |
| } |
| } |
| } |
| } |
| |
| /** Executor instance */ |
| private static ScheduledThreadPoolExecutor executor; //@GuardedBy("EvictionTimer.class") |
| |
| /** Keys are weak references to tasks, values are runners managed by executor. */ |
| private static final HashMap< |
| WeakReference<BaseGenericObjectPool<?, ?>.Evictor>, |
| WeakRunner<BaseGenericObjectPool<?, ?>.Evictor>> TASK_MAP = new HashMap<>(); // @GuardedBy("EvictionTimer.class") |
| |
| /** |
| * Removes the specified eviction task from the timer. |
| * |
| * @param evictor Task to be canceled. |
| * @param timeout If the associated executor is no longer required, how |
| * long should this thread wait for the executor to |
| * terminate? |
| * @param restarting The state of the evictor. |
| */ |
| static synchronized void cancel(final BaseGenericObjectPool<?, ?>.Evictor evictor, final Duration timeout, |
| final boolean restarting) { |
| if (evictor != null) { |
| evictor.cancel(); |
| remove(evictor); |
| } |
| if (!restarting && executor != null && TASK_MAP.isEmpty()) { |
| executor.shutdown(); |
| try { |
| executor.awaitTermination(timeout.toMillis(), TimeUnit.MILLISECONDS); |
| } catch (final InterruptedException e) { |
| // Swallow |
| // Significant API changes would be required to propagate this |
| } |
| executor.setCorePoolSize(0); |
| executor = null; |
| } |
| } |
| |
| /** |
| * For testing only. |
| * |
| * @return The executor. |
| */ |
| static ScheduledThreadPoolExecutor getExecutor() { |
| return executor; |
| } |
| |
| /** |
| * @return the number of eviction tasks under management. |
| */ |
| static synchronized int getNumTasks() { |
| return TASK_MAP.size(); |
| } |
| |
| /** |
| * Gets the task map. Keys are weak references to tasks, values are runners managed by executor. |
| * |
| * @return the task map. |
| */ |
| static HashMap<WeakReference<BaseGenericObjectPool<?, ?>.Evictor>, WeakRunner<BaseGenericObjectPool<?, ?>.Evictor>> getTaskMap() { |
| return TASK_MAP; |
| } |
| |
| /** |
| * Removes evictor from the task set and executor. |
| * Only called when holding the class lock. |
| * |
| * @param evictor Eviction task to remove |
| */ |
| private static void remove(final BaseGenericObjectPool<?, ?>.Evictor evictor) { |
| for (final Entry<WeakReference<BaseGenericObjectPool<?, ?>.Evictor>, WeakRunner<BaseGenericObjectPool<?, ?>.Evictor>> entry : TASK_MAP.entrySet()) { |
| if (entry.getKey().get() == evictor) { |
| executor.remove(entry.getValue()); |
| TASK_MAP.remove(entry.getKey()); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Adds the specified eviction task to the timer. Tasks that are added with |
| * a call to this method *must* call {@link |
| * #cancel(BaseGenericObjectPool.Evictor, Duration, boolean)} |
| * to cancel the task to prevent memory and/or thread leaks in application |
| * server environments. |
| * |
| * @param task Task to be scheduled. |
| * @param delay Delay in milliseconds before task is executed. |
| * @param period Time in milliseconds between executions. |
| */ |
| static synchronized void schedule( |
| final BaseGenericObjectPool<?, ?>.Evictor task, final Duration delay, final Duration period) { |
| if (null == executor) { |
| executor = new ScheduledThreadPoolExecutor(1, new EvictorThreadFactory()); |
| executor.setRemoveOnCancelPolicy(true); |
| executor.scheduleAtFixedRate(new Reaper(), delay.toMillis(), period.toMillis(), TimeUnit.MILLISECONDS); |
| } |
| final WeakReference<BaseGenericObjectPool<?, ?>.Evictor> ref = new WeakReference<>(task); |
| final WeakRunner<BaseGenericObjectPool<?, ?>.Evictor> runner = new WeakRunner<>(ref); |
| final ScheduledFuture<?> scheduledFuture = executor.scheduleWithFixedDelay(runner, delay.toMillis(), |
| period.toMillis(), TimeUnit.MILLISECONDS); |
| task.setScheduledFuture(scheduledFuture); |
| TASK_MAP.put(ref, runner); |
| } |
| |
| /** Prevents instantiation */ |
| private EvictionTimer() { |
| // Hide the default constructor |
| } |
| |
| /** |
| * @since 2.4.3 |
| */ |
| @Override |
| public String toString() { |
| final StringBuilder builder = new StringBuilder(); |
| builder.append("EvictionTimer []"); |
| return builder.toString(); |
| } |
| |
| } |