Created
July 11, 2015 16:42
-
-
Save Kiskae/5c44ea146857ca173170 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.google.common.base.Supplier; | |
import com.google.common.collect.ImmutableList; | |
import com.google.common.util.concurrent.ListenableFuture; | |
import com.google.common.util.concurrent.ListenableFutureTask; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import org.spongepowered.api.Game; | |
import org.spongepowered.api.plugin.PluginContainer; | |
import org.spongepowered.api.service.scheduler.SchedulerService; | |
import org.spongepowered.api.service.scheduler.Task; | |
import org.spongepowered.api.service.scheduler.TaskBuilder; | |
import javax.annotation.Nonnull; | |
import javax.annotation.Nullable; | |
import java.util.List; | |
import java.util.concurrent.*; | |
import static com.google.common.base.Preconditions.checkNotNull; | |
/** | |
* A delegating ExecutionService that schedules all its tasks on Sponge's {@link SchedulerService}. | |
* Any tasks scheduled through this service will run synchronously on the main Server Thread and will thus be able to | |
* safely modify game data. | |
* <br /> | |
* When using this ExecutorService with other asynchronous libraries, be careful of systems that inherit the | |
* ExecutorService from their parents, since any child tasks will also run on Sponge's main thread. | |
* <br /> | |
* In terms of contracts this ExecutorService cannot be shut down since it does not directly control the execution | |
* environment. | |
* | |
* @author Kiskae <kiskae@serverpeon.net> | |
*/ | |
public class SpongeExecutorService extends AbstractExecutorService implements ScheduledExecutorService { | |
private final static Logger log = LoggerFactory.getLogger(SpongeExecutorService.class); | |
private final SchedulerService scheduler; | |
private final PluginContainer plugin; | |
private final Supplier<String> taskNamer; | |
private SpongeExecutorService( | |
@Nonnull SchedulerService scheduler, | |
@Nonnull PluginContainer plugin, | |
@Nullable Supplier<String> taskNamer | |
) { | |
this.scheduler = scheduler; | |
this.plugin = plugin; | |
this.taskNamer = taskNamer; | |
} | |
/** | |
* Utility method to create a ExecutorService using the given {@link SchedulerService} with named tasks tied to | |
* the given {@link PluginContainer}. | |
* | |
* @param service Service on which the tasks will be scheduled. | |
* @param pc The plugin to which the tasks scheduled by this executor will be tied. | |
* @param taskNamer Supplier of custom names for the tasks that are scheduled, if null it uses the default naming | |
* scheme defined by {@link TaskBuilder#name(String)} | |
* @return An executor that will schedule all executions on the given {@link SchedulerService} | |
*/ | |
@Nonnull | |
public static SpongeExecutorService create( | |
@Nonnull SchedulerService service, | |
@Nonnull PluginContainer pc, | |
@Nullable Supplier<String> taskNamer | |
) { | |
return new SpongeExecutorService( | |
checkNotNull(service, "service == NULL"), | |
checkNotNull(pc, "pc == NULL"), | |
taskNamer | |
); | |
} | |
/** | |
* Utility method to create a ExecutorService using the given {@link SchedulerService} with tasks tied to | |
* the given {@link PluginContainer}. | |
* | |
* @param service Service on which the tasks will be scheduled. | |
* @param pc The plugin to which the tasks scheduled by this executor will be tied. | |
* @return An executor that will schedule all executions on the given {@link SchedulerService} | |
*/ | |
@Nonnull | |
public static SpongeExecutorService create( | |
@Nonnull SchedulerService service, | |
@Nonnull PluginContainer pc | |
) { | |
return create(service, pc, null); | |
} | |
/** | |
* Utility method to create a ExecutorService using Sponge's default {@link SchedulerService} with tasks tied to | |
* the given {@link PluginContainer}. | |
* | |
* @param game Instance of the current Sponge {@link Game} | |
* @param pc The plugin to which the tasks scheduled by this executor will be tied. | |
* @return An executor that will schedule all executions on Sponge's {@link SchedulerService} | |
*/ | |
@Nonnull | |
public static SpongeExecutorService create( | |
@Nonnull Game game, | |
@Nonnull PluginContainer pc | |
) { | |
return create(checkNotNull(game, "game == NULL").getScheduler(), pc); | |
} | |
@Override | |
public void shutdown() { | |
//NOOP | |
} | |
@Override | |
public List<Runnable> shutdownNow() { | |
return ImmutableList.of(); | |
} | |
@Override | |
public boolean isShutdown() { | |
return false; | |
} | |
@Override | |
public boolean isTerminated() { | |
return false; | |
} | |
@Override | |
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { | |
return false; | |
} | |
@Override | |
public void execute(Runnable command) { | |
this.setupTask(command) | |
.submit(this.plugin.getInstance()); | |
} | |
@Override | |
public SpongeFuture<?> schedule(final Runnable command, long delay, TimeUnit unit) { | |
final ListenableFutureTask<?> runnable = ListenableFutureTask.create(command, null); | |
final Task task = this.setupTask(runnable) | |
.delay(delay, unit) | |
.submit(this.plugin.getInstance()); | |
return new SpongeFuture<>(runnable, task); | |
} | |
@Override | |
public <V> SpongeFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { | |
final ListenableFutureTask<V> runnable = ListenableFutureTask.create(callable); | |
final Task task = this.setupTask(runnable) | |
.delay(delay, unit) | |
.submit(this.plugin.getInstance()); | |
return new SpongeFuture<>(runnable, task); | |
} | |
@Override | |
public SpongeFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { | |
final ListenableFutureTask<?> runnable = ListenableFutureTask.create(command, null); | |
final Task task = this.setupTask(runnable) | |
.delay(initialDelay, unit) | |
.interval(period, unit) | |
.submit(this.plugin.getInstance()); | |
return new SpongeFuture<>(runnable, task); | |
} | |
/** | |
* Behaves exactly like {@link #scheduleAtFixedRate(Runnable, long, long, TimeUnit)} since the backing scheduling | |
* is not customizable enough to make the distinction. | |
* <br /> | |
* {@inheritDoc} | |
*/ | |
@Override | |
public SpongeFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { | |
//Since we dont have full control over the execution, the contract needs to be a little broken | |
return this.scheduleAtFixedRate(command, initialDelay, delay, unit); | |
} | |
private TaskBuilder setupTask(Runnable run) { | |
log.debug("Scheduling runnable for {}: {}", plugin.getName(), run); | |
final TaskBuilder builder = this.scheduler.getTaskBuilder() | |
.execute(run); | |
if (taskNamer != null) { | |
return builder.name(this.taskNamer.get()); | |
} else { | |
return builder; | |
} | |
} | |
/** | |
* A {@link ScheduledFuture} that is backed by Sponge's {@link Task} | |
*/ | |
public static class SpongeFuture<V> implements ScheduledFuture<V> { | |
private final ListenableFuture<V> runnable; | |
private final Task task; | |
private SpongeFuture(ListenableFuture<V> runnable, Task task) { | |
this.runnable = runnable; | |
this.task = task; | |
} | |
/** | |
* @return The underlying task that was submitted to {@link SchedulerService} | |
*/ | |
@Nonnull | |
public Task getTask() { | |
return this.task; | |
} | |
@Override | |
public long getDelay(TimeUnit unit) { | |
return unit.convert(this.task.getDelay(), TimeUnit.MILLISECONDS); | |
} | |
@Override | |
public int compareTo(Delayed o) { | |
return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS)); | |
} | |
@Override | |
public boolean cancel(boolean mayInterruptIfRunning) { | |
return this.runnable.cancel(mayInterruptIfRunning); | |
} | |
@Override | |
public boolean isCancelled() { | |
return this.runnable.isCancelled(); | |
} | |
@Override | |
public boolean isDone() { | |
return this.runnable.isDone(); | |
} | |
@Override | |
public V get() throws InterruptedException, ExecutionException { | |
return this.runnable.get(); | |
} | |
@Override | |
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { | |
return this.runnable.get(timeout, unit); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment