package com.asteria.game.sync; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Phaser; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy; import com.asteria.game.GameConstants; import com.google.common.util.concurrent.ThreadFactoryBuilder; /** * A synchronization executor that executes {@link GameSyncTask}s. These have * support for both concurrent and sequential synchronization tasks, and are * smart enough to determine when each should be used on a task-to-task basis. * * @author lare96 <http://github.org/lare96> */ public final class GameSyncExecutor { /** * The executor that will execute the synchronization tasks. This value may * or may not be {@code null}. */ private final ExecutorService service; /** * The synchronizer that ensures that the thread waits until tasks are * completed before proceeding. This value may or may not be {@code null}. */ private final Phaser phaser; /** * Creates a new {@link GameSyncExecutor}. It automatically determines how * many threads; if any, are needed for game synchronization. */ public GameSyncExecutor() { this.service = GameConstants.CONCURRENCY ? create(Runtime.getRuntime().availableProcessors()) : null; this.phaser = GameConstants.CONCURRENCY ? new Phaser(1) : null; } /** * Submits {@code syncTask} to be executed as a synchronization task under * this executor. This method can and probably will block the calling thread * until it completes. * * @param syncTask * the synchronization task to execute. */ public void sync(GameSyncTask syncTask) { if (service == null || phaser == null || !syncTask.isConcurrent()) { for (int index = 1; index < syncTask.getCapacity(); index++) { if (!syncTask.checkIndex(index)) continue; syncTask.execute(index); } return; } phaser.bulkRegister(syncTask.getAmount()); for (int index = 1; index < syncTask.getCapacity(); index++) { if (!syncTask.checkIndex(index)) continue; final int finalIndex = index; service.execute(new Runnable() { @Override public void run() { try { syncTask.execute(finalIndex); } finally { phaser.arriveAndDeregister(); } } }); } phaser.arriveAndAwaitAdvance(); } /** * Creates and configures the update service for this game sync executor. * The returned executor is <b>unconfigurable</b> meaning it's configuration * can no longer be modified. * * @param nThreads * the amount of threads to create this service. * @return the newly created and configured service. */ private ExecutorService create(int nThreads) { if (nThreads <= 1) return null; ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(nThreads); executor.setRejectedExecutionHandler(new CallerRunsPolicy()); executor.setThreadFactory(new ThreadFactoryBuilder().setNameFormat("GameSyncThread").build()); return Executors.unconfigurableExecutorService(executor); } }