Coverage Summary for Class: AbstractScheduledService (com.google.common.util.concurrent)

Class Method, % Line, %
AbstractScheduledService 0% (0/18) 0% (0/24)
AbstractScheduledService$1 0% (0/3) 0% (0/3)
AbstractScheduledService$1ThreadFactoryImpl 0% (0/2) 0% (0/2)
AbstractScheduledService$CustomScheduler 0% (0/2) 0% (0/2)
AbstractScheduledService$CustomScheduler$ReschedulableCallable 0% (0/5) 0% (0/30)
AbstractScheduledService$CustomScheduler$Schedule 0% (0/2) 0% (0/4)
AbstractScheduledService$CustomScheduler$SupplantableFuture 0% (0/4) 0% (0/12)
AbstractScheduledService$FutureAsCancellable 0% (0/3) 0% (0/4)
AbstractScheduledService$Scheduler 0% (0/6) 0% (0/12)
AbstractScheduledService$Scheduler$1 0% (0/2) 0% (0/3)
AbstractScheduledService$Scheduler$2 0% (0/2) 0% (0/3)
AbstractScheduledService$ServiceDelegate 0% (0/5) 0% (0/12)
AbstractScheduledService$ServiceDelegate$1 0% (0/2) 0% (0/2)
AbstractScheduledService$ServiceDelegate$2 0% (0/2) 0% (0/11)
AbstractScheduledService$ServiceDelegate$3 0% (0/2) 0% (0/10)
AbstractScheduledService$ServiceDelegate$Task 0% (0/2) 0% (0/13)
Total 0% (0/62) 0% (0/147)


1 /* 2  * Copyright (C) 2011 The Guava Authors 3  * 4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5  * in compliance with the License. You may obtain a copy of the License at 6  * 7  * http://www.apache.org/licenses/LICENSE-2.0 8  * 9  * Unless required by applicable law or agreed to in writing, software distributed under the License 10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11  * or implied. See the License for the specific language governing permissions and limitations under 12  * the License. 13  */ 14  15 package com.google.common.util.concurrent; 16  17 import static com.google.common.base.Preconditions.checkArgument; 18 import static com.google.common.base.Preconditions.checkNotNull; 19 import static com.google.common.util.concurrent.Futures.immediateCancelledFuture; 20 import static com.google.common.util.concurrent.Internal.toNanosSaturated; 21 import static com.google.common.util.concurrent.MoreExecutors.directExecutor; 22 import static java.util.Objects.requireNonNull; 23  24 import com.google.common.annotations.GwtIncompatible; 25 import com.google.common.base.Supplier; 26 import com.google.errorprone.annotations.CanIgnoreReturnValue; 27 import com.google.errorprone.annotations.concurrent.GuardedBy; 28 import com.google.j2objc.annotations.WeakOuter; 29 import java.time.Duration; 30 import java.util.concurrent.Callable; 31 import java.util.concurrent.Executor; 32 import java.util.concurrent.Executors; 33 import java.util.concurrent.Future; 34 import java.util.concurrent.ScheduledExecutorService; 35 import java.util.concurrent.ScheduledFuture; 36 import java.util.concurrent.ThreadFactory; 37 import java.util.concurrent.TimeUnit; 38 import java.util.concurrent.TimeoutException; 39 import java.util.concurrent.locks.ReentrantLock; 40 import java.util.logging.Level; 41 import java.util.logging.Logger; 42 import javax.annotation.CheckForNull; 43 import org.checkerframework.checker.nullness.qual.Nullable; 44  45 /** 46  * Base class for services that can implement {@link #startUp} and {@link #shutDown} but while in 47  * the "running" state need to perform a periodic task. Subclasses can implement {@link #startUp}, 48  * {@link #shutDown} and also a {@link #runOneIteration} method that will be executed periodically. 49  * 50  * <p>This class uses the {@link ScheduledExecutorService} returned from {@link #executor} to run 51  * the {@link #startUp} and {@link #shutDown} methods and also uses that service to schedule the 52  * {@link #runOneIteration} that will be executed periodically as specified by its {@link 53  * Scheduler}. When this service is asked to stop via {@link #stopAsync} it will cancel the periodic 54  * task (but not interrupt it) and wait for it to stop before running the {@link #shutDown} method. 55  * 56  * <p>Subclasses are guaranteed that the life cycle methods ({@link #runOneIteration}, {@link 57  * #startUp} and {@link #shutDown}) will never run concurrently. Notably, if any execution of {@link 58  * #runOneIteration} takes longer than its schedule defines, then subsequent executions may start 59  * late. Also, all life cycle methods are executed with a lock held, so subclasses can safely modify 60  * shared state without additional synchronization necessary for visibility to later executions of 61  * the life cycle methods. 62  * 63  * <h3>Usage Example</h3> 64  * 65  * <p>Here is a sketch of a service which crawls a website and uses the scheduling capabilities to 66  * rate limit itself. 67  * 68  * <pre>{@code 69  * class CrawlingService extends AbstractScheduledService { 70  * private Set<Uri> visited; 71  * private Queue<Uri> toCrawl; 72  * protected void startUp() throws Exception { 73  * toCrawl = readStartingUris(); 74  * } 75  * 76  * protected void runOneIteration() throws Exception { 77  * Uri uri = toCrawl.remove(); 78  * Collection<Uri> newUris = crawl(uri); 79  * visited.add(uri); 80  * for (Uri newUri : newUris) { 81  * if (!visited.contains(newUri)) { toCrawl.add(newUri); } 82  * } 83  * } 84  * 85  * protected void shutDown() throws Exception { 86  * saveUris(toCrawl); 87  * } 88  * 89  * protected Scheduler scheduler() { 90  * return Scheduler.newFixedRateSchedule(0, 1, TimeUnit.SECONDS); 91  * } 92  * } 93  * }</pre> 94  * 95  * <p>This class uses the life cycle methods to read in a list of starting URIs and save the set of 96  * outstanding URIs when shutting down. Also, it takes advantage of the scheduling functionality to 97  * rate limit the number of queries we perform. 98  * 99  * @author Luke Sandberg 100  * @since 11.0 101  */ 102 @GwtIncompatible 103 @ElementTypesAreNonnullByDefault 104 public abstract class AbstractScheduledService implements Service { 105  private static final Logger logger = Logger.getLogger(AbstractScheduledService.class.getName()); 106  107  /** 108  * A scheduler defines the policy for how the {@link AbstractScheduledService} should run its 109  * task. 110  * 111  * <p>Consider using the {@link #newFixedDelaySchedule} and {@link #newFixedRateSchedule} factory 112  * methods, these provide {@link Scheduler} instances for the common use case of running the 113  * service with a fixed schedule. If more flexibility is needed then consider subclassing {@link 114  * CustomScheduler}. 115  * 116  * @author Luke Sandberg 117  * @since 11.0 118  */ 119  public abstract static class Scheduler { 120  /** 121  * Returns a {@link Scheduler} that schedules the task using the {@link 122  * ScheduledExecutorService#scheduleWithFixedDelay} method. 123  * 124  * @param initialDelay the time to delay first execution 125  * @param delay the delay between the termination of one execution and the commencement of the 126  * next 127  * @since 28.0 128  */ 129  public static Scheduler newFixedDelaySchedule(Duration initialDelay, Duration delay) { 130  return newFixedDelaySchedule( 131  toNanosSaturated(initialDelay), toNanosSaturated(delay), TimeUnit.NANOSECONDS); 132  } 133  134  /** 135  * Returns a {@link Scheduler} that schedules the task using the {@link 136  * ScheduledExecutorService#scheduleWithFixedDelay} method. 137  * 138  * @param initialDelay the time to delay first execution 139  * @param delay the delay between the termination of one execution and the commencement of the 140  * next 141  * @param unit the time unit of the initialDelay and delay parameters 142  */ 143  @SuppressWarnings("GoodTime") // should accept a java.time.Duration 144  public static Scheduler newFixedDelaySchedule( 145  final long initialDelay, final long delay, final TimeUnit unit) { 146  checkNotNull(unit); 147  checkArgument(delay > 0, "delay must be > 0, found %s", delay); 148  return new Scheduler() { 149  @Override 150  public Cancellable schedule( 151  AbstractService service, ScheduledExecutorService executor, Runnable task) { 152  return new FutureAsCancellable( 153  executor.scheduleWithFixedDelay(task, initialDelay, delay, unit)); 154  } 155  }; 156  } 157  158  /** 159  * Returns a {@link Scheduler} that schedules the task using the {@link 160  * ScheduledExecutorService#scheduleAtFixedRate} method. 161  * 162  * @param initialDelay the time to delay first execution 163  * @param period the period between successive executions of the task 164  * @since 28.0 165  */ 166  public static Scheduler newFixedRateSchedule(Duration initialDelay, Duration period) { 167  return newFixedRateSchedule( 168  toNanosSaturated(initialDelay), toNanosSaturated(period), TimeUnit.NANOSECONDS); 169  } 170  171  /** 172  * Returns a {@link Scheduler} that schedules the task using the {@link 173  * ScheduledExecutorService#scheduleAtFixedRate} method. 174  * 175  * @param initialDelay the time to delay first execution 176  * @param period the period between successive executions of the task 177  * @param unit the time unit of the initialDelay and period parameters 178  */ 179  @SuppressWarnings("GoodTime") // should accept a java.time.Duration 180  public static Scheduler newFixedRateSchedule( 181  final long initialDelay, final long period, final TimeUnit unit) { 182  checkNotNull(unit); 183  checkArgument(period > 0, "period must be > 0, found %s", period); 184  return new Scheduler() { 185  @Override 186  public Cancellable schedule( 187  AbstractService service, ScheduledExecutorService executor, Runnable task) { 188  return new FutureAsCancellable( 189  executor.scheduleAtFixedRate(task, initialDelay, period, unit)); 190  } 191  }; 192  } 193  194  /** Schedules the task to run on the provided executor on behalf of the service. */ 195  abstract Cancellable schedule( 196  AbstractService service, ScheduledExecutorService executor, Runnable runnable); 197  198  private Scheduler() {} 199  } 200  201  /* use AbstractService for state management */ 202  private final AbstractService delegate = new ServiceDelegate(); 203  204  @WeakOuter 205  private final class ServiceDelegate extends AbstractService { 206  207  // A handle to the running task so that we can stop it when a shutdown has been requested. 208  // These two fields are volatile because their values will be accessed from multiple threads. 209  @CheckForNull private volatile Cancellable runningTask; 210  @CheckForNull private volatile ScheduledExecutorService executorService; 211  212  // This lock protects the task so we can ensure that none of the template methods (startUp, 213  // shutDown or runOneIteration) run concurrently with one another. 214  // TODO(lukes): why don't we use ListenableFuture to sequence things? Then we could drop the 215  // lock. 216  private final ReentrantLock lock = new ReentrantLock(); 217  218  @WeakOuter 219  class Task implements Runnable { 220  @Override 221  public void run() { 222  lock.lock(); 223  try { 224  /* 225  * requireNonNull is safe because Task isn't run (or at least it doesn't succeed in taking 226  * the lock) until after it's scheduled and the runningTask field is set. 227  */ 228  if (requireNonNull(runningTask).isCancelled()) { 229  // task may have been cancelled while blocked on the lock. 230  return; 231  } 232  AbstractScheduledService.this.runOneIteration(); 233  } catch (Throwable t) { 234  try { 235  shutDown(); 236  } catch (Exception ignored) { 237  logger.log( 238  Level.WARNING, 239  "Error while attempting to shut down the service after failure.", 240  ignored); 241  } 242  notifyFailed(t); 243  // requireNonNull is safe now, just as it was above. 244  requireNonNull(runningTask).cancel(false); // prevent future invocations. 245  } finally { 246  lock.unlock(); 247  } 248  } 249  } 250  251  private final Runnable task = new Task(); 252  253  @Override 254  protected final void doStart() { 255  executorService = 256  MoreExecutors.renamingDecorator( 257  executor(), 258  new Supplier<String>() { 259  @Override 260  public String get() { 261  return serviceName() + " " + state(); 262  } 263  }); 264  executorService.execute( 265  new Runnable() { 266  @Override 267  public void run() { 268  lock.lock(); 269  try { 270  startUp(); 271  runningTask = scheduler().schedule(delegate, executorService, task); 272  notifyStarted(); 273  } catch (Throwable t) { 274  notifyFailed(t); 275  if (runningTask != null) { 276  // prevent the task from running if possible 277  runningTask.cancel(false); 278  } 279  } finally { 280  lock.unlock(); 281  } 282  } 283  }); 284  } 285  286  @Override 287  protected final void doStop() { 288  // Both requireNonNull calls are safe because doStop can run only after a successful doStart. 289  requireNonNull(runningTask); 290  requireNonNull(executorService); 291  runningTask.cancel(false); 292  executorService.execute( 293  new Runnable() { 294  @Override 295  public void run() { 296  try { 297  lock.lock(); 298  try { 299  if (state() != State.STOPPING) { 300  // This means that the state has changed since we were scheduled. This implies 301  // that an execution of runOneIteration has thrown an exception and we have 302  // transitioned to a failed state, also this means that shutDown has already 303  // been called, so we do not want to call it again. 304  return; 305  } 306  shutDown(); 307  } finally { 308  lock.unlock(); 309  } 310  notifyStopped(); 311  } catch (Throwable t) { 312  notifyFailed(t); 313  } 314  } 315  }); 316  } 317  318  @Override 319  public String toString() { 320  return AbstractScheduledService.this.toString(); 321  } 322  } 323  324  /** Constructor for use by subclasses. */ 325  protected AbstractScheduledService() {} 326  327  /** 328  * Run one iteration of the scheduled task. If any invocation of this method throws an exception, 329  * the service will transition to the {@link Service.State#FAILED} state and this method will no 330  * longer be called. 331  */ 332  protected abstract void runOneIteration() throws Exception; 333  334  /** 335  * Start the service. 336  * 337  * <p>By default this method does nothing. 338  */ 339  protected void startUp() throws Exception {} 340  341  /** 342  * Stop the service. This is guaranteed not to run concurrently with {@link #runOneIteration}. 343  * 344  * <p>By default this method does nothing. 345  */ 346  protected void shutDown() throws Exception {} 347  348  /** 349  * Returns the {@link Scheduler} object used to configure this service. This method will only be 350  * called once. 351  */ 352  // TODO(cpovirk): @ForOverride 353  protected abstract Scheduler scheduler(); 354  355  /** 356  * Returns the {@link ScheduledExecutorService} that will be used to execute the {@link #startUp}, 357  * {@link #runOneIteration} and {@link #shutDown} methods. If this method is overridden the 358  * executor will not be {@linkplain ScheduledExecutorService#shutdown shutdown} when this service 359  * {@linkplain Service.State#TERMINATED terminates} or {@linkplain Service.State#TERMINATED 360  * fails}. Subclasses may override this method to supply a custom {@link ScheduledExecutorService} 361  * instance. This method is guaranteed to only be called once. 362  * 363  * <p>By default this returns a new {@link ScheduledExecutorService} with a single thread pool 364  * that sets the name of the thread to the {@linkplain #serviceName() service name}. Also, the 365  * pool will be {@linkplain ScheduledExecutorService#shutdown() shut down} when the service 366  * {@linkplain Service.State#TERMINATED terminates} or {@linkplain Service.State#TERMINATED 367  * fails}. 368  */ 369  protected ScheduledExecutorService executor() { 370  @WeakOuter 371  class ThreadFactoryImpl implements ThreadFactory { 372  @Override 373  public Thread newThread(Runnable runnable) { 374  return MoreExecutors.newThread(serviceName(), runnable); 375  } 376  } 377  final ScheduledExecutorService executor = 378  Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl()); 379  // Add a listener to shutdown the executor after the service is stopped. This ensures that the 380  // JVM shutdown will not be prevented from exiting after this service has stopped or failed. 381  // Technically this listener is added after start() was called so it is a little gross, but it 382  // is called within doStart() so we know that the service cannot terminate or fail concurrently 383  // with adding this listener so it is impossible to miss an event that we are interested in. 384  addListener( 385  new Listener() { 386  @Override 387  public void terminated(State from) { 388  executor.shutdown(); 389  } 390  391  @Override 392  public void failed(State from, Throwable failure) { 393  executor.shutdown(); 394  } 395  }, 396  directExecutor()); 397  return executor; 398  } 399  400  /** 401  * Returns the name of this service. {@link AbstractScheduledService} may include the name in 402  * debugging output. 403  * 404  * @since 14.0 405  */ 406  protected String serviceName() { 407  return getClass().getSimpleName(); 408  } 409  410  @Override 411  public String toString() { 412  return serviceName() + " [" + state() + "]"; 413  } 414  415  @Override 416  public final boolean isRunning() { 417  return delegate.isRunning(); 418  } 419  420  @Override 421  public final State state() { 422  return delegate.state(); 423  } 424  425  /** @since 13.0 */ 426  @Override 427  public final void addListener(Listener listener, Executor executor) { 428  delegate.addListener(listener, executor); 429  } 430  431  /** @since 14.0 */ 432  @Override 433  public final Throwable failureCause() { 434  return delegate.failureCause(); 435  } 436  437  /** @since 15.0 */ 438  @CanIgnoreReturnValue 439  @Override 440  public final Service startAsync() { 441  delegate.startAsync(); 442  return this; 443  } 444  445  /** @since 15.0 */ 446  @CanIgnoreReturnValue 447  @Override 448  public final Service stopAsync() { 449  delegate.stopAsync(); 450  return this; 451  } 452  453  /** @since 15.0 */ 454  @Override 455  public final void awaitRunning() { 456  delegate.awaitRunning(); 457  } 458  459  /** @since 28.0 */ 460  @Override 461  public final void awaitRunning(Duration timeout) throws TimeoutException { 462  Service.super.awaitRunning(timeout); 463  } 464  465  /** @since 15.0 */ 466  @Override 467  public final void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException { 468  delegate.awaitRunning(timeout, unit); 469  } 470  471  /** @since 15.0 */ 472  @Override 473  public final void awaitTerminated() { 474  delegate.awaitTerminated(); 475  } 476  477  /** @since 28.0 */ 478  @Override 479  public final void awaitTerminated(Duration timeout) throws TimeoutException { 480  Service.super.awaitTerminated(timeout); 481  } 482  483  /** @since 15.0 */ 484  @Override 485  public final void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException { 486  delegate.awaitTerminated(timeout, unit); 487  } 488  489  interface Cancellable { 490  void cancel(boolean mayInterruptIfRunning); 491  492  boolean isCancelled(); 493  } 494  495  private static final class FutureAsCancellable implements Cancellable { 496  private final Future<?> delegate; 497  498  FutureAsCancellable(Future<?> delegate) { 499  this.delegate = delegate; 500  } 501  502  @Override 503  public void cancel(boolean mayInterruptIfRunning) { 504  delegate.cancel(mayInterruptIfRunning); 505  } 506  507  @Override 508  public boolean isCancelled() { 509  return delegate.isCancelled(); 510  } 511  } 512  513  /** 514  * A {@link Scheduler} that provides a convenient way for the {@link AbstractScheduledService} to 515  * use a dynamically changing schedule. After every execution of the task, assuming it hasn't been 516  * cancelled, the {@link #getNextSchedule} method will be called. 517  * 518  * @author Luke Sandberg 519  * @since 11.0 520  */ 521  public abstract static class CustomScheduler extends Scheduler { 522  523  /** A callable class that can reschedule itself using a {@link CustomScheduler}. */ 524  private final class ReschedulableCallable implements Callable<@Nullable Void> { 525  526  /** The underlying task. */ 527  private final Runnable wrappedRunnable; 528  529  /** The executor on which this Callable will be scheduled. */ 530  private final ScheduledExecutorService executor; 531  532  /** 533  * The service that is managing this callable. This is used so that failure can be reported 534  * properly. 535  */ 536  /* 537  * This reference is part of a reference cycle, which is typically something we want to avoid 538  * under j2objc -- but it is not detected by our j2objc cycle test. The cycle: 539  * 540  * - CustomScheduler.service contains an instance of ServiceDelegate. (It needs it so that it 541  * can call notifyFailed.) 542  * 543  * - ServiceDelegate.runningTask contains an instance of ReschedulableCallable (at least in 544  * the case that the service is using CustomScheduler). (It needs it so that it can cancel 545  * the task and detect whether it has been cancelled.) 546  * 547  * - ReschedulableCallable has a reference back to its enclosing CustomScheduler. (It needs it 548  * so that it can call getNextSchedule). 549  * 550  * Maybe there is a way to avoid this cycle. But we think the cycle is safe enough to ignore: 551  * Each task is retained for only as long as it is running -- so it's retained only as long as 552  * it would already be retained by the underlying executor. 553  * 554  * If the cycle test starts reporting this cycle in the future, we should add an entry to 555  * cycle_suppress_list.txt. 556  */ 557  private final AbstractService service; 558  559  /** 560  * This lock is used to ensure safe and correct cancellation, it ensures that a new task is 561  * not scheduled while a cancel is ongoing. Also it protects the currentFuture variable to 562  * ensure that it is assigned atomically with being scheduled. 563  */ 564  private final ReentrantLock lock = new ReentrantLock(); 565  566  /** The future that represents the next execution of this task. */ 567  @GuardedBy("lock") 568  @CheckForNull 569  private SupplantableFuture cancellationDelegate; 570  571  ReschedulableCallable( 572  AbstractService service, ScheduledExecutorService executor, Runnable runnable) { 573  this.wrappedRunnable = runnable; 574  this.executor = executor; 575  this.service = service; 576  } 577  578  @Override 579  @CheckForNull 580  public Void call() throws Exception { 581  wrappedRunnable.run(); 582  reschedule(); 583  return null; 584  } 585  586  /** 587  * Atomically reschedules this task and assigns the new future to {@link 588  * #cancellationDelegate}. 589  */ 590  @CanIgnoreReturnValue 591  public Cancellable reschedule() { 592  // invoke the callback outside the lock, prevents some shenanigans. 593  Schedule schedule; 594  try { 595  schedule = CustomScheduler.this.getNextSchedule(); 596  } catch (Throwable t) { 597  service.notifyFailed(t); 598  return new FutureAsCancellable(immediateCancelledFuture()); 599  } 600  // We reschedule ourselves with a lock held for two reasons. 1. we want to make sure that 601  // cancel calls cancel on the correct future. 2. we want to make sure that the assignment 602  // to currentFuture doesn't race with itself so that currentFuture is assigned in the 603  // correct order. 604  Throwable scheduleFailure = null; 605  Cancellable toReturn; 606  lock.lock(); 607  try { 608  toReturn = initializeOrUpdateCancellationDelegate(schedule); 609  } catch (Throwable e) { 610  // If an exception is thrown by the subclass then we need to make sure that the service 611  // notices and transitions to the FAILED state. We do it by calling notifyFailed directly 612  // because the service does not monitor the state of the future so if the exception is not 613  // caught and forwarded to the service the task would stop executing but the service would 614  // have no idea. 615  // TODO(lukes): consider building everything in terms of ListenableScheduledFuture then 616  // the AbstractService could monitor the future directly. Rescheduling is still hard... 617  // but it would help with some of these lock ordering issues. 618  scheduleFailure = e; 619  toReturn = new FutureAsCancellable(immediateCancelledFuture()); 620  } finally { 621  lock.unlock(); 622  } 623  // Call notifyFailed outside the lock to avoid lock ordering issues. 624  if (scheduleFailure != null) { 625  service.notifyFailed(scheduleFailure); 626  } 627  return toReturn; 628  } 629  630  @GuardedBy("lock") 631  /* 632  * The GuardedBy checker warns us that we're not holding cancellationDelegate.lock. But in 633  * fact we are holding it because it is the same as this.lock, which we know we are holding, 634  * thanks to @GuardedBy above. (cancellationDelegate.lock is initialized to this.lock in the 635  * call to `new SupplantableFuture` below.) 636  */ 637  @SuppressWarnings("GuardedBy") 638  private Cancellable initializeOrUpdateCancellationDelegate(Schedule schedule) { 639  if (cancellationDelegate == null) { 640  return cancellationDelegate = new SupplantableFuture(lock, submitToExecutor(schedule)); 641  } 642  if (!cancellationDelegate.currentFuture.isCancelled()) { 643  cancellationDelegate.currentFuture = submitToExecutor(schedule); 644  } 645  return cancellationDelegate; 646  } 647  648  private ScheduledFuture<@Nullable Void> submitToExecutor(Schedule schedule) { 649  return executor.schedule(this, schedule.delay, schedule.unit); 650  } 651  } 652  653  /** 654  * Contains the most recently submitted {@code Future}, which may be cancelled or updated, 655  * always under a lock. 656  */ 657  private static final class SupplantableFuture implements Cancellable { 658  private final ReentrantLock lock; 659  660  @GuardedBy("lock") 661  private Future<@Nullable Void> currentFuture; 662  663  SupplantableFuture(ReentrantLock lock, Future<@Nullable Void> currentFuture) { 664  this.lock = lock; 665  this.currentFuture = currentFuture; 666  } 667  668  @Override 669  public void cancel(boolean mayInterruptIfRunning) { 670  /* 671  * Lock to ensure that a task cannot be rescheduled while a cancel is ongoing. 672  * 673  * In theory, cancel() could execute arbitrary listeners -- bad to do while holding a lock. 674  * However, we don't expose currentFuture to users, so they can't attach listeners. And the 675  * Future might not even be a ListenableFuture, just a plain Future. That said, similar 676  * problems can exist with methods like FutureTask.done(), not to mention slow calls to 677  * Thread.interrupt() (as discussed in InterruptibleTask). At the end of the day, it's 678  * unlikely that cancel() will be slow, so we can probably get away with calling it while 679  * holding a lock. Still, it would be nice to avoid somehow. 680  */ 681  lock.lock(); 682  try { 683  currentFuture.cancel(mayInterruptIfRunning); 684  } finally { 685  lock.unlock(); 686  } 687  } 688  689  @Override 690  public boolean isCancelled() { 691  lock.lock(); 692  try { 693  return currentFuture.isCancelled(); 694  } finally { 695  lock.unlock(); 696  } 697  } 698  } 699  700  @Override 701  final Cancellable schedule( 702  AbstractService service, ScheduledExecutorService executor, Runnable runnable) { 703  return new ReschedulableCallable(service, executor, runnable).reschedule(); 704  } 705  706  /** 707  * A value object that represents an absolute delay until a task should be invoked. 708  * 709  * @author Luke Sandberg 710  * @since 11.0 711  */ 712  protected static final class Schedule { 713  714  private final long delay; 715  private final TimeUnit unit; 716  717  /** 718  * @param delay the time from now to delay execution 719  * @param unit the time unit of the delay parameter 720  */ 721  public Schedule(long delay, TimeUnit unit) { 722  this.delay = delay; 723  this.unit = checkNotNull(unit); 724  } 725  } 726  727  /** 728  * Calculates the time at which to next invoke the task. 729  * 730  * <p>This is guaranteed to be called immediately after the task has completed an iteration and 731  * on the same thread as the previous execution of {@link 732  * AbstractScheduledService#runOneIteration}. 733  * 734  * @return a schedule that defines the delay before the next execution. 735  */ 736  // TODO(cpovirk): @ForOverride 737  protected abstract Schedule getNextSchedule() throws Exception; 738  } 739 }