• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ben-manes / caffeine / #5573

03 Jul 2026 08:24PM UTC coverage: 98.975% (-1.0%) from 99.988%
#5573

push

github

ben-manes
Bound per-cycle expiration to amortize idle-recovery spikes

Cap expireAfterAccessEntries/expireAfterWriteEntries at
EXPIRATION_THRESHOLD evictions per maintenance cycle, then re-arm
via PROCESSING_TO_REQUIRED so the backlog drains across cycles.
Mirrors drainWriteBuffer's cap; keeps a large expired population
from stalling a writer-assist under evictionLock. The validation
subject pumps cleanUp until IDLE to drain the re-armed backlog.

3996 of 4072 branches covered (98.13%)

16 of 16 new or added lines in 1 file covered. (100.0%)

84 existing lines in 13 files now uncovered.

8205 of 8290 relevant lines covered (98.97%)

0.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.75
/caffeine/src/main/java/com/github/benmanes/caffeine/cache/Scheduler.java
1
/*
2
 * Copyright 2019 Ben Manes. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package com.github.benmanes.caffeine.cache;
17

18
import static java.util.Objects.requireNonNull;
19

20
import java.io.Serializable;
21
import java.lang.System.Logger;
22
import java.lang.System.Logger.Level;
23
import java.util.concurrent.CompletableFuture;
24
import java.util.concurrent.Executor;
25
import java.util.concurrent.Future;
26
import java.util.concurrent.ScheduledExecutorService;
27
import java.util.concurrent.TimeUnit;
28

29
import org.jspecify.annotations.NullMarked;
30
import org.jspecify.annotations.Nullable;
31

32
/**
33
 * A scheduler that submits a task to an executor after a given delay.
34
 *
35
 * @author ben.manes@gmail.com (Ben Manes)
36
 */
37
@NullMarked
38
@FunctionalInterface
39
public interface Scheduler {
40

41
  /**
42
   * Returns a future that will submit the task to the executor after the given delay.
43
   *
44
   * @param executor the executor to run the task
45
   * @param command the runnable task to schedule
46
   * @param delay how long to delay, in units of {@code unit}
47
   * @param unit a {@code TimeUnit} determining how to interpret the {@code delay} parameter
48
   * @return a scheduled future representing the pending submission of the task
49
   */
50
  Future<? extends @Nullable Object> schedule(
51
      Executor executor, Runnable command, long delay, TimeUnit unit);
52

53
  /**
54
   * Returns a scheduler that always returns a successfully completed future.
55
   *
56
   * @return a scheduler that always returns a successfully completed future
57
   */
58
  static Scheduler disabledScheduler() {
59
    return DisabledScheduler.INSTANCE;
1✔
60
  }
61

62
  /**
63
   * Returns a scheduler that uses the system-wide scheduling thread by using
64
   * {@link CompletableFuture#delayedExecutor}.
65
   *
66
   * @return a scheduler that uses the system-wide scheduling thread
67
   */
68
  static Scheduler systemScheduler() {
69
    return SystemScheduler.INSTANCE;
1✔
70
  }
71

72
  /**
73
   * Returns a scheduler that delegates to the a {@link ScheduledExecutorService}.
74
   * <p>
75
   * Note that this implementation will ignore scheduling the task if the executor was shutdown or
76
   * the submission was rejected. Consider implementing your own adapter if different behavior is
77
   * required.
78
   *
79
   * @param scheduledExecutorService the executor to schedule on
80
   * @return a scheduler that delegates to the a {@link ScheduledExecutorService}
81
   */
82
  static Scheduler forScheduledExecutorService(ScheduledExecutorService scheduledExecutorService) {
83
    return new ExecutorServiceScheduler(scheduledExecutorService);
1✔
84
  }
85

86
  /**
87
   * Returns a scheduler that suppresses and logs any exception thrown by the delegate
88
   * {@code scheduler}.
89
   *
90
   * @param scheduler the scheduler to delegate to
91
   * @return a scheduler that suppresses and logs any exception thrown by the delegate
92
   */
93
  static Scheduler guardedScheduler(Scheduler scheduler) {
94
    return (scheduler instanceof GuardedScheduler) ? scheduler : new GuardedScheduler(scheduler);
1✔
95
  }
96
}
97

98
enum SystemScheduler implements Scheduler {
1✔
99
  INSTANCE;
1✔
100

101
  @Override
102
  public Future<?> schedule(Executor executor, Runnable command, long delay, TimeUnit unit) {
103
    Executor delayedExecutor = CompletableFuture.delayedExecutor(delay, unit, executor);
1✔
104
    return CompletableFuture.runAsync(command, delayedExecutor);
1✔
105
  }
106
}
107

108
final class ExecutorServiceScheduler implements Scheduler, Serializable {
109
  private static final Logger logger = System.getLogger(ExecutorServiceScheduler.class.getName());
1✔
110
  private static final long serialVersionUID = 1;
111

112
  @SuppressWarnings("serial")
113
  final ScheduledExecutorService scheduledExecutorService;
114

115
  ExecutorServiceScheduler(ScheduledExecutorService scheduledExecutorService) {
1✔
116
    this.scheduledExecutorService = requireNonNull(scheduledExecutorService);
1✔
117
  }
1✔
118

119
  @Override
120
  public Future<? extends @Nullable Object> schedule(
121
      Executor executor, Runnable command, long delay, TimeUnit unit) {
122
    requireNonNull(executor);
1✔
123
    requireNonNull(command);
1✔
124
    requireNonNull(unit);
1✔
125

126
    if (scheduledExecutorService.isShutdown()) {
1✔
127
      return DisabledFuture.instance();
1✔
128
    }
129
    return scheduledExecutorService.schedule(() -> {
1✔
130
      try {
UNCOV
131
        executor.execute(command);
×
132
      } catch (Throwable t) {
1✔
133
        logger.log(Level.WARNING, "Exception thrown when submitting scheduled task", t);
1✔
134
        throw t;
1✔
UNCOV
135
      }
×
UNCOV
136
    }, delay, unit);
×
137
  }
138
}
139

140
final class GuardedScheduler implements Scheduler, Serializable {
141
  private static final Logger logger = System.getLogger(GuardedScheduler.class.getName());
1✔
142
  private static final long serialVersionUID = 1;
143

144
  @SuppressWarnings("serial")
145
  final Scheduler delegate;
146

147
  GuardedScheduler(Scheduler delegate) {
1✔
148
    this.delegate = requireNonNull(delegate);
1✔
149
  }
1✔
150

151
  @Override
152
  @SuppressWarnings("ConstantValue")
153
  public Future<? extends @Nullable Object> schedule(
154
      Executor executor, Runnable command, long delay, TimeUnit unit) {
155
    try {
156
      var future = delegate.schedule(executor, command, delay, unit);
1✔
157
      return (future == null) ? DisabledFuture.instance() : future;
1✔
158
    } catch (Throwable t) {
1✔
159
      logger.log(Level.WARNING, "Exception thrown by scheduler; discarded task", t);
1✔
160
      return DisabledFuture.instance();
1✔
161
    }
162
  }
163
}
164

165
enum DisabledScheduler implements Scheduler {
1✔
166
  INSTANCE;
1✔
167

168
  @Override
169
  public Future<? extends @Nullable Object> schedule(
170
      Executor executor, Runnable command, long delay, TimeUnit unit) {
171
    requireNonNull(executor);
1✔
172
    requireNonNull(command);
1✔
173
    requireNonNull(unit);
1✔
174
    return DisabledFuture.instance();
1✔
175
  }
176
}
177

178
enum DisabledFuture implements Future<@Nullable Void> {
1✔
179
  INSTANCE;
1✔
180

181
  static Future<? extends @Nullable Object> instance() {
182
    return INSTANCE;
1✔
183
  }
184

185
  @Override public boolean isDone() {
186
    return true;
1✔
187
  }
188
  @Override public boolean isCancelled() {
189
    return false;
1✔
190
  }
191
  @Override public boolean cancel(boolean mayInterruptIfRunning) {
192
    return false;
1✔
193
  }
194
  @Override public @Nullable Void get(long timeout, TimeUnit unit) {
195
    requireNonNull(unit);
1✔
196
    return null;
1✔
197
  }
198
  @Override public @Nullable Void get() {
199
    return null;
1✔
200
  }
201
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc