fix: obtained plugin list may cause thread-safe problems during iteration

pull/854/head
huangchengxing 3 years ago
parent 3b3ab515a5
commit ceb1efd0e5

@ -21,13 +21,33 @@ import cn.hippo4j.common.toolkit.Assert;
import cn.hippo4j.core.plugin.*; import cn.hippo4j.core.plugin.*;
import lombok.NonNull; import lombok.NonNull;
import java.util.*; import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
/** /**
* The default implementation of {@link ThreadPoolPluginManager}. * <p>The default implementation of {@link ThreadPoolPluginManager}.
* Provide basic {@link ThreadPoolPlugin} registration, logout and acquisition functions.
* Most APIs ensure limited thread-safe.
*
* <p>Usually registered to {@link cn.hippo4j.core.executor.ExtensibleThreadPoolExecutor},
* or bound to an {@link java.util.concurrent.ThreadPoolExecutor} instance through {@link ThreadPoolPluginSupport}
* to support its plugin based extension functions.
*
* <p><b>NOTE:</b>
* When the list of plugins is obtained through the {@code getXXX} method of manager, the list is not immutable.
* This means that until actually start iterating over the list,
* registering or unregistering plugins through the manager will affect the results of the iteration.
* Therefore, we should try to ensure that <b>get the latest plugin list from the manager before each use</b>.
*
* @see cn.hippo4j.core.executor.DynamicThreadPoolExecutor
* @see cn.hippo4j.core.executor.ExtensibleThreadPoolExecutor
*/ */
public class DefaultThreadPoolPluginManager implements ThreadPoolPluginManager { public class DefaultThreadPoolPluginManager implements ThreadPoolPluginManager {
@ -39,27 +59,27 @@ public class DefaultThreadPoolPluginManager implements ThreadPoolPluginManager {
/** /**
* Registered {@link ThreadPoolPlugin}. * Registered {@link ThreadPoolPlugin}.
*/ */
private final Map<String, ThreadPoolPlugin> registeredPlugins = new HashMap<>(16); private final Map<String, ThreadPoolPlugin> registeredPlugins = new ConcurrentHashMap<>(16);
/** /**
* Registered {@link TaskAwarePlugin}. * Registered {@link TaskAwarePlugin}.
*/ */
private final List<TaskAwarePlugin> taskAwarePluginList = new ArrayList<>(); private final List<TaskAwarePlugin> taskAwarePluginList = new CopyOnWriteArrayList<>();
/** /**
* Registered {@link ExecuteAwarePlugin}. * Registered {@link ExecuteAwarePlugin}.
*/ */
private final List<ExecuteAwarePlugin> executeAwarePluginList = new ArrayList<>(); private final List<ExecuteAwarePlugin> executeAwarePluginList = new CopyOnWriteArrayList<>();
/** /**
* Registered {@link RejectedAwarePlugin}. * Registered {@link RejectedAwarePlugin}.
*/ */
private final List<RejectedAwarePlugin> rejectedAwarePluginList = new ArrayList<>(); private final List<RejectedAwarePlugin> rejectedAwarePluginList = new CopyOnWriteArrayList<>();
/** /**
* Registered {@link ShutdownAwarePlugin}. * Registered {@link ShutdownAwarePlugin}.
*/ */
private final List<ShutdownAwarePlugin> shutdownAwarePluginList = new ArrayList<>(); private final List<ShutdownAwarePlugin> shutdownAwarePluginList = new CopyOnWriteArrayList<>();
/** /**
* Clear all. * Clear all.
@ -94,7 +114,7 @@ public class DefaultThreadPoolPluginManager implements ThreadPoolPluginManager {
writeLock.lock(); writeLock.lock();
try { try {
String id = plugin.getId(); String id = plugin.getId();
Assert.isTrue(!isRegistered(id), "The plug-in with id [" + id + "] has been registered"); Assert.isTrue(!isRegistered(id), "The plugin with id [" + id + "] has been registered");
// register plugin // register plugin
registeredPlugins.put(id, plugin); registeredPlugins.put(id, plugin);
@ -171,6 +191,13 @@ public class DefaultThreadPoolPluginManager implements ThreadPoolPluginManager {
} }
} }
/**
* Get all registered plugins.
*
* @return plugins
* @apiNote Be sure to avoid directly modifying returned collection instances,
* otherwise, unexpected results may be obtained through the manager
*/
@Override @Override
public Collection<ThreadPoolPlugin> getAllPlugins() { public Collection<ThreadPoolPlugin> getAllPlugins() {
Lock readLock = instanceLock.readLock(); Lock readLock = instanceLock.readLock();
@ -238,6 +265,8 @@ public class DefaultThreadPoolPluginManager implements ThreadPoolPluginManager {
* Get rejected plugin list. * Get rejected plugin list.
* *
* @return {@link RejectedAwarePlugin} * @return {@link RejectedAwarePlugin}
* @apiNote Be sure to avoid directly modifying returned collection instances,
* otherwise, unexpected results may be obtained through the manager
*/ */
@Override @Override
public Collection<RejectedAwarePlugin> getRejectedAwarePluginList() { public Collection<RejectedAwarePlugin> getRejectedAwarePluginList() {
@ -254,6 +283,8 @@ public class DefaultThreadPoolPluginManager implements ThreadPoolPluginManager {
* Get shutdown plugin list. * Get shutdown plugin list.
* *
* @return {@link ShutdownAwarePlugin} * @return {@link ShutdownAwarePlugin}
* @apiNote Be sure to avoid directly modifying returned collection instances,
* otherwise, unexpected results may be obtained through the manager
*/ */
@Override @Override
public Collection<ShutdownAwarePlugin> getShutdownAwarePluginList() { public Collection<ShutdownAwarePlugin> getShutdownAwarePluginList() {
@ -270,6 +301,8 @@ public class DefaultThreadPoolPluginManager implements ThreadPoolPluginManager {
* Get shutdown plugin list. * Get shutdown plugin list.
* *
* @return {@link ShutdownAwarePlugin} * @return {@link ShutdownAwarePlugin}
* @apiNote Be sure to avoid directly modifying returned collection instances,
* otherwise, unexpected results may be obtained through the manager
*/ */
@Override @Override
public Collection<TaskAwarePlugin> getTaskAwarePluginList() { public Collection<TaskAwarePlugin> getTaskAwarePluginList() {

Loading…
Cancel
Save