Handful of small fixes.

* Fix material loading from config requiring minecraft: namespace.
* Fix disallowed-lightning blocks. Lightning entities are weird.
* Fix /wg reload overwriting config changes.
* General around thread usage/naming/shutdown. If anyone was actually
  making their own managers/indices you deserve to break.
This commit is contained in:
wizjany 2019-07-31 18:34:11 -04:00
parent b7ad0257b7
commit 3468e3d47e
15 changed files with 137 additions and 71 deletions

View File

@ -60,7 +60,6 @@ public void copyDefaults() {
@Override @Override
public void unload() { public void unload() {
worlds.clear(); worlds.clear();
getConfig().save();
} }
@Override @Override
@ -69,6 +68,7 @@ public void postLoad() {
for (World world : WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getWorlds()) { for (World world : WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS).getWorlds()) {
get(world); get(world);
} }
getConfig().save();
} }
/** /**

View File

@ -97,6 +97,10 @@ public void onChunkUnload(ChunkUnloadEvent event) {
Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, cache::invalidateAll, CACHE_INVALIDATION_INTERVAL, CACHE_INVALIDATION_INTERVAL); Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, cache::invalidateAll, CACHE_INVALIDATION_INTERVAL, CACHE_INVALIDATION_INTERVAL);
} }
public void shutdown() {
container.shutdown();
}
@Override @Override
@Nullable @Nullable
protected RegionManager load(World world) { protected RegionManager load(World world) {

View File

@ -130,7 +130,7 @@ public void load() {
@Override @Override
public void unload() { public void unload() {
configuration.unload(); configuration.unload();
regionContainer.unload(); regionContainer.shutdown();
} }
@Override @Override

View File

@ -594,7 +594,7 @@ public void onBlockFromTo(BlockFromToEvent event) {
Material toType = to.getType(); Material toType = to.getType();
// Liquids pass this event when flowing to solid blocks // Liquids pass this event when flowing to solid blocks
if (toType.isSolid() && Materials.isLiquid(fromType)) { if (Materials.isLiquid(fromType) && toType.isSolid()) {
return; return;
} }

View File

@ -29,6 +29,8 @@
import com.sk89q.worldguard.protection.flags.StateFlag; import com.sk89q.worldguard.protection.flags.StateFlag;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@ -93,8 +95,12 @@ public void onLightningStrike(LightningStrikeEvent event) {
ConfigurationManager cfg = WorldGuard.getInstance().getPlatform().getGlobalStateManager(); ConfigurationManager cfg = WorldGuard.getInstance().getPlatform().getGlobalStateManager();
WorldConfiguration wcfg = cfg.get(BukkitAdapter.adapt(event.getWorld())); WorldConfiguration wcfg = cfg.get(BukkitAdapter.adapt(event.getWorld()));
if (wcfg.disallowedLightningBlocks.size() > 0) { if (!wcfg.disallowedLightningBlocks.isEmpty()) {
Material targetId = event.getLightning().getLocation().getBlock().getType(); final Block target = event.getLightning().getLocation().getBlock();
Material targetId = target.getType();
if (targetId == Material.AIR) {
targetId = target.getRelative(BlockFace.DOWN).getType();
}
if (wcfg.disallowedLightningBlocks.contains(BukkitAdapter.asBlockType(targetId).getId())) { if (wcfg.disallowedLightningBlocks.contains(BukkitAdapter.asBlockType(targetId).getId())) {
event.setCancelled(true); event.setCancelled(true);
} }

View File

@ -77,7 +77,8 @@ private WorldGuard() {
} }
public void setup() { public void setup() {
executorService = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 20)); executorService = MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 1, 20,
"WorldGuard Task Executor - %s"));
File cacheDir = new File(getPlatform().getConfigDir().toFile(), "cache"); File cacheDir = new File(getPlatform().getConfigDir().toFile(), "cache");
cacheDir.mkdirs(); cacheDir.mkdirs();

View File

@ -19,15 +19,21 @@
package com.sk89q.worldguard.config; package com.sk89q.worldguard.config;
import com.sk89q.worldedit.util.report.Unreported;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.entity.EntityType; import com.sk89q.worldedit.world.entity.EntityType;
import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.item.ItemTypes;
import com.sk89q.worldedit.world.registry.LegacyMapper; import com.sk89q.worldedit.world.registry.LegacyMapper;
import com.sk89q.worldguard.LocalPlayer; import com.sk89q.worldguard.LocalPlayer;
import com.sk89q.worldguard.blacklist.Blacklist; import com.sk89q.worldguard.blacklist.Blacklist;
import com.sk89q.worldedit.util.report.Unreported;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -55,7 +61,8 @@ public abstract class WorldConfiguration {
protected File blacklistFile; protected File blacklistFile;
@Unreported protected Blacklist blacklist; @Unreported
protected Blacklist blacklist;
public boolean boundedLocationFlags; public boolean boundedLocationFlags;
public boolean useRegions; public boolean useRegions;
@ -173,49 +180,63 @@ public Blacklist getBlacklist() {
} }
public List<String> convertLegacyItems(List<String> legacyItems) { public List<String> convertLegacyItems(List<String> legacyItems) {
return legacyItems.stream().map(this::convertLegacyItem).collect(Collectors.toList()); return legacyItems.stream().map(this::convertLegacyItem).filter(Objects::nonNull).collect(Collectors.toList());
} }
public String convertLegacyItem(String legacy) { public String convertLegacyItem(String legacy) {
String item = legacy; String[] splitter = legacy.split(":", 2);
try { try {
String[] splitter = item.split(":", 2); int id;
int id = 0; byte data;
byte data = 0;
if (splitter.length == 1) { if (splitter.length == 1) {
id = Integer.parseInt(item); id = Integer.parseInt(splitter[0]);
data = 0;
} else { } else {
id = Integer.parseInt(splitter[0]); id = Integer.parseInt(splitter[0]);
data = Byte.parseByte(splitter[1]); data = Byte.parseByte(splitter[1]);
} }
item = LegacyMapper.getInstance().getItemFromLegacy(id, data).getId(); ItemType legacyItem = LegacyMapper.getInstance().getItemFromLegacy(id, data);
} catch (Throwable e) { if (legacyItem != null) {
return legacyItem.getId();
}
} catch (NumberFormatException ignored) {
}
final ItemType itemType = ItemTypes.get(legacy);
if (itemType != null) {
return itemType.getId();
} }
return item; return null;
} }
public List<String> convertLegacyBlocks(List<String> legacyBlocks) { public List<String> convertLegacyBlocks(List<String> legacyBlocks) {
return legacyBlocks.stream().map(this::convertLegacyBlock).collect(Collectors.toList()); return legacyBlocks.stream().map(this::convertLegacyBlock).filter(Objects::nonNull).collect(Collectors.toList());
} }
public String convertLegacyBlock(String legacy) { public String convertLegacyBlock(String legacy) {
String block = legacy; String[] splitter = legacy.split(":", 2);
try { try {
String[] splitter = block.split(":", 2); int id;
int id = 0; byte data;
byte data = 0;
if (splitter.length == 1) { if (splitter.length == 1) {
id = Integer.parseInt(block); id = Integer.parseInt(splitter[0]);
data = 0;
} else { } else {
id = Integer.parseInt(splitter[0]); id = Integer.parseInt(splitter[0]);
data = Byte.parseByte(splitter[1]); data = Byte.parseByte(splitter[1]);
} }
block = LegacyMapper.getInstance().getBlockFromLegacy(id, data).getBlockType().getId(); BlockState legacyBlock = LegacyMapper.getInstance().getBlockFromLegacy(id, data);
} catch (Throwable e) { if (legacyBlock != null) {
return legacyBlock.getBlockType().getId();
}
} catch (NumberFormatException ignored) {
}
final BlockType blockType = BlockTypes.get(legacy);
if (blockType != null) {
return blockType.getId();
} }
return block; return null;
} }
public int getMaxRegionCount(LocalPlayer player) { public int getMaxRegionCount(LocalPlayer player) {

View File

@ -19,6 +19,8 @@
package com.sk89q.worldguard.protection.managers; package com.sk89q.worldguard.protection.managers;
import static com.google.common.base.Preconditions.checkNotNull;
import com.sk89q.worldguard.protection.flags.registry.FlagRegistry; import com.sk89q.worldguard.protection.flags.registry.FlagRegistry;
import com.sk89q.worldguard.protection.managers.index.ChunkHashTable; import com.sk89q.worldguard.protection.managers.index.ChunkHashTable;
import com.sk89q.worldguard.protection.managers.index.ConcurrentRegionIndex; import com.sk89q.worldguard.protection.managers.index.ConcurrentRegionIndex;
@ -29,15 +31,22 @@
import com.sk89q.worldguard.util.Normal; import com.sk89q.worldguard.util.Normal;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.*; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier; import java.util.function.Function;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import static com.google.common.base.Preconditions.checkNotNull;
/** /**
* Manages different {@link RegionManager}s for different worlds or dimensions. * Manages different {@link RegionManager}s for different worlds or dimensions.
* *
@ -52,8 +61,8 @@ public class RegionContainerImpl {
private final ConcurrentMap<Normal, RegionManager> mapping = new ConcurrentHashMap<>(); private final ConcurrentMap<Normal, RegionManager> mapping = new ConcurrentHashMap<>();
private final Object lock = new Object(); private final Object lock = new Object();
private final RegionDriver driver; private final RegionDriver driver;
private final Supplier<? extends ConcurrentRegionIndex> indexFactory = new ChunkHashTable.Factory(new PriorityRTreeIndex.Factory()); private final Function<String, ? extends ConcurrentRegionIndex> indexFactory = new ChunkHashTable.Factory(new PriorityRTreeIndex.Factory());
private final Timer timer = new Timer(); private final Timer timer = new Timer("WorldGuard Region I/O");
private final FlagRegistry flagRegistry; private final FlagRegistry flagRegistry;
private final Set<Normal> failingLoads = new HashSet<>(); private final Set<Normal> failingLoads = new HashSet<>();
@ -182,6 +191,14 @@ public void unloadAll() {
} }
} }
/**
* Disable completely.
*/
public void shutdown() {
timer.cancel();
unloadAll();
}
/** /**
* Get the region manager for the given world name. * Get the region manager for the given world name.
* *

View File

@ -37,6 +37,7 @@
import com.sk89q.worldguard.protection.util.RegionCollectionConsumer; import com.sk89q.worldguard.protection.util.RegionCollectionConsumer;
import com.sk89q.worldguard.util.Normal; import com.sk89q.worldguard.util.Normal;
import javax.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -47,9 +48,7 @@
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier; import java.util.function.Function;
import javax.annotation.Nullable;
/** /**
* A region manager holds the regions for a world. * A region manager holds the regions for a world.
@ -57,7 +56,7 @@
public final class RegionManager { public final class RegionManager {
private final RegionDatabase store; private final RegionDatabase store;
private final Supplier<? extends ConcurrentRegionIndex> indexFactory; private final Function<String, ? extends ConcurrentRegionIndex> indexFactory;
private final FlagRegistry flagRegistry; private final FlagRegistry flagRegistry;
private ConcurrentRegionIndex index; private ConcurrentRegionIndex index;
@ -68,14 +67,14 @@ public final class RegionManager {
* @param indexFactory the factory for creating new instances of the index * @param indexFactory the factory for creating new instances of the index
* @param flagRegistry the flag registry * @param flagRegistry the flag registry
*/ */
public RegionManager(RegionDatabase store, Supplier<? extends ConcurrentRegionIndex> indexFactory, FlagRegistry flagRegistry) { public RegionManager(RegionDatabase store, Function<String, ? extends ConcurrentRegionIndex> indexFactory, FlagRegistry flagRegistry) {
checkNotNull(store); checkNotNull(store);
checkNotNull(indexFactory); checkNotNull(indexFactory);
checkNotNull(flagRegistry, "flagRegistry"); checkNotNull(flagRegistry, "flagRegistry");
this.store = store; this.store = store;
this.indexFactory = indexFactory; this.indexFactory = indexFactory;
this.index = indexFactory.get(); this.index = indexFactory.apply(store.getName());
this.flagRegistry = flagRegistry; this.flagRegistry = flagRegistry;
} }
@ -218,7 +217,7 @@ public void setRegions(Map<String, ProtectedRegion> regions) {
public void setRegions(Collection<ProtectedRegion> regions) { public void setRegions(Collection<ProtectedRegion> regions) {
checkNotNull(regions); checkNotNull(regions);
ConcurrentRegionIndex newIndex = indexFactory.get(); ConcurrentRegionIndex newIndex = indexFactory.apply(getName());
newIndex.addAll(regions); newIndex.addAll(regions);
newIndex.getAndClearDifference(); // Clear changes newIndex.getAndClearDifference(); // Clear changes
this.index = newIndex; this.index = newIndex;

View File

@ -41,8 +41,8 @@
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -52,6 +52,7 @@
*/ */
public class ChunkHashTable implements ConcurrentRegionIndex { public class ChunkHashTable implements ConcurrentRegionIndex {
private final String name;
private ListeningExecutorService executor = createExecutor(); private ListeningExecutorService executor = createExecutor();
private LongHashTable<ChunkState> states = new LongHashTable<>(); private LongHashTable<ChunkState> states = new LongHashTable<>();
private final RegionIndex index; private final RegionIndex index;
@ -63,10 +64,12 @@ public class ChunkHashTable implements ConcurrentRegionIndex {
* Create a new instance. * Create a new instance.
* *
* @param index the index * @param index the index
* @param name
*/ */
public ChunkHashTable(RegionIndex index) { public ChunkHashTable(RegionIndex index, String name) {
checkNotNull(index); checkNotNull(index);
this.index = index; this.index = index;
this.name = name;
} }
/** /**
@ -75,8 +78,8 @@ public ChunkHashTable(RegionIndex index) {
* @return an executor service * @return an executor service
*/ */
private ListeningExecutorService createExecutor() { private ListeningExecutorService createExecutor() {
return MoreExecutors.listeningDecorator( return MoreExecutors.listeningDecorator(EvenMoreExecutors.newBoundedCachedThreadPool(0, 4, Integer.MAX_VALUE,
EvenMoreExecutors.newBoundedCachedThreadPool(0, 4, Integer.MAX_VALUE)); "WorldGuard Region Chunk Table - " + name));
} }
/** /**
@ -361,17 +364,17 @@ public boolean isLoaded() {
/** /**
* A factory for instances of {@code ChunkHashCache}. * A factory for instances of {@code ChunkHashCache}.
*/ */
public static class Factory implements Supplier<ChunkHashTable> { public static class Factory implements Function<String, ChunkHashTable> {
private final Supplier<? extends ConcurrentRegionIndex> supplier; private final Function<String, ? extends ConcurrentRegionIndex> supplier;
public Factory(Supplier<? extends ConcurrentRegionIndex> supplier) { public Factory(Function<String, ? extends ConcurrentRegionIndex> supplier) {
checkNotNull(supplier); checkNotNull(supplier);
this.supplier = supplier; this.supplier = supplier;
} }
@Override @Override
public ChunkHashTable get() { public ChunkHashTable apply(String name) {
return new ChunkHashTable(supplier.get()); return new ChunkHashTable(supplier.apply(name), name);
} }
} }

View File

@ -28,6 +28,7 @@
import com.sk89q.worldguard.protection.managers.RemovalStrategy; import com.sk89q.worldguard.protection.managers.RemovalStrategy;
import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import javax.annotation.Nullable;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
@ -35,10 +36,8 @@
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.annotation.Nullable;
/** /**
* An index that stores regions in a hash map, which allows for fast lookup * An index that stores regions in a hash map, which allows for fast lookup
@ -287,9 +286,9 @@ public void setDirty(boolean dirty) {
/** /**
* A factory for new instances using this index. * A factory for new instances using this index.
*/ */
public static final class Factory implements Supplier<HashMapIndex> { public static final class Factory implements Function<String, HashMapIndex> {
@Override @Override
public HashMapIndex get() { public HashMapIndex apply(String name) {
return new HashMapIndex(); return new HashMapIndex();
} }
} }

View File

@ -30,8 +30,8 @@
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.Supplier;
/** /**
* An implementation of an index that uses {@link HashMapIndex} for queries * An implementation of an index that uses {@link HashMapIndex} for queries
@ -103,9 +103,9 @@ public void applyIntersecting(ProtectedRegion region, Predicate<ProtectedRegion>
/** /**
* A factory for new instances using this index. * A factory for new instances using this index.
*/ */
public static final class Factory implements Supplier<PriorityRTreeIndex> { public static final class Factory implements Function<String, PriorityRTreeIndex> {
@Override @Override
public PriorityRTreeIndex get() { public PriorityRTreeIndex apply(String name) {
return new PriorityRTreeIndex(); return new PriorityRTreeIndex();
} }
} }

View File

@ -31,7 +31,12 @@
import com.sk89q.worldguard.protection.regions.ProtectedRegion; import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level; import java.util.logging.Level;
@ -47,7 +52,7 @@ public class UUIDMigration extends AbstractMigration {
private static final Logger log = Logger.getLogger(UUIDMigration.class.getCanonicalName()); private static final Logger log = Logger.getLogger(UUIDMigration.class.getCanonicalName());
private static final int LOG_DELAY = 5000; private static final int LOG_DELAY = 5000;
private final Timer timer = new Timer(); private final Timer timer = new Timer("WorldGuard UUID Migration");
private final ProfileService profileService; private final ProfileService profileService;
private final FlagRegistry flagRegistry; private final FlagRegistry flagRegistry;
private final ConcurrentMap<String, UUID> resolvedNames = new ConcurrentHashMap<>(); private final ConcurrentMap<String, UUID> resolvedNames = new ConcurrentHashMap<>();

View File

@ -41,7 +41,6 @@
import com.sk89q.worldguard.session.handler.WaterBreathing; import com.sk89q.worldguard.session.handler.WaterBreathing;
import com.sk89q.worldguard.session.handler.WeatherLockFlag; import com.sk89q.worldguard.session.handler.WeatherLockFlag;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Arrays; import java.util.Arrays;
@ -61,25 +60,18 @@ public abstract class AbstractSessionManager implements SessionManager {
private final LoadingCache<WorldPlayerTuple, Boolean> bypassCache = CacheBuilder.newBuilder() private final LoadingCache<WorldPlayerTuple, Boolean> bypassCache = CacheBuilder.newBuilder()
.maximumSize(1000) .maximumSize(1000)
.expireAfterAccess(2, TimeUnit.SECONDS) .expireAfterAccess(2, TimeUnit.SECONDS)
.build(new CacheLoader<WorldPlayerTuple, Boolean>() { .build(CacheLoader.from(tuple ->
@Override tuple.getPlayer().hasPermission("worldguard.region.bypass." + tuple.getWorld().getName())));
public Boolean load(@Nonnull WorldPlayerTuple tuple) throws Exception {
return tuple.getPlayer().hasPermission("worldguard.region.bypass." + tuple.getWorld().getName());
}
});
private final LoadingCache<CacheKey, Session> sessions = CacheBuilder.newBuilder() private final LoadingCache<CacheKey, Session> sessions = CacheBuilder.newBuilder()
.expireAfterAccess(SESSION_LIFETIME, TimeUnit.MINUTES) .expireAfterAccess(SESSION_LIFETIME, TimeUnit.MINUTES)
.build(new CacheLoader<CacheKey, Session>() { .build(CacheLoader.from(key ->
@Override createSession(key.playerRef.get())));
public Session load(@Nonnull CacheKey key) throws Exception {
return createSession(key.playerRef.get());
}
});
private List<Handler.Factory<? extends Handler>> handlers = new LinkedList<>(); private List<Handler.Factory<? extends Handler>> handlers = new LinkedList<>();
private static final List<Handler.Factory<? extends Handler>> defaultHandlers = new LinkedList<>(); private static final List<Handler.Factory<? extends Handler>> defaultHandlers = new LinkedList<>();
static { static {
Handler.Factory<?>[] factories = { Handler.Factory<?>[] factories = {
HealFlag.FACTORY, HealFlag.FACTORY,

View File

@ -19,6 +19,8 @@
package com.sk89q.worldguard.util.concurrent; package com.sk89q.worldguard.util.concurrent;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
@ -43,11 +45,28 @@ private EvenMoreExecutors() {
* @return the newly created thread pool * @return the newly created thread pool
*/ */
public static ExecutorService newBoundedCachedThreadPool(int minThreads, int maxThreads, int queueSize) { public static ExecutorService newBoundedCachedThreadPool(int minThreads, int maxThreads, int queueSize) {
return newBoundedCachedThreadPool(minThreads, maxThreads, queueSize, null);}
/**
* Creates a thread pool that creates new threads as needed up to
* a maximum number of threads, but will reuse previously constructed
* threads when they are available.
*
* @param minThreads the minimum number of threads to have at a given time
* @param maxThreads the maximum number of threads to have at a given time
* @param queueSize the size of the queue before new submissions are rejected
* @param threadFormat thread name formatter
* @return the newly created thread pool
*/
public static ExecutorService newBoundedCachedThreadPool(int minThreads, int maxThreads, int queueSize, String threadFormat) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
minThreads, maxThreads, minThreads, maxThreads,
60L, TimeUnit.SECONDS, 60L, TimeUnit.SECONDS,
new LinkedBlockingDeque<>(queueSize)); new LinkedBlockingDeque<>(queueSize));
threadPoolExecutor.allowCoreThreadTimeOut(true); threadPoolExecutor.allowCoreThreadTimeOut(true);
if (threadFormat != null) {
threadPoolExecutor.setThreadFactory(new ThreadFactoryBuilder().setNameFormat(threadFormat).build());
}
return threadPoolExecutor; return threadPoolExecutor;
} }