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