Better L2jFree based ThreadPool.
Adapted from: L2jUnity free files.
This commit is contained in:
parent
0c201f75e7
commit
c071f2a1bc
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
public interface IThreadPoolInitializer
|
||||
{
|
||||
int getScheduledThreadPoolSize();
|
||||
|
||||
int getThreadPoolSize();
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class RejectedExecutionHandlerImpl implements RejectedExecutionHandler
|
||||
{
|
||||
private static final Logger LOGGER = Logger.getLogger(RejectedExecutionHandlerImpl.class.getName());
|
||||
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
|
||||
{
|
||||
if (executor.isShutdown())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.warning(r + " from " + executor + " " + new RejectedExecutionException());
|
||||
|
||||
if (Thread.currentThread().getPriority() > Thread.NORM_PRIORITY)
|
||||
{
|
||||
new Thread(r).start();
|
||||
}
|
||||
else
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
|
||||
/**
|
||||
* @author UnAfraid
|
||||
*/
|
||||
public final class RunnableWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public RunnableWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
}
|
||||
catch (final Throwable e)
|
||||
{
|
||||
final Thread t = Thread.currentThread();
|
||||
final UncaughtExceptionHandler h = t.getUncaughtExceptionHandler();
|
||||
if (h != null)
|
||||
{
|
||||
h.uncaughtException(t, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,282 +18,250 @@ package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* This class handles thread pooling system. It relies on two ThreadPoolExecutor arrays, which poolers number is generated using config.
|
||||
* <p>
|
||||
* Those arrays hold following pools :
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Scheduled pool keeps a track about incoming, future events.</li>
|
||||
* <li>Instant pool handles short-life events.</li>
|
||||
* </ul>
|
||||
* @author _dev_ (savormix)
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class ThreadPool
|
||||
{
|
||||
protected static final Logger LOG = Logger.getLogger(ThreadPool.class.getName());
|
||||
private static final Logger LOGGER = Logger.getLogger(ThreadPool.class.getName());
|
||||
|
||||
private static final long MAX_DELAY = TimeUnit.NANOSECONDS.toMillis(Long.MAX_VALUE - System.nanoTime()) / 2;
|
||||
private static ScheduledThreadPoolExecutor SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
private static ThreadPoolExecutor THREAD_POOL_EXECUTOR;
|
||||
|
||||
private static int _threadPoolRandomizer;
|
||||
|
||||
protected static ScheduledThreadPoolExecutor[] _scheduledPools;
|
||||
protected static ThreadPoolExecutor[] _instantPools;
|
||||
|
||||
/**
|
||||
* Init the different pools, based on Config. It is launched only once, on Gameserver instance.
|
||||
*/
|
||||
public static void init()
|
||||
public static void initThreadPools(IThreadPoolInitializer initializer) throws Exception
|
||||
{
|
||||
// Feed scheduled pool.
|
||||
int poolCount = Config.SCHEDULED_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR != null) || (THREAD_POOL_EXECUTOR != null))
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
throw new Exception("The thread pool has been already initialized!");
|
||||
}
|
||||
|
||||
_scheduledPools = new ScheduledThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_scheduledPools[i] = new ScheduledThreadPoolExecutor(Config.THREADS_PER_SCHEDULED_THREAD_POOL);
|
||||
}
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR = new ScheduledThreadPoolExecutor(initializer.getScheduledThreadPoolSize(), new PoolThreadFactory("L2JU-SP-", Thread.NORM_PRIORITY));
|
||||
THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(initializer.getThreadPoolSize(), initializer.getThreadPoolSize(), 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(), new PoolThreadFactory("L2JU-IT-", Thread.NORM_PRIORITY));
|
||||
|
||||
// Feed instant pool.
|
||||
poolCount = Config.INSTANT_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
tp.setRejectedExecutionHandler(new RejectedExecutionHandlerImpl());
|
||||
tp.prestartAllCoreThreads();
|
||||
});
|
||||
|
||||
_instantPools = new ThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_instantPools[i] = new ThreadPoolExecutor(Config.THREADS_PER_INSTANT_THREAD_POOL, Config.THREADS_PER_INSTANT_THREAD_POOL, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100000));
|
||||
}
|
||||
scheduleAtFixedRate(ThreadPool::purge, 60000, 60000); // Repeats every one minute.
|
||||
|
||||
// Prestart core threads.
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
// Launch purge task.
|
||||
scheduleAtFixedRate(() ->
|
||||
{
|
||||
purge();
|
||||
}, 600000, 600000);
|
||||
|
||||
LOG.info("ThreadPoolManager: Initialized " + getPoolSize(_instantPools) + "/" + getMaximumPoolSize(_instantPools) + " instant thread(s).");
|
||||
LOG.info("ThreadPoolManager: Initialized " + getPoolSize(_scheduledPools) + "/" + getMaximumPoolSize(_scheduledPools) + " scheduled thread(s).");
|
||||
}
|
||||
|
||||
public static void purge()
|
||||
{
|
||||
for (ScheduledThreadPoolExecutor threadPool1 : _scheduledPools)
|
||||
{
|
||||
threadPool1.purge();
|
||||
}
|
||||
for (ThreadPoolExecutor threadPool2 : _instantPools)
|
||||
{
|
||||
threadPool2.purge();
|
||||
}
|
||||
LOGGER.info("ThreadPool: Initialized with");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + "/" + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + " scheduled thread(s)."); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getPoolSize() + "/" + THREAD_POOL_EXECUTOR.getMaximumPoolSize() + " thread(s).");
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a one-shot action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion.
|
||||
* Gets the scheduled thread pool executor.
|
||||
* @return the scheduled thread pool executor
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable r, long delay)
|
||||
public static ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).schedule(new TaskWrapper(r), validate(delay), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a periodic action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @param period : the period between successive executions.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will throw an exception upon cancellation.
|
||||
* Gets the thread pool executor.
|
||||
* @return the thread pool executor
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable r, long delay, long period)
|
||||
public static ThreadPoolExecutor getThreadPoolExecutor()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).scheduleAtFixedRate(new TaskWrapper(r), validate(delay), validate(period), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a stream of all the thread pools.
|
||||
* @return the stream of all the thread pools
|
||||
*/
|
||||
public static Stream<ThreadPoolExecutor> getThreadPools()
|
||||
{
|
||||
return Stream.of(SCHEDULED_THREAD_POOL_EXECUTOR, THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable task, long delay)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.schedule(new RunnableWrapper(task), delay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay at fixed rate in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleAtFixedRate(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay with fixed delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleWithFixedDelay(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given task sometime in the future.
|
||||
* @param r : the task to execute.
|
||||
* @param task the task to execute
|
||||
*/
|
||||
public static void execute(Runnable r)
|
||||
public static void execute(Runnable task)
|
||||
{
|
||||
try
|
||||
{
|
||||
getPool(_instantPools).execute(new TaskWrapper(r));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getStats()
|
||||
{
|
||||
List<String> stats = new ArrayList<>();
|
||||
for (int i = 0; i < _scheduledPools.length; i++)
|
||||
{
|
||||
final ScheduledThreadPoolExecutor threadPool = _scheduledPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
for (int i = 0; i < _instantPools.length; i++)
|
||||
{
|
||||
final ThreadPoolExecutor threadPool = _instantPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
return stats.toArray(new String[stats.size()]);
|
||||
THREAD_POOL_EXECUTOR.execute(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown thread pooling system correctly. Send different informations.
|
||||
* Submits a Runnable task for execution and returns a Future representing that task. The Future's get method will return null upon successful completion.
|
||||
* @param task the task to submit
|
||||
* @return a Future representing pending completion of the task
|
||||
*/
|
||||
public static Future<?> submit(Runnable task)
|
||||
{
|
||||
return THREAD_POOL_EXECUTOR.submit(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges all thread pools.
|
||||
*/
|
||||
public static void purge()
|
||||
{
|
||||
getThreadPools().forEach(ThreadPoolExecutor::purge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thread pools stats.
|
||||
* @return the stats
|
||||
*/
|
||||
public static List<String> getStats()
|
||||
{
|
||||
final List<String> list = new ArrayList<>(23);
|
||||
list.add("");
|
||||
list.add("Scheduled pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + SCHEDULED_THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize()); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
list.add("getCompletedTaskCount: " + SCHEDULED_THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + SCHEDULED_THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
list.add("Thread pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + THREAD_POOL_EXECUTOR.getMaximumPoolSize());
|
||||
list.add("getCompletedTaskCount: " + THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdowns the thread pools waiting for tasks to finish.
|
||||
*/
|
||||
public static void shutdown()
|
||||
{
|
||||
try
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR == null) && (THREAD_POOL_EXECUTOR == null))
|
||||
{
|
||||
LOG.info("ThreadPoolManager: Shutting down.");
|
||||
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T> : The pool type.
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the less fed pool.
|
||||
*/
|
||||
private static <T> T getPool(T[] threadPools)
|
||||
{
|
||||
return threadPools[_threadPoolRandomizer++ % threadPools.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delay : The delay to validate.
|
||||
* @return a secured value, from 0 to MAX_DELAY.
|
||||
*/
|
||||
private static long validate(long delay)
|
||||
{
|
||||
return Math.max(0, Math.min(MAX_DELAY, delay));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall actual pools size.
|
||||
*/
|
||||
private static long getPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getPoolSize();
|
||||
return;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall maximum pools size.
|
||||
*/
|
||||
private static long getMaximumPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getMaximumPoolSize();
|
||||
}
|
||||
LOGGER.info("ThreadPool: Shutting down.");
|
||||
LOGGER.info("...executing " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks.");
|
||||
LOGGER.info("...executing " + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final class TaskWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public TaskWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
tp.shutdown();
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOG.warning("Exception in " + _runnable.getClass().getName() + " execution: " + e.getMessage());
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
});
|
||||
|
||||
getThreadPools().forEach(t ->
|
||||
{
|
||||
try
|
||||
{
|
||||
t.awaitTermination(15, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
LOGGER.warning("" + e);
|
||||
}
|
||||
});
|
||||
|
||||
if (!SCHEDULED_THREAD_POOL_EXECUTOR.isTerminated())
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
|
||||
try
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.awaitTermination(5, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("...success: " + getThreadPools().allMatch(ThreadPoolExecutor::isTerminated) + " in " + (System.currentTimeMillis() - startTime) + " ms.");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks left.");
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks left.");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PoolThreadFactory implements ThreadFactory
|
||||
{
|
||||
private final String _prefix;
|
||||
private final int _priority;
|
||||
private final AtomicInteger _threadId = new AtomicInteger();
|
||||
|
||||
public PoolThreadFactory(String prefix, int priority)
|
||||
{
|
||||
_prefix = prefix;
|
||||
_priority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r)
|
||||
{
|
||||
final Thread thread = new Thread(r, _prefix + _threadId.incrementAndGet());
|
||||
thread.setPriority(_priority);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,18 +173,19 @@ public class GameServer
|
||||
{
|
||||
final long serverLoadStart = System.currentTimeMillis();
|
||||
|
||||
printSection("ThreadPool");
|
||||
ThreadPool.initThreadPools(new GameThreadPools());
|
||||
|
||||
printSection("IdFactory");
|
||||
if (!IdFactory.getInstance().isInitialized())
|
||||
{
|
||||
LOGGER.severe(getClass().getSimpleName() + ": Could not read object IDs from database. Please check your configuration.");
|
||||
throw new Exception("Could not initialize the ID factory");
|
||||
}
|
||||
|
||||
printSection("ThreadPool");
|
||||
ThreadPool.init();
|
||||
EventDispatcher.getInstance();
|
||||
|
||||
// load script engines
|
||||
printSection("Scripting Engines");
|
||||
EventDispatcher.getInstance();
|
||||
ScriptEngineManager.getInstance();
|
||||
|
||||
printSection("Telnet");
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.commons.concurrent.IThreadPoolInitializer;
|
||||
|
||||
/**
|
||||
* @author nos
|
||||
*/
|
||||
public final class GameThreadPools implements IThreadPoolInitializer
|
||||
{
|
||||
@Override
|
||||
public int getScheduledThreadPoolSize()
|
||||
{
|
||||
return Config.SCHEDULED_THREAD_POOL_COUNT != -1 ? Config.SCHEDULED_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_SCHEDULED_THREAD_POOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThreadPoolSize()
|
||||
{
|
||||
return Config.INSTANT_THREAD_POOL_COUNT != -1 ? Config.INSTANT_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_INSTANT_THREAD_POOL;
|
||||
}
|
||||
}
|
@ -109,7 +109,6 @@ Others:
|
||||
-Reworked drop system
|
||||
-Skill system from L2jUnity
|
||||
-GeoEngine from aCis
|
||||
-Threadpool manager from aCis
|
||||
|
||||
TODO:
|
||||
-Modify all skill levels/sublevels based on client
|
||||
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
public interface IThreadPoolInitializer
|
||||
{
|
||||
int getScheduledThreadPoolSize();
|
||||
|
||||
int getThreadPoolSize();
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class RejectedExecutionHandlerImpl implements RejectedExecutionHandler
|
||||
{
|
||||
private static final Logger LOGGER = Logger.getLogger(RejectedExecutionHandlerImpl.class.getName());
|
||||
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
|
||||
{
|
||||
if (executor.isShutdown())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.warning(r + " from " + executor + " " + new RejectedExecutionException());
|
||||
|
||||
if (Thread.currentThread().getPriority() > Thread.NORM_PRIORITY)
|
||||
{
|
||||
new Thread(r).start();
|
||||
}
|
||||
else
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
|
||||
/**
|
||||
* @author UnAfraid
|
||||
*/
|
||||
public final class RunnableWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public RunnableWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
}
|
||||
catch (final Throwable e)
|
||||
{
|
||||
final Thread t = Thread.currentThread();
|
||||
final UncaughtExceptionHandler h = t.getUncaughtExceptionHandler();
|
||||
if (h != null)
|
||||
{
|
||||
h.uncaughtException(t, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,282 +18,250 @@ package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* This class handles thread pooling system. It relies on two ThreadPoolExecutor arrays, which poolers number is generated using config.
|
||||
* <p>
|
||||
* Those arrays hold following pools :
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Scheduled pool keeps a track about incoming, future events.</li>
|
||||
* <li>Instant pool handles short-life events.</li>
|
||||
* </ul>
|
||||
* @author _dev_ (savormix)
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class ThreadPool
|
||||
{
|
||||
protected static final Logger LOG = Logger.getLogger(ThreadPool.class.getName());
|
||||
private static final Logger LOGGER = Logger.getLogger(ThreadPool.class.getName());
|
||||
|
||||
private static final long MAX_DELAY = TimeUnit.NANOSECONDS.toMillis(Long.MAX_VALUE - System.nanoTime()) / 2;
|
||||
private static ScheduledThreadPoolExecutor SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
private static ThreadPoolExecutor THREAD_POOL_EXECUTOR;
|
||||
|
||||
private static int _threadPoolRandomizer;
|
||||
|
||||
protected static ScheduledThreadPoolExecutor[] _scheduledPools;
|
||||
protected static ThreadPoolExecutor[] _instantPools;
|
||||
|
||||
/**
|
||||
* Init the different pools, based on Config. It is launched only once, on Gameserver instance.
|
||||
*/
|
||||
public static void init()
|
||||
public static void initThreadPools(IThreadPoolInitializer initializer) throws Exception
|
||||
{
|
||||
// Feed scheduled pool.
|
||||
int poolCount = Config.SCHEDULED_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR != null) || (THREAD_POOL_EXECUTOR != null))
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
throw new Exception("The thread pool has been already initialized!");
|
||||
}
|
||||
|
||||
_scheduledPools = new ScheduledThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_scheduledPools[i] = new ScheduledThreadPoolExecutor(Config.THREADS_PER_SCHEDULED_THREAD_POOL);
|
||||
}
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR = new ScheduledThreadPoolExecutor(initializer.getScheduledThreadPoolSize(), new PoolThreadFactory("L2JU-SP-", Thread.NORM_PRIORITY));
|
||||
THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(initializer.getThreadPoolSize(), initializer.getThreadPoolSize(), 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(), new PoolThreadFactory("L2JU-IT-", Thread.NORM_PRIORITY));
|
||||
|
||||
// Feed instant pool.
|
||||
poolCount = Config.INSTANT_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
tp.setRejectedExecutionHandler(new RejectedExecutionHandlerImpl());
|
||||
tp.prestartAllCoreThreads();
|
||||
});
|
||||
|
||||
_instantPools = new ThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_instantPools[i] = new ThreadPoolExecutor(Config.THREADS_PER_INSTANT_THREAD_POOL, Config.THREADS_PER_INSTANT_THREAD_POOL, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100000));
|
||||
}
|
||||
scheduleAtFixedRate(ThreadPool::purge, 60000, 60000); // Repeats every one minute.
|
||||
|
||||
// Prestart core threads.
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
// Launch purge task.
|
||||
scheduleAtFixedRate(() ->
|
||||
{
|
||||
purge();
|
||||
}, 600000, 600000);
|
||||
|
||||
LOG.info("ThreadPoolManager: Initialized " + getPoolSize(_instantPools) + "/" + getMaximumPoolSize(_instantPools) + " instant thread(s).");
|
||||
LOG.info("ThreadPoolManager: Initialized " + getPoolSize(_scheduledPools) + "/" + getMaximumPoolSize(_scheduledPools) + " scheduled thread(s).");
|
||||
}
|
||||
|
||||
public static void purge()
|
||||
{
|
||||
for (ScheduledThreadPoolExecutor threadPool1 : _scheduledPools)
|
||||
{
|
||||
threadPool1.purge();
|
||||
}
|
||||
for (ThreadPoolExecutor threadPool2 : _instantPools)
|
||||
{
|
||||
threadPool2.purge();
|
||||
}
|
||||
LOGGER.info("ThreadPool: Initialized with");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + "/" + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + " scheduled thread(s)."); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getPoolSize() + "/" + THREAD_POOL_EXECUTOR.getMaximumPoolSize() + " thread(s).");
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a one-shot action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion.
|
||||
* Gets the scheduled thread pool executor.
|
||||
* @return the scheduled thread pool executor
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable r, long delay)
|
||||
public static ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).schedule(new TaskWrapper(r), validate(delay), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a periodic action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @param period : the period between successive executions.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will throw an exception upon cancellation.
|
||||
* Gets the thread pool executor.
|
||||
* @return the thread pool executor
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable r, long delay, long period)
|
||||
public static ThreadPoolExecutor getThreadPoolExecutor()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).scheduleAtFixedRate(new TaskWrapper(r), validate(delay), validate(period), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a stream of all the thread pools.
|
||||
* @return the stream of all the thread pools
|
||||
*/
|
||||
public static Stream<ThreadPoolExecutor> getThreadPools()
|
||||
{
|
||||
return Stream.of(SCHEDULED_THREAD_POOL_EXECUTOR, THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable task, long delay)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.schedule(new RunnableWrapper(task), delay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay at fixed rate in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleAtFixedRate(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay with fixed delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleWithFixedDelay(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given task sometime in the future.
|
||||
* @param r : the task to execute.
|
||||
* @param task the task to execute
|
||||
*/
|
||||
public static void execute(Runnable r)
|
||||
public static void execute(Runnable task)
|
||||
{
|
||||
try
|
||||
{
|
||||
getPool(_instantPools).execute(new TaskWrapper(r));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getStats()
|
||||
{
|
||||
List<String> stats = new ArrayList<>();
|
||||
for (int i = 0; i < _scheduledPools.length; i++)
|
||||
{
|
||||
final ScheduledThreadPoolExecutor threadPool = _scheduledPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
for (int i = 0; i < _instantPools.length; i++)
|
||||
{
|
||||
final ThreadPoolExecutor threadPool = _instantPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
return stats.toArray(new String[stats.size()]);
|
||||
THREAD_POOL_EXECUTOR.execute(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown thread pooling system correctly. Send different informations.
|
||||
* Submits a Runnable task for execution and returns a Future representing that task. The Future's get method will return null upon successful completion.
|
||||
* @param task the task to submit
|
||||
* @return a Future representing pending completion of the task
|
||||
*/
|
||||
public static Future<?> submit(Runnable task)
|
||||
{
|
||||
return THREAD_POOL_EXECUTOR.submit(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges all thread pools.
|
||||
*/
|
||||
public static void purge()
|
||||
{
|
||||
getThreadPools().forEach(ThreadPoolExecutor::purge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thread pools stats.
|
||||
* @return the stats
|
||||
*/
|
||||
public static List<String> getStats()
|
||||
{
|
||||
final List<String> list = new ArrayList<>(23);
|
||||
list.add("");
|
||||
list.add("Scheduled pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + SCHEDULED_THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize()); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
list.add("getCompletedTaskCount: " + SCHEDULED_THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + SCHEDULED_THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
list.add("Thread pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + THREAD_POOL_EXECUTOR.getMaximumPoolSize());
|
||||
list.add("getCompletedTaskCount: " + THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdowns the thread pools waiting for tasks to finish.
|
||||
*/
|
||||
public static void shutdown()
|
||||
{
|
||||
try
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR == null) && (THREAD_POOL_EXECUTOR == null))
|
||||
{
|
||||
LOG.info("ThreadPoolManager: Shutting down.");
|
||||
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T> : The pool type.
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the less fed pool.
|
||||
*/
|
||||
private static <T> T getPool(T[] threadPools)
|
||||
{
|
||||
return threadPools[_threadPoolRandomizer++ % threadPools.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delay : The delay to validate.
|
||||
* @return a secured value, from 0 to MAX_DELAY.
|
||||
*/
|
||||
private static long validate(long delay)
|
||||
{
|
||||
return Math.max(0, Math.min(MAX_DELAY, delay));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall actual pools size.
|
||||
*/
|
||||
private static long getPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getPoolSize();
|
||||
return;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall maximum pools size.
|
||||
*/
|
||||
private static long getMaximumPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getMaximumPoolSize();
|
||||
}
|
||||
LOGGER.info("ThreadPool: Shutting down.");
|
||||
LOGGER.info("...executing " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks.");
|
||||
LOGGER.info("...executing " + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final class TaskWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public TaskWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
tp.shutdown();
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOG.warning("Exception in " + _runnable.getClass().getName() + " execution: " + e.getMessage());
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
});
|
||||
|
||||
getThreadPools().forEach(t ->
|
||||
{
|
||||
try
|
||||
{
|
||||
t.awaitTermination(15, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
LOGGER.warning("" + e);
|
||||
}
|
||||
});
|
||||
|
||||
if (!SCHEDULED_THREAD_POOL_EXECUTOR.isTerminated())
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
|
||||
try
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.awaitTermination(5, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("...success: " + getThreadPools().allMatch(ThreadPoolExecutor::isTerminated) + " in " + (System.currentTimeMillis() - startTime) + " ms.");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks left.");
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks left.");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PoolThreadFactory implements ThreadFactory
|
||||
{
|
||||
private final String _prefix;
|
||||
private final int _priority;
|
||||
private final AtomicInteger _threadId = new AtomicInteger();
|
||||
|
||||
public PoolThreadFactory(String prefix, int priority)
|
||||
{
|
||||
_prefix = prefix;
|
||||
_priority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r)
|
||||
{
|
||||
final Thread thread = new Thread(r, _prefix + _threadId.incrementAndGet());
|
||||
thread.setPriority(_priority);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,18 +177,19 @@ public class GameServer
|
||||
{
|
||||
final long serverLoadStart = System.currentTimeMillis();
|
||||
|
||||
printSection("ThreadPool");
|
||||
ThreadPool.initThreadPools(new GameThreadPools());
|
||||
|
||||
printSection("IdFactory");
|
||||
if (!IdFactory.getInstance().isInitialized())
|
||||
{
|
||||
LOGGER.severe(getClass().getSimpleName() + ": Could not read object IDs from database. Please check your configuration.");
|
||||
throw new Exception("Could not initialize the ID factory");
|
||||
}
|
||||
|
||||
printSection("ThreadPool");
|
||||
ThreadPool.init();
|
||||
EventDispatcher.getInstance();
|
||||
|
||||
// load script engines
|
||||
printSection("Scripting Engines");
|
||||
EventDispatcher.getInstance();
|
||||
ScriptEngineManager.getInstance();
|
||||
|
||||
printSection("Telnet");
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.commons.concurrent.IThreadPoolInitializer;
|
||||
|
||||
/**
|
||||
* @author nos
|
||||
*/
|
||||
public final class GameThreadPools implements IThreadPoolInitializer
|
||||
{
|
||||
@Override
|
||||
public int getScheduledThreadPoolSize()
|
||||
{
|
||||
return Config.SCHEDULED_THREAD_POOL_COUNT != -1 ? Config.SCHEDULED_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_SCHEDULED_THREAD_POOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThreadPoolSize()
|
||||
{
|
||||
return Config.INSTANT_THREAD_POOL_COUNT != -1 ? Config.INSTANT_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_INSTANT_THREAD_POOL;
|
||||
}
|
||||
}
|
@ -122,7 +122,6 @@ Others:
|
||||
-Reworked drop system
|
||||
-Skill system from L2jUnity
|
||||
-GeoEngine from aCis
|
||||
-Threadpool manager from aCis
|
||||
|
||||
TODO:
|
||||
-Provisional Clan Halls
|
||||
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
public interface IThreadPoolInitializer
|
||||
{
|
||||
int getScheduledThreadPoolSize();
|
||||
|
||||
int getThreadPoolSize();
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class RejectedExecutionHandlerImpl implements RejectedExecutionHandler
|
||||
{
|
||||
private static final Logger LOGGER = Logger.getLogger(RejectedExecutionHandlerImpl.class.getName());
|
||||
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
|
||||
{
|
||||
if (executor.isShutdown())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.warning(r + " from " + executor + " " + new RejectedExecutionException());
|
||||
|
||||
if (Thread.currentThread().getPriority() > Thread.NORM_PRIORITY)
|
||||
{
|
||||
new Thread(r).start();
|
||||
}
|
||||
else
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
|
||||
/**
|
||||
* @author UnAfraid
|
||||
*/
|
||||
public final class RunnableWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public RunnableWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
}
|
||||
catch (final Throwable e)
|
||||
{
|
||||
final Thread t = Thread.currentThread();
|
||||
final UncaughtExceptionHandler h = t.getUncaughtExceptionHandler();
|
||||
if (h != null)
|
||||
{
|
||||
h.uncaughtException(t, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,282 +18,250 @@ package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* This class handles thread pooling system. It relies on two ThreadPoolExecutor arrays, which poolers number is generated using config.
|
||||
* <p>
|
||||
* Those arrays hold following pools :
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Scheduled pool keeps a track about incoming, future events.</li>
|
||||
* <li>Instant pool handles short-life events.</li>
|
||||
* </ul>
|
||||
* @author _dev_ (savormix)
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class ThreadPool
|
||||
{
|
||||
protected static final Logger LOG = Logger.getLogger(ThreadPool.class.getName());
|
||||
private static final Logger LOGGER = Logger.getLogger(ThreadPool.class.getName());
|
||||
|
||||
private static final long MAX_DELAY = TimeUnit.NANOSECONDS.toMillis(Long.MAX_VALUE - System.nanoTime()) / 2;
|
||||
private static ScheduledThreadPoolExecutor SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
private static ThreadPoolExecutor THREAD_POOL_EXECUTOR;
|
||||
|
||||
private static int _threadPoolRandomizer;
|
||||
|
||||
protected static ScheduledThreadPoolExecutor[] _scheduledPools;
|
||||
protected static ThreadPoolExecutor[] _instantPools;
|
||||
|
||||
/**
|
||||
* Init the different pools, based on Config. It is launched only once, on Gameserver instance.
|
||||
*/
|
||||
public static void init()
|
||||
public static void initThreadPools(IThreadPoolInitializer initializer) throws Exception
|
||||
{
|
||||
// Feed scheduled pool.
|
||||
int poolCount = Config.SCHEDULED_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR != null) || (THREAD_POOL_EXECUTOR != null))
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
throw new Exception("The thread pool has been already initialized!");
|
||||
}
|
||||
|
||||
_scheduledPools = new ScheduledThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_scheduledPools[i] = new ScheduledThreadPoolExecutor(Config.THREADS_PER_SCHEDULED_THREAD_POOL);
|
||||
}
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR = new ScheduledThreadPoolExecutor(initializer.getScheduledThreadPoolSize(), new PoolThreadFactory("L2JU-SP-", Thread.NORM_PRIORITY));
|
||||
THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(initializer.getThreadPoolSize(), initializer.getThreadPoolSize(), 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(), new PoolThreadFactory("L2JU-IT-", Thread.NORM_PRIORITY));
|
||||
|
||||
// Feed instant pool.
|
||||
poolCount = Config.INSTANT_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
tp.setRejectedExecutionHandler(new RejectedExecutionHandlerImpl());
|
||||
tp.prestartAllCoreThreads();
|
||||
});
|
||||
|
||||
_instantPools = new ThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_instantPools[i] = new ThreadPoolExecutor(Config.THREADS_PER_INSTANT_THREAD_POOL, Config.THREADS_PER_INSTANT_THREAD_POOL, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100000));
|
||||
}
|
||||
scheduleAtFixedRate(ThreadPool::purge, 60000, 60000); // Repeats every one minute.
|
||||
|
||||
// Prestart core threads.
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
// Launch purge task.
|
||||
scheduleAtFixedRate(() ->
|
||||
{
|
||||
purge();
|
||||
}, 600000, 600000);
|
||||
|
||||
LOG.info("ThreadPoolManager: Initialized " + getPoolSize(_instantPools) + "/" + getMaximumPoolSize(_instantPools) + " instant thread(s).");
|
||||
LOG.info("ThreadPoolManager: Initialized " + getPoolSize(_scheduledPools) + "/" + getMaximumPoolSize(_scheduledPools) + " scheduled thread(s).");
|
||||
}
|
||||
|
||||
public static void purge()
|
||||
{
|
||||
for (ScheduledThreadPoolExecutor threadPool1 : _scheduledPools)
|
||||
{
|
||||
threadPool1.purge();
|
||||
}
|
||||
for (ThreadPoolExecutor threadPool2 : _instantPools)
|
||||
{
|
||||
threadPool2.purge();
|
||||
}
|
||||
LOGGER.info("ThreadPool: Initialized with");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + "/" + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + " scheduled thread(s)."); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getPoolSize() + "/" + THREAD_POOL_EXECUTOR.getMaximumPoolSize() + " thread(s).");
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a one-shot action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion.
|
||||
* Gets the scheduled thread pool executor.
|
||||
* @return the scheduled thread pool executor
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable r, long delay)
|
||||
public static ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).schedule(new TaskWrapper(r), validate(delay), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a periodic action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @param period : the period between successive executions.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will throw an exception upon cancellation.
|
||||
* Gets the thread pool executor.
|
||||
* @return the thread pool executor
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable r, long delay, long period)
|
||||
public static ThreadPoolExecutor getThreadPoolExecutor()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).scheduleAtFixedRate(new TaskWrapper(r), validate(delay), validate(period), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a stream of all the thread pools.
|
||||
* @return the stream of all the thread pools
|
||||
*/
|
||||
public static Stream<ThreadPoolExecutor> getThreadPools()
|
||||
{
|
||||
return Stream.of(SCHEDULED_THREAD_POOL_EXECUTOR, THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable task, long delay)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.schedule(new RunnableWrapper(task), delay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay at fixed rate in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleAtFixedRate(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay with fixed delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleWithFixedDelay(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given task sometime in the future.
|
||||
* @param r : the task to execute.
|
||||
* @param task the task to execute
|
||||
*/
|
||||
public static void execute(Runnable r)
|
||||
public static void execute(Runnable task)
|
||||
{
|
||||
try
|
||||
{
|
||||
getPool(_instantPools).execute(new TaskWrapper(r));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getStats()
|
||||
{
|
||||
List<String> stats = new ArrayList<>();
|
||||
for (int i = 0; i < _scheduledPools.length; i++)
|
||||
{
|
||||
final ScheduledThreadPoolExecutor threadPool = _scheduledPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
for (int i = 0; i < _instantPools.length; i++)
|
||||
{
|
||||
final ThreadPoolExecutor threadPool = _instantPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
return stats.toArray(new String[stats.size()]);
|
||||
THREAD_POOL_EXECUTOR.execute(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown thread pooling system correctly. Send different informations.
|
||||
* Submits a Runnable task for execution and returns a Future representing that task. The Future's get method will return null upon successful completion.
|
||||
* @param task the task to submit
|
||||
* @return a Future representing pending completion of the task
|
||||
*/
|
||||
public static Future<?> submit(Runnable task)
|
||||
{
|
||||
return THREAD_POOL_EXECUTOR.submit(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges all thread pools.
|
||||
*/
|
||||
public static void purge()
|
||||
{
|
||||
getThreadPools().forEach(ThreadPoolExecutor::purge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thread pools stats.
|
||||
* @return the stats
|
||||
*/
|
||||
public static List<String> getStats()
|
||||
{
|
||||
final List<String> list = new ArrayList<>(23);
|
||||
list.add("");
|
||||
list.add("Scheduled pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + SCHEDULED_THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize()); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
list.add("getCompletedTaskCount: " + SCHEDULED_THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + SCHEDULED_THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
list.add("Thread pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + THREAD_POOL_EXECUTOR.getMaximumPoolSize());
|
||||
list.add("getCompletedTaskCount: " + THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdowns the thread pools waiting for tasks to finish.
|
||||
*/
|
||||
public static void shutdown()
|
||||
{
|
||||
try
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR == null) && (THREAD_POOL_EXECUTOR == null))
|
||||
{
|
||||
LOG.info("ThreadPoolManager: Shutting down.");
|
||||
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T> : The pool type.
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the less fed pool.
|
||||
*/
|
||||
private static <T> T getPool(T[] threadPools)
|
||||
{
|
||||
return threadPools[_threadPoolRandomizer++ % threadPools.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delay : The delay to validate.
|
||||
* @return a secured value, from 0 to MAX_DELAY.
|
||||
*/
|
||||
private static long validate(long delay)
|
||||
{
|
||||
return Math.max(0, Math.min(MAX_DELAY, delay));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall actual pools size.
|
||||
*/
|
||||
private static long getPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getPoolSize();
|
||||
return;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall maximum pools size.
|
||||
*/
|
||||
private static long getMaximumPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getMaximumPoolSize();
|
||||
}
|
||||
LOGGER.info("ThreadPool: Shutting down.");
|
||||
LOGGER.info("...executing " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks.");
|
||||
LOGGER.info("...executing " + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final class TaskWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public TaskWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
tp.shutdown();
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOG.warning("Exception in " + _runnable.getClass().getName() + " execution: " + e.getMessage());
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
});
|
||||
|
||||
getThreadPools().forEach(t ->
|
||||
{
|
||||
try
|
||||
{
|
||||
t.awaitTermination(15, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
LOGGER.warning("" + e);
|
||||
}
|
||||
});
|
||||
|
||||
if (!SCHEDULED_THREAD_POOL_EXECUTOR.isTerminated())
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
|
||||
try
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.awaitTermination(5, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("...success: " + getThreadPools().allMatch(ThreadPoolExecutor::isTerminated) + " in " + (System.currentTimeMillis() - startTime) + " ms.");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks left.");
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks left.");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PoolThreadFactory implements ThreadFactory
|
||||
{
|
||||
private final String _prefix;
|
||||
private final int _priority;
|
||||
private final AtomicInteger _threadId = new AtomicInteger();
|
||||
|
||||
public PoolThreadFactory(String prefix, int priority)
|
||||
{
|
||||
_prefix = prefix;
|
||||
_priority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r)
|
||||
{
|
||||
final Thread thread = new Thread(r, _prefix + _threadId.incrementAndGet());
|
||||
thread.setPriority(_priority);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,18 +177,19 @@ public class GameServer
|
||||
{
|
||||
final long serverLoadStart = System.currentTimeMillis();
|
||||
|
||||
printSection("ThreadPool");
|
||||
ThreadPool.initThreadPools(new GameThreadPools());
|
||||
|
||||
printSection("IdFactory");
|
||||
if (!IdFactory.getInstance().isInitialized())
|
||||
{
|
||||
LOGGER.severe(getClass().getSimpleName() + ": Could not read object IDs from database. Please check your configuration.");
|
||||
throw new Exception("Could not initialize the ID factory");
|
||||
}
|
||||
|
||||
printSection("ThreadPool");
|
||||
ThreadPool.init();
|
||||
EventDispatcher.getInstance();
|
||||
|
||||
// load script engines
|
||||
printSection("Scripting Engines");
|
||||
EventDispatcher.getInstance();
|
||||
ScriptEngineManager.getInstance();
|
||||
|
||||
printSection("Telnet");
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.commons.concurrent.IThreadPoolInitializer;
|
||||
|
||||
/**
|
||||
* @author nos
|
||||
*/
|
||||
public final class GameThreadPools implements IThreadPoolInitializer
|
||||
{
|
||||
@Override
|
||||
public int getScheduledThreadPoolSize()
|
||||
{
|
||||
return Config.SCHEDULED_THREAD_POOL_COUNT != -1 ? Config.SCHEDULED_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_SCHEDULED_THREAD_POOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThreadPoolSize()
|
||||
{
|
||||
return Config.INSTANT_THREAD_POOL_COUNT != -1 ? Config.INSTANT_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_INSTANT_THREAD_POOL;
|
||||
}
|
||||
}
|
@ -131,7 +131,6 @@ Others:
|
||||
-Reworked drop system
|
||||
-Skill system from L2jUnity
|
||||
-GeoEngine from aCis
|
||||
-Threadpool manager from aCis
|
||||
|
||||
TODO:
|
||||
-Provisional Clan Halls
|
||||
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
public interface IThreadPoolInitializer
|
||||
{
|
||||
int getScheduledThreadPoolSize();
|
||||
|
||||
int getThreadPoolSize();
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class RejectedExecutionHandlerImpl implements RejectedExecutionHandler
|
||||
{
|
||||
private static final Logger LOGGER = Logger.getLogger(RejectedExecutionHandlerImpl.class.getName());
|
||||
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
|
||||
{
|
||||
if (executor.isShutdown())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.warning(r + " from " + executor + " " + new RejectedExecutionException());
|
||||
|
||||
if (Thread.currentThread().getPriority() > Thread.NORM_PRIORITY)
|
||||
{
|
||||
new Thread(r).start();
|
||||
}
|
||||
else
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
|
||||
/**
|
||||
* @author UnAfraid
|
||||
*/
|
||||
public final class RunnableWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public RunnableWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
}
|
||||
catch (final Throwable e)
|
||||
{
|
||||
final Thread t = Thread.currentThread();
|
||||
final UncaughtExceptionHandler h = t.getUncaughtExceptionHandler();
|
||||
if (h != null)
|
||||
{
|
||||
h.uncaughtException(t, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,282 +18,250 @@ package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* This class handles thread pooling system. It relies on two ThreadPoolExecutor arrays, which poolers number is generated using config.
|
||||
* <p>
|
||||
* Those arrays hold following pools :
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Scheduled pool keeps a track about incoming, future events.</li>
|
||||
* <li>Instant pool handles short-life events.</li>
|
||||
* </ul>
|
||||
* @author _dev_ (savormix)
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class ThreadPool
|
||||
{
|
||||
protected static final Logger LOG = Logger.getLogger(ThreadPool.class.getName());
|
||||
private static final Logger LOGGER = Logger.getLogger(ThreadPool.class.getName());
|
||||
|
||||
private static final long MAX_DELAY = TimeUnit.NANOSECONDS.toMillis(Long.MAX_VALUE - System.nanoTime()) / 2;
|
||||
private static ScheduledThreadPoolExecutor SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
private static ThreadPoolExecutor THREAD_POOL_EXECUTOR;
|
||||
|
||||
private static int _threadPoolRandomizer;
|
||||
|
||||
protected static ScheduledThreadPoolExecutor[] _scheduledPools;
|
||||
protected static ThreadPoolExecutor[] _instantPools;
|
||||
|
||||
/**
|
||||
* Init the different pools, based on Config. It is launched only once, on Gameserver instance.
|
||||
*/
|
||||
public static void init()
|
||||
public static void initThreadPools(IThreadPoolInitializer initializer) throws Exception
|
||||
{
|
||||
// Feed scheduled pool.
|
||||
int poolCount = Config.SCHEDULED_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR != null) || (THREAD_POOL_EXECUTOR != null))
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
throw new Exception("The thread pool has been already initialized!");
|
||||
}
|
||||
|
||||
_scheduledPools = new ScheduledThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_scheduledPools[i] = new ScheduledThreadPoolExecutor(Config.THREADS_PER_SCHEDULED_THREAD_POOL);
|
||||
}
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR = new ScheduledThreadPoolExecutor(initializer.getScheduledThreadPoolSize(), new PoolThreadFactory("L2JU-SP-", Thread.NORM_PRIORITY));
|
||||
THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(initializer.getThreadPoolSize(), initializer.getThreadPoolSize(), 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(), new PoolThreadFactory("L2JU-IT-", Thread.NORM_PRIORITY));
|
||||
|
||||
// Feed instant pool.
|
||||
poolCount = Config.INSTANT_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
tp.setRejectedExecutionHandler(new RejectedExecutionHandlerImpl());
|
||||
tp.prestartAllCoreThreads();
|
||||
});
|
||||
|
||||
_instantPools = new ThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_instantPools[i] = new ThreadPoolExecutor(Config.THREADS_PER_INSTANT_THREAD_POOL, Config.THREADS_PER_INSTANT_THREAD_POOL, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100000));
|
||||
}
|
||||
scheduleAtFixedRate(ThreadPool::purge, 60000, 60000); // Repeats every one minute.
|
||||
|
||||
// Prestart core threads.
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
// Launch purge task.
|
||||
scheduleAtFixedRate(() ->
|
||||
{
|
||||
purge();
|
||||
}, 600000, 600000);
|
||||
|
||||
LOG.info("ThreadPoolManager: Initialized " + getPoolSize(_instantPools) + "/" + getMaximumPoolSize(_instantPools) + " instant thread(s).");
|
||||
LOG.info("ThreadPoolManager: Initialized " + getPoolSize(_scheduledPools) + "/" + getMaximumPoolSize(_scheduledPools) + " scheduled thread(s).");
|
||||
}
|
||||
|
||||
public static void purge()
|
||||
{
|
||||
for (ScheduledThreadPoolExecutor threadPool1 : _scheduledPools)
|
||||
{
|
||||
threadPool1.purge();
|
||||
}
|
||||
for (ThreadPoolExecutor threadPool2 : _instantPools)
|
||||
{
|
||||
threadPool2.purge();
|
||||
}
|
||||
LOGGER.info("ThreadPool: Initialized with");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + "/" + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + " scheduled thread(s)."); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getPoolSize() + "/" + THREAD_POOL_EXECUTOR.getMaximumPoolSize() + " thread(s).");
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a one-shot action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion.
|
||||
* Gets the scheduled thread pool executor.
|
||||
* @return the scheduled thread pool executor
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable r, long delay)
|
||||
public static ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).schedule(new TaskWrapper(r), validate(delay), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a periodic action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @param period : the period between successive executions.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will throw an exception upon cancellation.
|
||||
* Gets the thread pool executor.
|
||||
* @return the thread pool executor
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable r, long delay, long period)
|
||||
public static ThreadPoolExecutor getThreadPoolExecutor()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).scheduleAtFixedRate(new TaskWrapper(r), validate(delay), validate(period), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a stream of all the thread pools.
|
||||
* @return the stream of all the thread pools
|
||||
*/
|
||||
public static Stream<ThreadPoolExecutor> getThreadPools()
|
||||
{
|
||||
return Stream.of(SCHEDULED_THREAD_POOL_EXECUTOR, THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable task, long delay)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.schedule(new RunnableWrapper(task), delay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay at fixed rate in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleAtFixedRate(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay with fixed delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleWithFixedDelay(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given task sometime in the future.
|
||||
* @param r : the task to execute.
|
||||
* @param task the task to execute
|
||||
*/
|
||||
public static void execute(Runnable r)
|
||||
public static void execute(Runnable task)
|
||||
{
|
||||
try
|
||||
{
|
||||
getPool(_instantPools).execute(new TaskWrapper(r));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getStats()
|
||||
{
|
||||
List<String> stats = new ArrayList<>();
|
||||
for (int i = 0; i < _scheduledPools.length; i++)
|
||||
{
|
||||
final ScheduledThreadPoolExecutor threadPool = _scheduledPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
for (int i = 0; i < _instantPools.length; i++)
|
||||
{
|
||||
final ThreadPoolExecutor threadPool = _instantPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
return stats.toArray(new String[stats.size()]);
|
||||
THREAD_POOL_EXECUTOR.execute(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown thread pooling system correctly. Send different informations.
|
||||
* Submits a Runnable task for execution and returns a Future representing that task. The Future's get method will return null upon successful completion.
|
||||
* @param task the task to submit
|
||||
* @return a Future representing pending completion of the task
|
||||
*/
|
||||
public static Future<?> submit(Runnable task)
|
||||
{
|
||||
return THREAD_POOL_EXECUTOR.submit(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges all thread pools.
|
||||
*/
|
||||
public static void purge()
|
||||
{
|
||||
getThreadPools().forEach(ThreadPoolExecutor::purge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thread pools stats.
|
||||
* @return the stats
|
||||
*/
|
||||
public static List<String> getStats()
|
||||
{
|
||||
final List<String> list = new ArrayList<>(23);
|
||||
list.add("");
|
||||
list.add("Scheduled pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + SCHEDULED_THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize()); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
list.add("getCompletedTaskCount: " + SCHEDULED_THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + SCHEDULED_THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
list.add("Thread pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + THREAD_POOL_EXECUTOR.getMaximumPoolSize());
|
||||
list.add("getCompletedTaskCount: " + THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdowns the thread pools waiting for tasks to finish.
|
||||
*/
|
||||
public static void shutdown()
|
||||
{
|
||||
try
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR == null) && (THREAD_POOL_EXECUTOR == null))
|
||||
{
|
||||
LOG.info("ThreadPoolManager: Shutting down.");
|
||||
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T> : The pool type.
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the less fed pool.
|
||||
*/
|
||||
private static <T> T getPool(T[] threadPools)
|
||||
{
|
||||
return threadPools[_threadPoolRandomizer++ % threadPools.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delay : The delay to validate.
|
||||
* @return a secured value, from 0 to MAX_DELAY.
|
||||
*/
|
||||
private static long validate(long delay)
|
||||
{
|
||||
return Math.max(0, Math.min(MAX_DELAY, delay));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall actual pools size.
|
||||
*/
|
||||
private static long getPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getPoolSize();
|
||||
return;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall maximum pools size.
|
||||
*/
|
||||
private static long getMaximumPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getMaximumPoolSize();
|
||||
}
|
||||
LOGGER.info("ThreadPool: Shutting down.");
|
||||
LOGGER.info("...executing " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks.");
|
||||
LOGGER.info("...executing " + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final class TaskWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public TaskWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
tp.shutdown();
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOG.warning("Exception in " + _runnable.getClass().getName() + " execution: " + e.getMessage());
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
});
|
||||
|
||||
getThreadPools().forEach(t ->
|
||||
{
|
||||
try
|
||||
{
|
||||
t.awaitTermination(15, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
LOGGER.warning("" + e);
|
||||
}
|
||||
});
|
||||
|
||||
if (!SCHEDULED_THREAD_POOL_EXECUTOR.isTerminated())
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
|
||||
try
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.awaitTermination(5, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("...success: " + getThreadPools().allMatch(ThreadPoolExecutor::isTerminated) + " in " + (System.currentTimeMillis() - startTime) + " ms.");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks left.");
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks left.");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PoolThreadFactory implements ThreadFactory
|
||||
{
|
||||
private final String _prefix;
|
||||
private final int _priority;
|
||||
private final AtomicInteger _threadId = new AtomicInteger();
|
||||
|
||||
public PoolThreadFactory(String prefix, int priority)
|
||||
{
|
||||
_prefix = prefix;
|
||||
_priority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r)
|
||||
{
|
||||
final Thread thread = new Thread(r, _prefix + _threadId.incrementAndGet());
|
||||
thread.setPriority(_priority);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,18 +177,19 @@ public class GameServer
|
||||
{
|
||||
final long serverLoadStart = System.currentTimeMillis();
|
||||
|
||||
printSection("ThreadPool");
|
||||
ThreadPool.initThreadPools(new GameThreadPools());
|
||||
|
||||
printSection("IdFactory");
|
||||
if (!IdFactory.getInstance().isInitialized())
|
||||
{
|
||||
LOGGER.severe(getClass().getSimpleName() + ": Could not read object IDs from database. Please check your configuration.");
|
||||
throw new Exception("Could not initialize the ID factory");
|
||||
}
|
||||
|
||||
printSection("ThreadPool");
|
||||
ThreadPool.init();
|
||||
EventDispatcher.getInstance();
|
||||
|
||||
// load script engines
|
||||
printSection("Scripting Engines");
|
||||
EventDispatcher.getInstance();
|
||||
ScriptEngineManager.getInstance();
|
||||
|
||||
printSection("Telnet");
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.commons.concurrent.IThreadPoolInitializer;
|
||||
|
||||
/**
|
||||
* @author nos
|
||||
*/
|
||||
public final class GameThreadPools implements IThreadPoolInitializer
|
||||
{
|
||||
@Override
|
||||
public int getScheduledThreadPoolSize()
|
||||
{
|
||||
return Config.SCHEDULED_THREAD_POOL_COUNT != -1 ? Config.SCHEDULED_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_SCHEDULED_THREAD_POOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThreadPoolSize()
|
||||
{
|
||||
return Config.INSTANT_THREAD_POOL_COUNT != -1 ? Config.INSTANT_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_INSTANT_THREAD_POOL;
|
||||
}
|
||||
}
|
@ -135,7 +135,6 @@ Others:
|
||||
-Reworked drop system
|
||||
-Skill system from L2jUnity
|
||||
-GeoEngine from aCis
|
||||
-Threadpool manager from aCis
|
||||
|
||||
TODO:
|
||||
-Provisional Clan Halls
|
||||
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
public interface IThreadPoolInitializer
|
||||
{
|
||||
int getScheduledThreadPoolSize();
|
||||
|
||||
int getThreadPoolSize();
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class RejectedExecutionHandlerImpl implements RejectedExecutionHandler
|
||||
{
|
||||
private static final Logger LOGGER = Logger.getLogger(RejectedExecutionHandlerImpl.class.getName());
|
||||
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
|
||||
{
|
||||
if (executor.isShutdown())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.warning(r + " from " + executor + " " + new RejectedExecutionException());
|
||||
|
||||
if (Thread.currentThread().getPriority() > Thread.NORM_PRIORITY)
|
||||
{
|
||||
new Thread(r).start();
|
||||
}
|
||||
else
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
|
||||
/**
|
||||
* @author UnAfraid
|
||||
*/
|
||||
public final class RunnableWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public RunnableWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
}
|
||||
catch (final Throwable e)
|
||||
{
|
||||
final Thread t = Thread.currentThread();
|
||||
final UncaughtExceptionHandler h = t.getUncaughtExceptionHandler();
|
||||
if (h != null)
|
||||
{
|
||||
h.uncaughtException(t, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,282 +18,250 @@ package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* This class handles thread pooling system. It relies on two ThreadPoolExecutor arrays, which poolers number is generated using config.
|
||||
* <p>
|
||||
* Those arrays hold following pools :
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Scheduled pool keeps a track about incoming, future events.</li>
|
||||
* <li>Instant pool handles short-life events.</li>
|
||||
* </ul>
|
||||
* @author _dev_ (savormix)
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class ThreadPool
|
||||
{
|
||||
protected static final Logger LOGGER = Logger.getLogger(ThreadPool.class.getName());
|
||||
private static final Logger LOGGER = Logger.getLogger(ThreadPool.class.getName());
|
||||
|
||||
private static final long MAX_DELAY = TimeUnit.NANOSECONDS.toMillis(Long.MAX_VALUE - System.nanoTime()) / 2;
|
||||
private static ScheduledThreadPoolExecutor SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
private static ThreadPoolExecutor THREAD_POOL_EXECUTOR;
|
||||
|
||||
private static int _threadPoolRandomizer;
|
||||
|
||||
protected static ScheduledThreadPoolExecutor[] _scheduledPools;
|
||||
protected static ThreadPoolExecutor[] _instantPools;
|
||||
|
||||
/**
|
||||
* Init the different pools, based on Config. It is launched only once, on Gameserver instance.
|
||||
*/
|
||||
public static void init()
|
||||
public static void initThreadPools(IThreadPoolInitializer initializer) throws Exception
|
||||
{
|
||||
// Feed scheduled pool.
|
||||
int poolCount = Config.SCHEDULED_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR != null) || (THREAD_POOL_EXECUTOR != null))
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
throw new Exception("The thread pool has been already initialized!");
|
||||
}
|
||||
|
||||
_scheduledPools = new ScheduledThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_scheduledPools[i] = new ScheduledThreadPoolExecutor(Config.THREADS_PER_SCHEDULED_THREAD_POOL);
|
||||
}
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR = new ScheduledThreadPoolExecutor(initializer.getScheduledThreadPoolSize(), new PoolThreadFactory("L2JU-SP-", Thread.NORM_PRIORITY));
|
||||
THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(initializer.getThreadPoolSize(), initializer.getThreadPoolSize(), 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(), new PoolThreadFactory("L2JU-IT-", Thread.NORM_PRIORITY));
|
||||
|
||||
// Feed instant pool.
|
||||
poolCount = Config.INSTANT_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
tp.setRejectedExecutionHandler(new RejectedExecutionHandlerImpl());
|
||||
tp.prestartAllCoreThreads();
|
||||
});
|
||||
|
||||
_instantPools = new ThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_instantPools[i] = new ThreadPoolExecutor(Config.THREADS_PER_INSTANT_THREAD_POOL, Config.THREADS_PER_INSTANT_THREAD_POOL, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100000));
|
||||
}
|
||||
scheduleAtFixedRate(ThreadPool::purge, 60000, 60000); // Repeats every one minute.
|
||||
|
||||
// Prestart core threads.
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
// Launch purge task.
|
||||
scheduleAtFixedRate(() ->
|
||||
{
|
||||
purge();
|
||||
}, 600000, 600000);
|
||||
|
||||
LOGGER.info("ThreadPoolManager: Initialized " + getPoolSize(_instantPools) + "/" + getMaximumPoolSize(_instantPools) + " instant thread(s).");
|
||||
LOGGER.info("ThreadPoolManager: Initialized " + getPoolSize(_scheduledPools) + "/" + getMaximumPoolSize(_scheduledPools) + " scheduled thread(s).");
|
||||
}
|
||||
|
||||
public static void purge()
|
||||
{
|
||||
for (ScheduledThreadPoolExecutor threadPool1 : _scheduledPools)
|
||||
{
|
||||
threadPool1.purge();
|
||||
}
|
||||
for (ThreadPoolExecutor threadPool2 : _instantPools)
|
||||
{
|
||||
threadPool2.purge();
|
||||
}
|
||||
LOGGER.info("ThreadPool: Initialized with");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + "/" + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + " scheduled thread(s)."); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getPoolSize() + "/" + THREAD_POOL_EXECUTOR.getMaximumPoolSize() + " thread(s).");
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a one-shot action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion.
|
||||
* Gets the scheduled thread pool executor.
|
||||
* @return the scheduled thread pool executor
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable r, long delay)
|
||||
public static ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).schedule(new TaskWrapper(r), validate(delay), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a periodic action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @param period : the period between successive executions.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will throw an exception upon cancellation.
|
||||
* Gets the thread pool executor.
|
||||
* @return the thread pool executor
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable r, long delay, long period)
|
||||
public static ThreadPoolExecutor getThreadPoolExecutor()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).scheduleAtFixedRate(new TaskWrapper(r), validate(delay), validate(period), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a stream of all the thread pools.
|
||||
* @return the stream of all the thread pools
|
||||
*/
|
||||
public static Stream<ThreadPoolExecutor> getThreadPools()
|
||||
{
|
||||
return Stream.of(SCHEDULED_THREAD_POOL_EXECUTOR, THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable task, long delay)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.schedule(new RunnableWrapper(task), delay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay at fixed rate in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleAtFixedRate(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay with fixed delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleWithFixedDelay(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given task sometime in the future.
|
||||
* @param r : the task to execute.
|
||||
* @param task the task to execute
|
||||
*/
|
||||
public static void execute(Runnable r)
|
||||
public static void execute(Runnable task)
|
||||
{
|
||||
try
|
||||
{
|
||||
getPool(_instantPools).execute(new TaskWrapper(r));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getStats()
|
||||
{
|
||||
List<String> stats = new ArrayList<>();
|
||||
for (int i = 0; i < _scheduledPools.length; i++)
|
||||
{
|
||||
final ScheduledThreadPoolExecutor threadPool = _scheduledPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
for (int i = 0; i < _instantPools.length; i++)
|
||||
{
|
||||
final ThreadPoolExecutor threadPool = _instantPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
return stats.toArray(new String[stats.size()]);
|
||||
THREAD_POOL_EXECUTOR.execute(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown thread pooling system correctly. Send different informations.
|
||||
* Submits a Runnable task for execution and returns a Future representing that task. The Future's get method will return null upon successful completion.
|
||||
* @param task the task to submit
|
||||
* @return a Future representing pending completion of the task
|
||||
*/
|
||||
public static Future<?> submit(Runnable task)
|
||||
{
|
||||
return THREAD_POOL_EXECUTOR.submit(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges all thread pools.
|
||||
*/
|
||||
public static void purge()
|
||||
{
|
||||
getThreadPools().forEach(ThreadPoolExecutor::purge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thread pools stats.
|
||||
* @return the stats
|
||||
*/
|
||||
public static List<String> getStats()
|
||||
{
|
||||
final List<String> list = new ArrayList<>(23);
|
||||
list.add("");
|
||||
list.add("Scheduled pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + SCHEDULED_THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize()); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
list.add("getCompletedTaskCount: " + SCHEDULED_THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + SCHEDULED_THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
list.add("Thread pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + THREAD_POOL_EXECUTOR.getMaximumPoolSize());
|
||||
list.add("getCompletedTaskCount: " + THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdowns the thread pools waiting for tasks to finish.
|
||||
*/
|
||||
public static void shutdown()
|
||||
{
|
||||
try
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR == null) && (THREAD_POOL_EXECUTOR == null))
|
||||
{
|
||||
LOGGER.info("ThreadPoolManager: Shutting down.");
|
||||
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T> : The pool type.
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the less fed pool.
|
||||
*/
|
||||
private static <T> T getPool(T[] threadPools)
|
||||
{
|
||||
return threadPools[_threadPoolRandomizer++ % threadPools.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delay : The delay to validate.
|
||||
* @return a secured value, from 0 to MAX_DELAY.
|
||||
*/
|
||||
private static long validate(long delay)
|
||||
{
|
||||
return Math.max(0, Math.min(MAX_DELAY, delay));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall actual pools size.
|
||||
*/
|
||||
private static long getPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getPoolSize();
|
||||
return;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall maximum pools size.
|
||||
*/
|
||||
private static long getMaximumPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getMaximumPoolSize();
|
||||
}
|
||||
LOGGER.info("ThreadPool: Shutting down.");
|
||||
LOGGER.info("...executing " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks.");
|
||||
LOGGER.info("...executing " + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final class TaskWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public TaskWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
tp.shutdown();
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOGGER.warning("Exception in " + _runnable.getClass().getName() + " execution: " + e.getMessage());
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
});
|
||||
|
||||
getThreadPools().forEach(t ->
|
||||
{
|
||||
try
|
||||
{
|
||||
t.awaitTermination(15, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
LOGGER.warning("" + e);
|
||||
}
|
||||
});
|
||||
|
||||
if (!SCHEDULED_THREAD_POOL_EXECUTOR.isTerminated())
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
|
||||
try
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.awaitTermination(5, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("...success: " + getThreadPools().allMatch(ThreadPoolExecutor::isTerminated) + " in " + (System.currentTimeMillis() - startTime) + " ms.");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks left.");
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks left.");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PoolThreadFactory implements ThreadFactory
|
||||
{
|
||||
private final String _prefix;
|
||||
private final int _priority;
|
||||
private final AtomicInteger _threadId = new AtomicInteger();
|
||||
|
||||
public PoolThreadFactory(String prefix, int priority)
|
||||
{
|
||||
_prefix = prefix;
|
||||
_priority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r)
|
||||
{
|
||||
final Thread thread = new Thread(r, _prefix + _threadId.incrementAndGet());
|
||||
thread.setPriority(_priority);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,14 +179,22 @@ public class GameServer
|
||||
|
||||
Util.printSection("Database");
|
||||
DatabaseFactory.getInstance();
|
||||
LOGGER.info("L2DatabaseFactory: loaded.");
|
||||
LOGGER.info("Database: Initialized.");
|
||||
|
||||
Util.printSection("Threads");
|
||||
ThreadPool.init();
|
||||
Util.printSection("ThreadPool");
|
||||
ThreadPool.initThreadPools(new GameThreadPools());
|
||||
if (Config.DEADLOCKCHECK_INTIAL_TIME > 0)
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(DeadlockDetector.getInstance(), Config.DEADLOCKCHECK_INTIAL_TIME, Config.DEADLOCKCHECK_DELAY_TIME);
|
||||
}
|
||||
|
||||
Util.printSection("IdFactory");
|
||||
if (!IdFactory.getInstance().isInitialized())
|
||||
{
|
||||
LOGGER.info("Could not read object IDs from DB. Please Check Your Data.");
|
||||
throw new Exception("Could not initialize the ID factory");
|
||||
}
|
||||
|
||||
new File(Config.DATAPACK_ROOT, "data/clans").mkdirs();
|
||||
new File(Config.DATAPACK_ROOT, "data/crests").mkdirs();
|
||||
new File(Config.DATAPACK_ROOT, "data/pathnode").mkdirs();
|
||||
@ -207,11 +215,6 @@ public class GameServer
|
||||
MapRegionTable.getInstance();
|
||||
Announcements.getInstance();
|
||||
AutoAnnouncementHandler.getInstance();
|
||||
if (!IdFactory.getInstance().isInitialized())
|
||||
{
|
||||
LOGGER.info("Could not read object IDs from DB. Please Check Your Data.");
|
||||
throw new Exception("Could not initialize the ID factory");
|
||||
}
|
||||
GlobalVariablesManager.getInstance();
|
||||
StaticObjects.getInstance();
|
||||
TeleportLocationTable.getInstance();
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.commons.concurrent.IThreadPoolInitializer;
|
||||
|
||||
/**
|
||||
* @author nos
|
||||
*/
|
||||
public final class GameThreadPools implements IThreadPoolInitializer
|
||||
{
|
||||
@Override
|
||||
public int getScheduledThreadPoolSize()
|
||||
{
|
||||
return Config.SCHEDULED_THREAD_POOL_COUNT != -1 ? Config.SCHEDULED_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_SCHEDULED_THREAD_POOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThreadPoolSize()
|
||||
{
|
||||
return Config.INSTANT_THREAD_POOL_COUNT != -1 ? Config.INSTANT_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_INSTANT_THREAD_POOL;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
public interface IThreadPoolInitializer
|
||||
{
|
||||
int getScheduledThreadPoolSize();
|
||||
|
||||
int getThreadPoolSize();
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class RejectedExecutionHandlerImpl implements RejectedExecutionHandler
|
||||
{
|
||||
private static final Logger LOGGER = Logger.getLogger(RejectedExecutionHandlerImpl.class.getName());
|
||||
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
|
||||
{
|
||||
if (executor.isShutdown())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.warning(r + " from " + executor + " " + new RejectedExecutionException());
|
||||
|
||||
if (Thread.currentThread().getPriority() > Thread.NORM_PRIORITY)
|
||||
{
|
||||
new Thread(r).start();
|
||||
}
|
||||
else
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
|
||||
/**
|
||||
* @author UnAfraid
|
||||
*/
|
||||
public final class RunnableWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public RunnableWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
}
|
||||
catch (final Throwable e)
|
||||
{
|
||||
final Thread t = Thread.currentThread();
|
||||
final UncaughtExceptionHandler h = t.getUncaughtExceptionHandler();
|
||||
if (h != null)
|
||||
{
|
||||
h.uncaughtException(t, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,282 +18,250 @@ package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* This class handles thread pooling system. It relies on two ThreadPoolExecutor arrays, which poolers number is generated using config.
|
||||
* <p>
|
||||
* Those arrays hold following pools :
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Scheduled pool keeps a track about incoming, future events.</li>
|
||||
* <li>Instant pool handles short-life events.</li>
|
||||
* </ul>
|
||||
* @author _dev_ (savormix)
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class ThreadPool
|
||||
{
|
||||
protected static final Logger LOG = Logger.getLogger(ThreadPool.class.getName());
|
||||
private static final Logger LOGGER = Logger.getLogger(ThreadPool.class.getName());
|
||||
|
||||
private static final long MAX_DELAY = TimeUnit.NANOSECONDS.toMillis(Long.MAX_VALUE - System.nanoTime()) / 2;
|
||||
private static ScheduledThreadPoolExecutor SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
private static ThreadPoolExecutor THREAD_POOL_EXECUTOR;
|
||||
|
||||
private static int _threadPoolRandomizer;
|
||||
|
||||
protected static ScheduledThreadPoolExecutor[] _scheduledPools;
|
||||
protected static ThreadPoolExecutor[] _instantPools;
|
||||
|
||||
/**
|
||||
* Init the different pools, based on Config. It is launched only once, on Gameserver instance.
|
||||
*/
|
||||
public static void init()
|
||||
public static void initThreadPools(IThreadPoolInitializer initializer) throws Exception
|
||||
{
|
||||
// Feed scheduled pool.
|
||||
int poolCount = Config.SCHEDULED_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR != null) || (THREAD_POOL_EXECUTOR != null))
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
throw new Exception("The thread pool has been already initialized!");
|
||||
}
|
||||
|
||||
_scheduledPools = new ScheduledThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_scheduledPools[i] = new ScheduledThreadPoolExecutor(Config.THREADS_PER_SCHEDULED_THREAD_POOL);
|
||||
}
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR = new ScheduledThreadPoolExecutor(initializer.getScheduledThreadPoolSize(), new PoolThreadFactory("L2JU-SP-", Thread.NORM_PRIORITY));
|
||||
THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(initializer.getThreadPoolSize(), initializer.getThreadPoolSize(), 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(), new PoolThreadFactory("L2JU-IT-", Thread.NORM_PRIORITY));
|
||||
|
||||
// Feed instant pool.
|
||||
poolCount = Config.INSTANT_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
tp.setRejectedExecutionHandler(new RejectedExecutionHandlerImpl());
|
||||
tp.prestartAllCoreThreads();
|
||||
});
|
||||
|
||||
_instantPools = new ThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_instantPools[i] = new ThreadPoolExecutor(Config.THREADS_PER_INSTANT_THREAD_POOL, Config.THREADS_PER_INSTANT_THREAD_POOL, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100000));
|
||||
}
|
||||
scheduleAtFixedRate(ThreadPool::purge, 60000, 60000); // Repeats every one minute.
|
||||
|
||||
// Prestart core threads.
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
// Launch purge task.
|
||||
scheduleAtFixedRate(() ->
|
||||
{
|
||||
purge();
|
||||
}, 600000, 600000);
|
||||
|
||||
LOG.info("ThreadPoolManager: Initialized " + getPoolSize(_instantPools) + "/" + getMaximumPoolSize(_instantPools) + " instant thread(s).");
|
||||
LOG.info("ThreadPoolManager: Initialized " + getPoolSize(_scheduledPools) + "/" + getMaximumPoolSize(_scheduledPools) + " scheduled thread(s).");
|
||||
}
|
||||
|
||||
public static void purge()
|
||||
{
|
||||
for (ScheduledThreadPoolExecutor threadPool1 : _scheduledPools)
|
||||
{
|
||||
threadPool1.purge();
|
||||
}
|
||||
for (ThreadPoolExecutor threadPool2 : _instantPools)
|
||||
{
|
||||
threadPool2.purge();
|
||||
}
|
||||
LOGGER.info("ThreadPool: Initialized with");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + "/" + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + " scheduled thread(s)."); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getPoolSize() + "/" + THREAD_POOL_EXECUTOR.getMaximumPoolSize() + " thread(s).");
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a one-shot action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion.
|
||||
* Gets the scheduled thread pool executor.
|
||||
* @return the scheduled thread pool executor
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable r, long delay)
|
||||
public static ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).schedule(new TaskWrapper(r), validate(delay), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a periodic action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @param period : the period between successive executions.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will throw an exception upon cancellation.
|
||||
* Gets the thread pool executor.
|
||||
* @return the thread pool executor
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable r, long delay, long period)
|
||||
public static ThreadPoolExecutor getThreadPoolExecutor()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).scheduleAtFixedRate(new TaskWrapper(r), validate(delay), validate(period), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a stream of all the thread pools.
|
||||
* @return the stream of all the thread pools
|
||||
*/
|
||||
public static Stream<ThreadPoolExecutor> getThreadPools()
|
||||
{
|
||||
return Stream.of(SCHEDULED_THREAD_POOL_EXECUTOR, THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable task, long delay)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.schedule(new RunnableWrapper(task), delay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay at fixed rate in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleAtFixedRate(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay with fixed delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleWithFixedDelay(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given task sometime in the future.
|
||||
* @param r : the task to execute.
|
||||
* @param task the task to execute
|
||||
*/
|
||||
public static void execute(Runnable r)
|
||||
public static void execute(Runnable task)
|
||||
{
|
||||
try
|
||||
{
|
||||
getPool(_instantPools).execute(new TaskWrapper(r));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getStats()
|
||||
{
|
||||
List<String> stats = new ArrayList<>();
|
||||
for (int i = 0; i < _scheduledPools.length; i++)
|
||||
{
|
||||
final ScheduledThreadPoolExecutor threadPool = _scheduledPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
for (int i = 0; i < _instantPools.length; i++)
|
||||
{
|
||||
final ThreadPoolExecutor threadPool = _instantPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
return stats.toArray(new String[stats.size()]);
|
||||
THREAD_POOL_EXECUTOR.execute(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown thread pooling system correctly. Send different informations.
|
||||
* Submits a Runnable task for execution and returns a Future representing that task. The Future's get method will return null upon successful completion.
|
||||
* @param task the task to submit
|
||||
* @return a Future representing pending completion of the task
|
||||
*/
|
||||
public static Future<?> submit(Runnable task)
|
||||
{
|
||||
return THREAD_POOL_EXECUTOR.submit(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges all thread pools.
|
||||
*/
|
||||
public static void purge()
|
||||
{
|
||||
getThreadPools().forEach(ThreadPoolExecutor::purge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thread pools stats.
|
||||
* @return the stats
|
||||
*/
|
||||
public static List<String> getStats()
|
||||
{
|
||||
final List<String> list = new ArrayList<>(23);
|
||||
list.add("");
|
||||
list.add("Scheduled pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + SCHEDULED_THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize()); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
list.add("getCompletedTaskCount: " + SCHEDULED_THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + SCHEDULED_THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
list.add("Thread pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + THREAD_POOL_EXECUTOR.getMaximumPoolSize());
|
||||
list.add("getCompletedTaskCount: " + THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdowns the thread pools waiting for tasks to finish.
|
||||
*/
|
||||
public static void shutdown()
|
||||
{
|
||||
try
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR == null) && (THREAD_POOL_EXECUTOR == null))
|
||||
{
|
||||
LOG.info("ThreadPoolManager: Shutting down.");
|
||||
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T> : The pool type.
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the less fed pool.
|
||||
*/
|
||||
private static <T> T getPool(T[] threadPools)
|
||||
{
|
||||
return threadPools[_threadPoolRandomizer++ % threadPools.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delay : The delay to validate.
|
||||
* @return a secured value, from 0 to MAX_DELAY.
|
||||
*/
|
||||
private static long validate(long delay)
|
||||
{
|
||||
return Math.max(0, Math.min(MAX_DELAY, delay));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall actual pools size.
|
||||
*/
|
||||
private static long getPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getPoolSize();
|
||||
return;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall maximum pools size.
|
||||
*/
|
||||
private static long getMaximumPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getMaximumPoolSize();
|
||||
}
|
||||
LOGGER.info("ThreadPool: Shutting down.");
|
||||
LOGGER.info("...executing " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks.");
|
||||
LOGGER.info("...executing " + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final class TaskWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public TaskWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
tp.shutdown();
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOG.warning("Exception in " + _runnable.getClass().getName() + " execution: " + e.getMessage());
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
});
|
||||
|
||||
getThreadPools().forEach(t ->
|
||||
{
|
||||
try
|
||||
{
|
||||
t.awaitTermination(15, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
LOGGER.warning("" + e);
|
||||
}
|
||||
});
|
||||
|
||||
if (!SCHEDULED_THREAD_POOL_EXECUTOR.isTerminated())
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
|
||||
try
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.awaitTermination(5, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("...success: " + getThreadPools().allMatch(ThreadPoolExecutor::isTerminated) + " in " + (System.currentTimeMillis() - startTime) + " ms.");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks left.");
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks left.");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PoolThreadFactory implements ThreadFactory
|
||||
{
|
||||
private final String _prefix;
|
||||
private final int _priority;
|
||||
private final AtomicInteger _threadId = new AtomicInteger();
|
||||
|
||||
public PoolThreadFactory(String prefix, int priority)
|
||||
{
|
||||
_prefix = prefix;
|
||||
_priority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r)
|
||||
{
|
||||
final Thread thread = new Thread(r, _prefix + _threadId.incrementAndGet());
|
||||
thread.setPriority(_priority);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,20 +161,21 @@ public final class GameServer
|
||||
{
|
||||
final long serverLoadStart = System.currentTimeMillis();
|
||||
|
||||
printSection("ThreadPool");
|
||||
ThreadPool.initThreadPools(new GameThreadPools());
|
||||
|
||||
printSection("IdFactory");
|
||||
if (!IdFactory.getInstance().isInitialized())
|
||||
{
|
||||
_log.severe(getClass().getSimpleName() + ": Could not read object IDs from database. Please check your configuration.");
|
||||
throw new Exception("Could not initialize the ID factory!");
|
||||
}
|
||||
|
||||
printSection("ThreadPool");
|
||||
ThreadPool.init();
|
||||
EventDispatcher.getInstance();
|
||||
|
||||
new File("log/game").mkdirs();
|
||||
|
||||
// load script engines
|
||||
printSection("Scripting Engines");
|
||||
EventDispatcher.getInstance();
|
||||
ScriptEngineManager.getInstance();
|
||||
|
||||
printSection("World");
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.commons.concurrent.IThreadPoolInitializer;
|
||||
|
||||
/**
|
||||
* @author nos
|
||||
*/
|
||||
public final class GameThreadPools implements IThreadPoolInitializer
|
||||
{
|
||||
@Override
|
||||
public int getScheduledThreadPoolSize()
|
||||
{
|
||||
return Config.SCHEDULED_THREAD_POOL_COUNT != -1 ? Config.SCHEDULED_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_SCHEDULED_THREAD_POOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThreadPoolSize()
|
||||
{
|
||||
return Config.INSTANT_THREAD_POOL_COUNT != -1 ? Config.INSTANT_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_INSTANT_THREAD_POOL;
|
||||
}
|
||||
}
|
@ -25,4 +25,3 @@ What is done
|
||||
-User command expon/expoff
|
||||
-Reworked drop system
|
||||
-GeoEngine from aCis
|
||||
-Threadpool manager from aCis
|
||||
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
public interface IThreadPoolInitializer
|
||||
{
|
||||
int getScheduledThreadPoolSize();
|
||||
|
||||
int getThreadPoolSize();
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class RejectedExecutionHandlerImpl implements RejectedExecutionHandler
|
||||
{
|
||||
private static final Logger LOGGER = Logger.getLogger(RejectedExecutionHandlerImpl.class.getName());
|
||||
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
|
||||
{
|
||||
if (executor.isShutdown())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.warning(r + " from " + executor + " " + new RejectedExecutionException());
|
||||
|
||||
if (Thread.currentThread().getPriority() > Thread.NORM_PRIORITY)
|
||||
{
|
||||
new Thread(r).start();
|
||||
}
|
||||
else
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
|
||||
/**
|
||||
* @author UnAfraid
|
||||
*/
|
||||
public final class RunnableWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public RunnableWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
}
|
||||
catch (final Throwable e)
|
||||
{
|
||||
final Thread t = Thread.currentThread();
|
||||
final UncaughtExceptionHandler h = t.getUncaughtExceptionHandler();
|
||||
if (h != null)
|
||||
{
|
||||
h.uncaughtException(t, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,282 +18,250 @@ package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* This class handles thread pooling system. It relies on two ThreadPoolExecutor arrays, which poolers number is generated using config.
|
||||
* <p>
|
||||
* Those arrays hold following pools :
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Scheduled pool keeps a track about incoming, future events.</li>
|
||||
* <li>Instant pool handles short-life events.</li>
|
||||
* </ul>
|
||||
* @author _dev_ (savormix)
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class ThreadPool
|
||||
{
|
||||
protected static final Logger LOG = Logger.getLogger(ThreadPool.class.getName());
|
||||
private static final Logger LOGGER = Logger.getLogger(ThreadPool.class.getName());
|
||||
|
||||
private static final long MAX_DELAY = TimeUnit.NANOSECONDS.toMillis(Long.MAX_VALUE - System.nanoTime()) / 2;
|
||||
private static ScheduledThreadPoolExecutor SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
private static ThreadPoolExecutor THREAD_POOL_EXECUTOR;
|
||||
|
||||
private static int _threadPoolRandomizer;
|
||||
|
||||
protected static ScheduledThreadPoolExecutor[] _scheduledPools;
|
||||
protected static ThreadPoolExecutor[] _instantPools;
|
||||
|
||||
/**
|
||||
* Init the different pools, based on Config. It is launched only once, on Gameserver instance.
|
||||
*/
|
||||
public static void init()
|
||||
public static void initThreadPools(IThreadPoolInitializer initializer) throws Exception
|
||||
{
|
||||
// Feed scheduled pool.
|
||||
int poolCount = Config.SCHEDULED_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR != null) || (THREAD_POOL_EXECUTOR != null))
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
throw new Exception("The thread pool has been already initialized!");
|
||||
}
|
||||
|
||||
_scheduledPools = new ScheduledThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_scheduledPools[i] = new ScheduledThreadPoolExecutor(Config.THREADS_PER_SCHEDULED_THREAD_POOL);
|
||||
}
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR = new ScheduledThreadPoolExecutor(initializer.getScheduledThreadPoolSize(), new PoolThreadFactory("L2JU-SP-", Thread.NORM_PRIORITY));
|
||||
THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(initializer.getThreadPoolSize(), initializer.getThreadPoolSize(), 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(), new PoolThreadFactory("L2JU-IT-", Thread.NORM_PRIORITY));
|
||||
|
||||
// Feed instant pool.
|
||||
poolCount = Config.INSTANT_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
tp.setRejectedExecutionHandler(new RejectedExecutionHandlerImpl());
|
||||
tp.prestartAllCoreThreads();
|
||||
});
|
||||
|
||||
_instantPools = new ThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_instantPools[i] = new ThreadPoolExecutor(Config.THREADS_PER_INSTANT_THREAD_POOL, Config.THREADS_PER_INSTANT_THREAD_POOL, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100000));
|
||||
}
|
||||
scheduleAtFixedRate(ThreadPool::purge, 60000, 60000); // Repeats every one minute.
|
||||
|
||||
// Prestart core threads.
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
// Launch purge task.
|
||||
scheduleAtFixedRate(() ->
|
||||
{
|
||||
purge();
|
||||
}, 600000, 600000);
|
||||
|
||||
LOG.info("ThreadPoolManager: Initialized " + getPoolSize(_instantPools) + "/" + getMaximumPoolSize(_instantPools) + " instant thread(s).");
|
||||
LOG.info("ThreadPoolManager: Initialized " + getPoolSize(_scheduledPools) + "/" + getMaximumPoolSize(_scheduledPools) + " scheduled thread(s).");
|
||||
}
|
||||
|
||||
public static void purge()
|
||||
{
|
||||
for (ScheduledThreadPoolExecutor threadPool1 : _scheduledPools)
|
||||
{
|
||||
threadPool1.purge();
|
||||
}
|
||||
for (ThreadPoolExecutor threadPool2 : _instantPools)
|
||||
{
|
||||
threadPool2.purge();
|
||||
}
|
||||
LOGGER.info("ThreadPool: Initialized with");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + "/" + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + " scheduled thread(s)."); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getPoolSize() + "/" + THREAD_POOL_EXECUTOR.getMaximumPoolSize() + " thread(s).");
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a one-shot action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion.
|
||||
* Gets the scheduled thread pool executor.
|
||||
* @return the scheduled thread pool executor
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable r, long delay)
|
||||
public static ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).schedule(new TaskWrapper(r), validate(delay), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a periodic action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @param period : the period between successive executions.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will throw an exception upon cancellation.
|
||||
* Gets the thread pool executor.
|
||||
* @return the thread pool executor
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable r, long delay, long period)
|
||||
public static ThreadPoolExecutor getThreadPoolExecutor()
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).scheduleAtFixedRate(new TaskWrapper(r), validate(delay), validate(period), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a stream of all the thread pools.
|
||||
* @return the stream of all the thread pools
|
||||
*/
|
||||
public static Stream<ThreadPoolExecutor> getThreadPools()
|
||||
{
|
||||
return Stream.of(SCHEDULED_THREAD_POOL_EXECUTOR, THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable task, long delay)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.schedule(new RunnableWrapper(task), delay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay at fixed rate in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleAtFixedRate(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay with fixed delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleWithFixedDelay(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given task sometime in the future.
|
||||
* @param r : the task to execute.
|
||||
* @param task the task to execute
|
||||
*/
|
||||
public static void execute(Runnable r)
|
||||
public static void execute(Runnable task)
|
||||
{
|
||||
try
|
||||
{
|
||||
getPool(_instantPools).execute(new TaskWrapper(r));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getStats()
|
||||
{
|
||||
List<String> stats = new ArrayList<>();
|
||||
for (int i = 0; i < _scheduledPools.length; i++)
|
||||
{
|
||||
final ScheduledThreadPoolExecutor threadPool = _scheduledPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
for (int i = 0; i < _instantPools.length; i++)
|
||||
{
|
||||
final ThreadPoolExecutor threadPool = _instantPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
return stats.toArray(new String[stats.size()]);
|
||||
THREAD_POOL_EXECUTOR.execute(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown thread pooling system correctly. Send different informations.
|
||||
* Submits a Runnable task for execution and returns a Future representing that task. The Future's get method will return null upon successful completion.
|
||||
* @param task the task to submit
|
||||
* @return a Future representing pending completion of the task
|
||||
*/
|
||||
public static Future<?> submit(Runnable task)
|
||||
{
|
||||
return THREAD_POOL_EXECUTOR.submit(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges all thread pools.
|
||||
*/
|
||||
public static void purge()
|
||||
{
|
||||
getThreadPools().forEach(ThreadPoolExecutor::purge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thread pools stats.
|
||||
* @return the stats
|
||||
*/
|
||||
public static List<String> getStats()
|
||||
{
|
||||
final List<String> list = new ArrayList<>(23);
|
||||
list.add("");
|
||||
list.add("Scheduled pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + SCHEDULED_THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize()); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
list.add("getCompletedTaskCount: " + SCHEDULED_THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + SCHEDULED_THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
list.add("Thread pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + THREAD_POOL_EXECUTOR.getMaximumPoolSize());
|
||||
list.add("getCompletedTaskCount: " + THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdowns the thread pools waiting for tasks to finish.
|
||||
*/
|
||||
public static void shutdown()
|
||||
{
|
||||
try
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR == null) && (THREAD_POOL_EXECUTOR == null))
|
||||
{
|
||||
LOG.info("ThreadPoolManager: Shutting down.");
|
||||
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T> : The pool type.
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the less fed pool.
|
||||
*/
|
||||
private static <T> T getPool(T[] threadPools)
|
||||
{
|
||||
return threadPools[_threadPoolRandomizer++ % threadPools.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delay : The delay to validate.
|
||||
* @return a secured value, from 0 to MAX_DELAY.
|
||||
*/
|
||||
private static long validate(long delay)
|
||||
{
|
||||
return Math.max(0, Math.min(MAX_DELAY, delay));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall actual pools size.
|
||||
*/
|
||||
private static long getPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getPoolSize();
|
||||
return;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall maximum pools size.
|
||||
*/
|
||||
private static long getMaximumPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getMaximumPoolSize();
|
||||
}
|
||||
LOGGER.info("ThreadPool: Shutting down.");
|
||||
LOGGER.info("...executing " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks.");
|
||||
LOGGER.info("...executing " + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final class TaskWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public TaskWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
tp.shutdown();
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOG.warning("Exception in " + _runnable.getClass().getName() + " execution: " + e.getMessage());
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
});
|
||||
|
||||
getThreadPools().forEach(t ->
|
||||
{
|
||||
try
|
||||
{
|
||||
t.awaitTermination(15, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
LOGGER.warning("" + e);
|
||||
}
|
||||
});
|
||||
|
||||
if (!SCHEDULED_THREAD_POOL_EXECUTOR.isTerminated())
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
|
||||
try
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.awaitTermination(5, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("...success: " + getThreadPools().allMatch(ThreadPoolExecutor::isTerminated) + " in " + (System.currentTimeMillis() - startTime) + " ms.");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks left.");
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks left.");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class PoolThreadFactory implements ThreadFactory
|
||||
{
|
||||
private final String _prefix;
|
||||
private final int _priority;
|
||||
private final AtomicInteger _threadId = new AtomicInteger();
|
||||
|
||||
public PoolThreadFactory(String prefix, int priority)
|
||||
{
|
||||
_prefix = prefix;
|
||||
_priority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r)
|
||||
{
|
||||
final Thread thread = new Thread(r, _prefix + _threadId.incrementAndGet());
|
||||
thread.setPriority(_priority);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,18 +173,19 @@ public class GameServer
|
||||
{
|
||||
final long serverLoadStart = System.currentTimeMillis();
|
||||
|
||||
printSection("ThreadPool");
|
||||
ThreadPool.initThreadPools(new GameThreadPools());
|
||||
|
||||
printSection("IdFactory");
|
||||
if (!IdFactory.getInstance().isInitialized())
|
||||
{
|
||||
LOGGER.severe(getClass().getSimpleName() + ": Could not read object IDs from database. Please check your configuration.");
|
||||
throw new Exception("Could not initialize the ID factory");
|
||||
}
|
||||
|
||||
printSection("ThreadPool");
|
||||
ThreadPool.init();
|
||||
EventDispatcher.getInstance();
|
||||
|
||||
// load script engines
|
||||
printSection("Scripting Engines");
|
||||
EventDispatcher.getInstance();
|
||||
ScriptEngineManager.getInstance();
|
||||
|
||||
printSection("Telnet");
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.commons.concurrent.IThreadPoolInitializer;
|
||||
|
||||
/**
|
||||
* @author nos
|
||||
*/
|
||||
public final class GameThreadPools implements IThreadPoolInitializer
|
||||
{
|
||||
@Override
|
||||
public int getScheduledThreadPoolSize()
|
||||
{
|
||||
return Config.SCHEDULED_THREAD_POOL_COUNT != -1 ? Config.SCHEDULED_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_SCHEDULED_THREAD_POOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThreadPoolSize()
|
||||
{
|
||||
return Config.INSTANT_THREAD_POOL_COUNT != -1 ? Config.INSTANT_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_INSTANT_THREAD_POOL;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
public interface IThreadPoolInitializer
|
||||
{
|
||||
int getScheduledThreadPoolSize();
|
||||
|
||||
int getThreadPoolSize();
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class RejectedExecutionHandlerImpl implements RejectedExecutionHandler
|
||||
{
|
||||
private static final Logger LOGGER = Logger.getLogger(RejectedExecutionHandlerImpl.class.getName());
|
||||
|
||||
@Override
|
||||
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
|
||||
{
|
||||
if (executor.isShutdown())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.warning(r + " from " + executor + " " + new RejectedExecutionException());
|
||||
|
||||
if (Thread.currentThread().getPriority() > Thread.NORM_PRIORITY)
|
||||
{
|
||||
new Thread(r).start();
|
||||
}
|
||||
else
|
||||
{
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
|
||||
/**
|
||||
* @author UnAfraid
|
||||
*/
|
||||
public final class RunnableWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public RunnableWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
}
|
||||
catch (final Throwable e)
|
||||
{
|
||||
final Thread t = Thread.currentThread();
|
||||
final UncaughtExceptionHandler h = t.getUncaughtExceptionHandler();
|
||||
if (h != null)
|
||||
{
|
||||
h.uncaughtException(t, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,299 +1,267 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
|
||||
/**
|
||||
* This class handles thread pooling system. It relies on two ThreadPoolExecutor arrays, which poolers number is generated using config.
|
||||
* <p>
|
||||
* Those arrays hold following pools :
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Scheduled pool keeps a track about incoming, future events.</li>
|
||||
* <li>Instant pool handles short-life events.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class ThreadPool
|
||||
{
|
||||
protected static final Logger LOG = Logger.getLogger(ThreadPool.class.getName());
|
||||
|
||||
private static final long MAX_DELAY = TimeUnit.NANOSECONDS.toMillis(Long.MAX_VALUE - System.nanoTime()) / 2;
|
||||
|
||||
private static int _threadPoolRandomizer;
|
||||
|
||||
protected static ScheduledThreadPoolExecutor[] _scheduledPools;
|
||||
protected static ThreadPoolExecutor[] _instantPools;
|
||||
|
||||
/**
|
||||
* Init the different pools, based on Config. It is launched only once, on Gameserver instance.
|
||||
*/
|
||||
public static void init()
|
||||
{
|
||||
// Feed scheduled pool.
|
||||
int poolCount = Config.SCHEDULED_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
|
||||
_scheduledPools = new ScheduledThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_scheduledPools[i] = new ScheduledThreadPoolExecutor(Config.THREADS_PER_SCHEDULED_THREAD_POOL);
|
||||
}
|
||||
|
||||
// Feed instant pool.
|
||||
poolCount = Config.INSTANT_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
|
||||
_instantPools = new ThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_instantPools[i] = new ThreadPoolExecutor(Config.THREADS_PER_INSTANT_THREAD_POOL, Config.THREADS_PER_INSTANT_THREAD_POOL, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100000));
|
||||
}
|
||||
|
||||
// Prestart core threads.
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
// Launch purge task.
|
||||
scheduleAtFixedRate(() ->
|
||||
{
|
||||
purge();
|
||||
}, 600000, 600000);
|
||||
|
||||
LOG.info("ThreadPoolManager: Initialized " + getPoolSize(_instantPools) + "/" + getMaximumPoolSize(_instantPools) + " instant thread(s).");
|
||||
LOG.info("ThreadPoolManager: Initialized " + getPoolSize(_scheduledPools) + "/" + getMaximumPoolSize(_scheduledPools) + " scheduled thread(s).");
|
||||
}
|
||||
|
||||
public static void purge()
|
||||
{
|
||||
for (ScheduledThreadPoolExecutor threadPool1 : _scheduledPools)
|
||||
{
|
||||
threadPool1.purge();
|
||||
}
|
||||
for (ThreadPoolExecutor threadPool2 : _instantPools)
|
||||
{
|
||||
threadPool2.purge();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a one-shot action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion.
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable r, long delay)
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).schedule(new TaskWrapper(r), validate(delay), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a periodic action that becomes enabled after a delay. The pool is chosen based on pools activity.
|
||||
* @param r : the task to execute.
|
||||
* @param delay : the time from now to delay execution.
|
||||
* @param period : the period between successive executions.
|
||||
* @return a ScheduledFuture representing pending completion of the task and whose get() method will throw an exception upon cancellation.
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable r, long delay, long period)
|
||||
{
|
||||
try
|
||||
{
|
||||
return getPool(_scheduledPools).scheduleAtFixedRate(new TaskWrapper(r), validate(delay), validate(period), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given task sometime in the future.
|
||||
* @param r : the task to execute.
|
||||
*/
|
||||
public static void execute(Runnable r)
|
||||
{
|
||||
try
|
||||
{
|
||||
getPool(_instantPools).execute(new TaskWrapper(r));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getStats()
|
||||
{
|
||||
List<String> stats = new ArrayList<>();
|
||||
for (int i = 0; i < _scheduledPools.length; i++)
|
||||
{
|
||||
final ScheduledThreadPoolExecutor threadPool = _scheduledPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
for (int i = 0; i < _instantPools.length; i++)
|
||||
{
|
||||
final ThreadPoolExecutor threadPool = _instantPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
return stats.toArray(new String[stats.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown thread pooling system correctly. Send different informations.
|
||||
*/
|
||||
public static void shutdown()
|
||||
{
|
||||
try
|
||||
{
|
||||
LOG.info("ThreadPoolManager: Shutting down.");
|
||||
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T> : The pool type.
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the less fed pool.
|
||||
*/
|
||||
private static <T> T getPool(T[] threadPools)
|
||||
{
|
||||
return threadPools[_threadPoolRandomizer++ % threadPools.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delay : The delay to validate.
|
||||
* @return a secured value, from 0 to MAX_DELAY.
|
||||
*/
|
||||
private static long validate(long delay)
|
||||
{
|
||||
return Math.max(0, Math.min(MAX_DELAY, delay));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall actual pools size.
|
||||
*/
|
||||
private static long getPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getPoolSize();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall maximum pools size.
|
||||
*/
|
||||
private static long getMaximumPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getMaximumPoolSize();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final class TaskWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public TaskWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
LOG.warning("Exception in " + _runnable.getClass().getName() + " execution: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.commons.concurrent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author _dev_ (savormix)
|
||||
* @author NB4L1
|
||||
*/
|
||||
public final class ThreadPool
|
||||
{
|
||||
private static final Logger LOGGER = Logger.getLogger(ThreadPool.class.getName());
|
||||
|
||||
private static ScheduledThreadPoolExecutor SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
private static ThreadPoolExecutor THREAD_POOL_EXECUTOR;
|
||||
|
||||
public static void initThreadPools(IThreadPoolInitializer initializer) throws Exception
|
||||
{
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR != null) || (THREAD_POOL_EXECUTOR != null))
|
||||
{
|
||||
throw new Exception("The thread pool has been already initialized!");
|
||||
}
|
||||
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR = new ScheduledThreadPoolExecutor(initializer.getScheduledThreadPoolSize(), new PoolThreadFactory("L2JU-SP-", Thread.NORM_PRIORITY));
|
||||
THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(initializer.getThreadPoolSize(), initializer.getThreadPoolSize(), 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(), new PoolThreadFactory("L2JU-IT-", Thread.NORM_PRIORITY));
|
||||
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
tp.setRejectedExecutionHandler(new RejectedExecutionHandlerImpl());
|
||||
tp.prestartAllCoreThreads();
|
||||
});
|
||||
|
||||
scheduleAtFixedRate(ThreadPool::purge, 60000, 60000); // Repeats every one minute.
|
||||
|
||||
LOGGER.info("ThreadPool: Initialized with");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + "/" + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize() + " scheduled thread(s)."); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getPoolSize() + "/" + THREAD_POOL_EXECUTOR.getMaximumPoolSize() + " thread(s).");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scheduled thread pool executor.
|
||||
* @return the scheduled thread pool executor
|
||||
*/
|
||||
public static ScheduledThreadPoolExecutor getScheduledThreadPoolExecutor()
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thread pool executor.
|
||||
* @return the thread pool executor
|
||||
*/
|
||||
public static ThreadPoolExecutor getThreadPoolExecutor()
|
||||
{
|
||||
return THREAD_POOL_EXECUTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a stream of all the thread pools.
|
||||
* @return the stream of all the thread pools
|
||||
*/
|
||||
public static Stream<ThreadPoolExecutor> getThreadPools()
|
||||
{
|
||||
return Stream.of(SCHEDULED_THREAD_POOL_EXECUTOR, THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> schedule(Runnable task, long delay)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.schedule(new RunnableWrapper(task), delay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay at fixed rate in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleAtFixedRate(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed after the given delay with fixed delay in milliseconds.
|
||||
* @param task the task to execute
|
||||
* @param delay the delay in the given time unit
|
||||
* @param period the period in the given time unit
|
||||
* @return a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
|
||||
*/
|
||||
public static ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay, long period)
|
||||
{
|
||||
return SCHEDULED_THREAD_POOL_EXECUTOR.scheduleWithFixedDelay(new RunnableWrapper(task), delay, period, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the given task sometime in the future.
|
||||
* @param task the task to execute
|
||||
*/
|
||||
public static void execute(Runnable task)
|
||||
{
|
||||
THREAD_POOL_EXECUTOR.execute(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits a Runnable task for execution and returns a Future representing that task. The Future's get method will return null upon successful completion.
|
||||
* @param task the task to submit
|
||||
* @return a Future representing pending completion of the task
|
||||
*/
|
||||
public static Future<?> submit(Runnable task)
|
||||
{
|
||||
return THREAD_POOL_EXECUTOR.submit(new RunnableWrapper(task));
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges all thread pools.
|
||||
*/
|
||||
public static void purge()
|
||||
{
|
||||
getThreadPools().forEach(ThreadPoolExecutor::purge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the thread pools stats.
|
||||
* @return the stats
|
||||
*/
|
||||
public static List<String> getStats()
|
||||
{
|
||||
final List<String> list = new ArrayList<>(23);
|
||||
list.add("");
|
||||
list.add("Scheduled pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + SCHEDULED_THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + SCHEDULED_THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getCorePoolSize()); // ScheduledThreadPoolExecutor has a fixed number of threads and maximumPoolSize has no effect
|
||||
list.add("getCompletedTaskCount: " + SCHEDULED_THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + SCHEDULED_THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
list.add("Thread pool:");
|
||||
list.add("=================================================");
|
||||
list.add("getActiveCount: ...... " + THREAD_POOL_EXECUTOR.getActiveCount());
|
||||
list.add("getCorePoolSize: ..... " + THREAD_POOL_EXECUTOR.getCorePoolSize());
|
||||
list.add("getPoolSize: ......... " + THREAD_POOL_EXECUTOR.getPoolSize());
|
||||
list.add("getLargestPoolSize: .. " + THREAD_POOL_EXECUTOR.getLargestPoolSize());
|
||||
list.add("getMaximumPoolSize: .. " + THREAD_POOL_EXECUTOR.getMaximumPoolSize());
|
||||
list.add("getCompletedTaskCount: " + THREAD_POOL_EXECUTOR.getCompletedTaskCount());
|
||||
list.add("getQueuedTaskCount: .. " + THREAD_POOL_EXECUTOR.getQueue().size());
|
||||
list.add("getTaskCount: ........ " + THREAD_POOL_EXECUTOR.getTaskCount());
|
||||
list.add("");
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdowns the thread pools waiting for tasks to finish.
|
||||
*/
|
||||
public static void shutdown()
|
||||
{
|
||||
if ((SCHEDULED_THREAD_POOL_EXECUTOR == null) && (THREAD_POOL_EXECUTOR == null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
LOGGER.info("ThreadPool: Shutting down.");
|
||||
LOGGER.info("...executing " + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks.");
|
||||
LOGGER.info("...executing " + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks.");
|
||||
|
||||
getThreadPools().forEach(tp ->
|
||||
{
|
||||
try
|
||||
{
|
||||
tp.shutdown();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
});
|
||||
|
||||
getThreadPools().forEach(t ->
|
||||
{
|
||||
try
|
||||
{
|
||||
t.awaitTermination(15, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
LOGGER.warning("" + e);
|
||||
}
|
||||
});
|
||||
|
||||
if (!SCHEDULED_THREAD_POOL_EXECUTOR.isTerminated())
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
|
||||
try
|
||||
{
|
||||
SCHEDULED_THREAD_POOL_EXECUTOR.awaitTermination(5, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOGGER.warning("" + t);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("...success: " + getThreadPools().allMatch(ThreadPoolExecutor::isTerminated) + " in " + (System.currentTimeMillis() - startTime) + " ms.");
|
||||
LOGGER.info("..." + SCHEDULED_THREAD_POOL_EXECUTOR.getQueue().size() + " scheduled tasks left.");
|
||||
LOGGER.info("..." + THREAD_POOL_EXECUTOR.getQueue().size() + " tasks left.");
|
||||
}
|
||||
|
||||
private static final class PoolThreadFactory implements ThreadFactory
|
||||
{
|
||||
private final String _prefix;
|
||||
private final int _priority;
|
||||
private final AtomicInteger _threadId = new AtomicInteger();
|
||||
|
||||
public PoolThreadFactory(String prefix, int priority)
|
||||
{
|
||||
_prefix = prefix;
|
||||
_priority = priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r)
|
||||
{
|
||||
final Thread thread = new Thread(r, _prefix + _threadId.incrementAndGet());
|
||||
thread.setPriority(_priority);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,18 +173,19 @@ public class GameServer
|
||||
{
|
||||
final long serverLoadStart = System.currentTimeMillis();
|
||||
|
||||
printSection("ThreadPool");
|
||||
ThreadPool.initThreadPools(new GameThreadPools());
|
||||
|
||||
printSection("IdFactory");
|
||||
if (!IdFactory.getInstance().isInitialized())
|
||||
{
|
||||
LOGGER.severe(getClass().getSimpleName() + ": Could not read object IDs from database. Please check your configuration.");
|
||||
throw new Exception("Could not initialize the ID factory");
|
||||
}
|
||||
|
||||
printSection("ThreadPool");
|
||||
ThreadPool.init();
|
||||
EventDispatcher.getInstance();
|
||||
|
||||
// load script engines
|
||||
printSection("Scripting Engines");
|
||||
EventDispatcher.getInstance();
|
||||
ScriptEngineManager.getInstance();
|
||||
|
||||
printSection("Telnet");
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.commons.concurrent.IThreadPoolInitializer;
|
||||
|
||||
/**
|
||||
* @author nos
|
||||
*/
|
||||
public final class GameThreadPools implements IThreadPoolInitializer
|
||||
{
|
||||
@Override
|
||||
public int getScheduledThreadPoolSize()
|
||||
{
|
||||
return Config.SCHEDULED_THREAD_POOL_COUNT != -1 ? Config.SCHEDULED_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_SCHEDULED_THREAD_POOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThreadPoolSize()
|
||||
{
|
||||
return Config.INSTANT_THREAD_POOL_COUNT != -1 ? Config.INSTANT_THREAD_POOL_COUNT : Runtime.getRuntime().availableProcessors() * Config.THREADS_PER_INSTANT_THREAD_POOL;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user