Assign unused Future return value to a variable

Error Prone plans to make this pattern into a compile error; this avoids
future breakage.

In some of these cases, it's not obvious that ignoring the return value
is the correct thing to do; the hope is that the construct is ugly
enough to make the author think twice. In many cases though, it's clear
that a locally-created Runnable or Callable already does its own error
handling, so we can leave a comment alongside the ignored return value.

Eventually many of these should be audited again, and we can replace
some of the @SuppressWarnings with Error Prone's @CanIgnoreReturnValue.
That much is left for future cleanup.

Change-Id: Ia989214d85e0d6c387e388a77178e0b5c4bf2498
This commit is contained in:
Dave Borowitz 2017-02-02 15:49:50 -05:00
parent a00f42a52f
commit 9189e67c3d
19 changed files with 160 additions and 103 deletions

View File

@ -49,6 +49,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.CyclicBarrier; import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.apache.log4j.Level; import org.apache.log4j.Level;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
@ -169,22 +170,28 @@ public class GerritServer {
} else { } else {
site = initSite(cfg); site = initSite(cfg);
daemonService = Executors.newSingleThreadExecutor(); daemonService = Executors.newSingleThreadExecutor();
daemonService.submit( @SuppressWarnings("unused")
new Callable<Void>() { Future<?> possiblyIgnoredError =
@Override daemonService.submit(
public Void call() throws Exception { new Callable<Void>() {
int rc = @Override
daemon.main( public Void call() throws Exception {
new String[] { int rc =
"-d", site.getPath(), "--headless", "--console-log", "--show-stack-trace", daemon.main(
}); new String[] {
if (rc != 0) { "-d",
System.err.println("Failed to start Gerrit daemon"); site.getPath(),
serverStarted.reset(); "--headless",
} "--console-log",
return null; "--show-stack-trace",
} });
}); if (rc != 0) {
System.err.println("Failed to start Gerrit daemon");
serverStarted.reset();
}
return null;
}
});
serverStarted.await(); serverStarted.await();
System.out.println("Gerrit Server Started"); System.out.println("Gerrit Server Started");
} }

View File

@ -39,6 +39,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
@ -116,7 +117,9 @@ class H2CacheFactory implements PersistentCacheFactory, LifecycleListener {
if (executor != null) { if (executor != null) {
for (final H2CacheImpl<?, ?> cache : caches) { for (final H2CacheImpl<?, ?> cache : caches) {
executor.execute(cache::start); executor.execute(cache::start);
cleanup.schedule(() -> cache.prune(cleanup), 30, TimeUnit.SECONDS); @SuppressWarnings("unused")
Future<?> possiblyIgnoredError =
cleanup.schedule(() -> cache.prune(cleanup), 30, TimeUnit.SECONDS);
} }
} }
} }

View File

@ -46,6 +46,7 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
@ -196,7 +197,9 @@ public class H2CacheImpl<K, V> extends AbstractLoadingCache<K, V> implements Per
cal.add(Calendar.DAY_OF_MONTH, 1); cal.add(Calendar.DAY_OF_MONTH, 1);
long delay = cal.getTimeInMillis() - TimeUtil.nowMs(); long delay = cal.getTimeInMillis() - TimeUtil.nowMs();
service.schedule(() -> prune(service), delay, TimeUnit.MILLISECONDS); @SuppressWarnings("unused")
Future<?> possiblyIgnoredError =
service.schedule(() -> prune(service), delay, TimeUnit.MILLISECONDS);
} }
static class ValueHolder<V> { static class ValueHolder<V> {

View File

@ -40,6 +40,7 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@ -116,34 +117,37 @@ public abstract class AbstractLuceneIndex<K, V> implements Index<K, V> {
.setNameFormat(index + " Commit-%d") .setNameFormat(index + " Commit-%d")
.setDaemon(true) .setDaemon(true)
.build()); .build());
autoCommitExecutor.scheduleAtFixedRate( @SuppressWarnings("unused") // Error handling within Runnable.
new Runnable() { Future<?> possiblyIgnoredError =
@Override autoCommitExecutor.scheduleAtFixedRate(
public void run() { new Runnable() {
try { @Override
if (autoCommitWriter.hasUncommittedChanges()) { public void run() {
autoCommitWriter.manualFlush(); try {
autoCommitWriter.commit(); if (autoCommitWriter.hasUncommittedChanges()) {
autoCommitWriter.manualFlush();
autoCommitWriter.commit();
}
} catch (IOException e) {
log.error("Error committing " + index + " Lucene index", e);
} catch (OutOfMemoryError e) {
log.error("Error committing " + index + " Lucene index", e);
try {
autoCommitWriter.close();
} catch (IOException e2) {
log.error(
"SEVERE: Error closing "
+ index
+ " Lucene index after OOM;"
+ " index may be corrupted.",
e);
}
}
} }
} catch (IOException e) { },
log.error("Error committing " + index + " Lucene index", e); commitPeriod,
} catch (OutOfMemoryError e) { commitPeriod,
log.error("Error committing " + index + " Lucene index", e); MILLISECONDS);
try {
autoCommitWriter.close();
} catch (IOException e2) {
log.error(
"SEVERE: Error closing "
+ index
+ " Lucene index after OOM; index may be corrupted.",
e);
}
}
}
},
commitPeriod,
commitPeriod,
MILLISECONDS);
} }
writer = new TrackingIndexWriter(delegateWriter); writer = new TrackingIndexWriter(delegateWriter);
searcherManager = new WrappableSearcherManager(writer.getIndexWriter(), true, searcherFactory); searcherManager = new WrappableSearcherManager(writer.getIndexWriter(), true, searcherFactory);

View File

@ -31,6 +31,7 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.concurrent.Future;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -63,9 +64,12 @@ public class LogFileCompressor implements Runnable {
ZoneId zone = ZoneId.systemDefault(); ZoneId zone = ZoneId.systemDefault();
LocalDate now = LocalDate.now(zone); LocalDate now = LocalDate.now(zone);
long milliSecondsUntil11pm = now.atStartOfDay(zone).plusHours(23).toInstant().toEpochMilli(); long milliSecondsUntil11pm = now.atStartOfDay(zone).plusHours(23).toInstant().toEpochMilli();
queue @SuppressWarnings("unused")
.getDefaultQueue() Future<?> possiblyIgnoredError =
.scheduleAtFixedRate(compressor, milliSecondsUntil11pm, HOURS.toMillis(24), MILLISECONDS); queue
.getDefaultQueue()
.scheduleAtFixedRate(
compressor, milliSecondsUntil11pm, HOURS.toMillis(24), MILLISECONDS);
} }
@Override @Override

View File

@ -25,6 +25,7 @@ import com.google.gerrit.server.util.ManualRequestContext;
import com.google.gerrit.server.util.OneOffRequestContext; import com.google.gerrit.server.util.OneOffRequestContext;
import com.google.gwtorm.server.OrmException; import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -64,7 +65,11 @@ public class ChangeCleanupRunner implements Runnable {
String.format( String.format(
"Ignoring invalid changeCleanup schedule configuration: %s", scheduleConfig)); "Ignoring invalid changeCleanup schedule configuration: %s", scheduleConfig));
} else { } else {
queue.getDefaultQueue().scheduleAtFixedRate(runner, delay, interval, TimeUnit.MILLISECONDS); @SuppressWarnings("unused")
Future<?> possiblyIgnoredError =
queue
.getDefaultQueue()
.scheduleAtFixedRate(runner, delay, interval, TimeUnit.MILLISECONDS);
} }
} }

View File

@ -70,6 +70,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.transport.ReceiveCommand; import org.eclipse.jgit.transport.ReceiveCommand;
@ -449,7 +450,9 @@ public class ChangeInserter extends BatchUpdate.InsertChangeOp {
} }
}; };
if (requestScopePropagator != null) { if (requestScopePropagator != null) {
sendEmailExecutor.submit(requestScopePropagator.wrap(sender)); @SuppressWarnings("unused")
Future<?> possiblyIgnoredError =
sendEmailExecutor.submit(requestScopePropagator.wrap(sender));
} else { } else {
sender.run(); sender.run();
} }

View File

@ -42,6 +42,7 @@ import com.google.inject.ProvisionException;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
import java.util.List; import java.util.List;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -111,7 +112,8 @@ public class EmailReviewComments implements Runnable, RequestContext {
} }
void sendAsync() { void sendAsync() {
sendEmailsExecutor.submit(this); @SuppressWarnings("unused")
Future<?> possiblyIgnoredError = sendEmailsExecutor.submit(this);
} }
@Override @Override

View File

@ -35,6 +35,7 @@ import com.google.inject.Provider;
import com.google.inject.ProvisionException; import com.google.inject.ProvisionException;
import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.Assisted;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -89,7 +90,8 @@ public class EmailMerge implements Runnable, RequestContext {
} }
public void sendAsync() { public void sendAsync() {
sendEmailsExecutor.submit(this); @SuppressWarnings("unused")
Future<?> possiblyIgnoredError = sendEmailsExecutor.submit(this);
} }
@Override @Override

View File

@ -22,6 +22,7 @@ import com.google.gerrit.server.config.GcConfig;
import com.google.gerrit.server.config.ScheduleConfig; import com.google.gerrit.server.config.ScheduleConfig;
import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectCache;
import com.google.inject.Inject; import com.google.inject.Inject;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -52,9 +53,11 @@ public class GarbageCollectionRunner implements Runnable {
} else if (delay < 0 || interval <= 0) { } else if (delay < 0 || interval <= 0) {
gcLog.warn(String.format("Ignoring invalid gc schedule configuration: %s", scheduleConfig)); gcLog.warn(String.format("Ignoring invalid gc schedule configuration: %s", scheduleConfig));
} else { } else {
queue @SuppressWarnings("unused")
.getDefaultQueue() Future<?> possiblyIgnoredError =
.scheduleAtFixedRate(gcRunner, delay, interval, TimeUnit.MILLISECONDS); queue
.getDefaultQueue()
.scheduleAtFixedRate(gcRunner, delay, interval, TimeUnit.MILLISECONDS);
} }
} }

View File

@ -39,6 +39,7 @@ import com.google.inject.assistedinject.AssistedInject;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
@ -164,27 +165,29 @@ public class MergedByPushOp extends BatchUpdate.Op {
if (!correctBranch) { if (!correctBranch) {
return; return;
} }
sendEmailExecutor.submit( @SuppressWarnings("unused") // Runnable already handles errors
requestScopePropagator.wrap( Future<?> possiblyIgnoredError =
new Runnable() { sendEmailExecutor.submit(
@Override requestScopePropagator.wrap(
public void run() { new Runnable() {
try { @Override
MergedSender cm = public void run() {
mergedSenderFactory.create(ctx.getProject(), psId.getParentKey()); try {
cm.setFrom(ctx.getAccountId()); MergedSender cm =
cm.setPatchSet(patchSet, info); mergedSenderFactory.create(ctx.getProject(), psId.getParentKey());
cm.send(); cm.setFrom(ctx.getAccountId());
} catch (Exception e) { cm.setPatchSet(patchSet, info);
log.error("Cannot send email for submitted patch set " + psId, e); cm.send();
} } catch (Exception e) {
} log.error("Cannot send email for submitted patch set " + psId, e);
}
}
@Override @Override
public String toString() { public String toString() {
return "send-email merged"; return "send-email merged";
} }
})); }));
changeMerged.fire( changeMerged.fire(
change, patchSet, ctx.getAccount(), patchSet.getRevision().get(), ctx.getWhen()); change, patchSet, ctx.getAccount(), patchSet.getRevision().get(), ctx.getWhen());

View File

@ -138,6 +138,7 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.IncorrectObjectTypeException;
@ -1959,8 +1960,9 @@ public class ReceiveCommits {
Change change = notes.getChange(); Change change = notes.getChange();
if (change.getDest().equals(magicBranch.dest)) { if (change.getDest().equals(magicBranch.dest)) {
logDebug("Found change {} from existing refs.", change.getKey()); logDebug("Found change {} from existing refs.", change.getKey());
// reindex the change asynchronously // Reindex the change asynchronously, ignoring errors.
indexer.indexAsync(project.getNameKey(), change.getId()); @SuppressWarnings("unused")
Future<?> possiblyIgnoredError = indexer.indexAsync(project.getNameKey(), change.getId());
return true; return true;
} }
} }

View File

@ -23,6 +23,7 @@ import com.google.inject.assistedinject.Assisted;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.errors.RepositoryNotFoundException;
@ -92,11 +93,12 @@ public class RenameGroupOp extends DefaultQueueOp {
} }
} }
// If one or more projects did not update, wait 5 minutes // If one or more projects did not update, wait 5 minutes and give it
// and give it another attempt. // another attempt. If it doesn't update after that, give up.
if (!retryOn.isEmpty() && !tryingAgain) { if (!retryOn.isEmpty() && !tryingAgain) {
tryingAgain = true; tryingAgain = true;
start(5, TimeUnit.MINUTES); @SuppressWarnings("unused")
Future<?> possiblyIgnoredError = start(5, TimeUnit.MINUTES);
} }
} }

View File

@ -61,6 +61,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
@ -412,7 +413,9 @@ public class ReplaceOp extends BatchUpdate.Op {
}; };
if (requestScopePropagator != null) { if (requestScopePropagator != null) {
sendEmailExecutor.submit(requestScopePropagator.wrap(sender)); @SuppressWarnings("unused")
Future<?> possiblyIgnoredError =
sendEmailExecutor.submit(requestScopePropagator.wrap(sender));
} else { } else {
sender.run(); sender.run();
} }

View File

@ -39,6 +39,7 @@ import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Singleton @Singleton
@ -125,13 +126,15 @@ public class PutName implements RestModifyView<GroupResource, Input> {
groupCache.evict(group); groupCache.evict(group);
groupCache.evictAfterRename(old, key); groupCache.evictAfterRename(old, key);
renameGroupOpFactory @SuppressWarnings("unused")
.create( Future<?> possiblyIgnoredError =
currentUser.get().newCommitterIdent(new Date(), TimeZone.getDefault()), renameGroupOpFactory
group.getGroupUUID(), .create(
old.get(), currentUser.get().newCommitterIdent(new Date(), TimeZone.getDefault()),
newName) group.getGroupUUID(),
.start(0, TimeUnit.MILLISECONDS); old.get(),
newName)
.start(0, TimeUnit.MILLISECONDS);
return groupDetailFactory.create(groupId).call(); return groupDetailFactory.create(groupId).call();
} }

View File

@ -52,6 +52,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -313,7 +314,9 @@ public class ChangeIndexer {
private void reindexAfterIndexUpdate(Project.NameKey project, Change.Id id) { private void reindexAfterIndexUpdate(Project.NameKey project, Change.Id id) {
if (reindexAfterIndexUpdate) { if (reindexAfterIndexUpdate) {
reindexIfStale(project, id); // Don't retry indefinitely; if this fails the change will be stale.
@SuppressWarnings("unused")
Future<?> possiblyIgnoredError = reindexIfStale(project, id);
} }
} }

View File

@ -39,6 +39,7 @@ import com.google.inject.Provider;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -81,7 +82,9 @@ public class ReindexAfterUpdate implements GitReferenceUpdatedListener {
@Override @Override
public void onSuccess(List<Change> changes) { public void onSuccess(List<Change> changes) {
for (Change c : changes) { for (Change c : changes) {
executor.submit(new Index(event, c.getId())); // Don't retry indefinitely; if this fails changes may be stale.
@SuppressWarnings("unused")
Future<?> possiblyIgnoredError = executor.submit(new Index(event, c.getId()));
} }
} }

View File

@ -28,6 +28,7 @@ import java.util.Set;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -133,7 +134,8 @@ public abstract class MailReceiver implements LifecycleListener {
} }
return null; return null;
}; };
workQueue.getDefaultQueue().submit(task); @SuppressWarnings("unused")
Future<?> possiblyIgnoredError = workQueue.getDefaultQueue().submit(task);
} else { } else {
// Synchronous processing is used only in tests. // Synchronous processing is used only in tests.
try { try {

View File

@ -20,6 +20,7 @@ import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
@ -50,18 +51,17 @@ public class ProjectCacheClock {
.setDaemon(true) .setDaemon(true)
.setPriority(Thread.MIN_PRIORITY) .setPriority(Thread.MIN_PRIORITY)
.build()); .build());
executor.scheduleAtFixedRate( @SuppressWarnings("unused") // Runnable already handles errors
new Runnable() { Future<?> possiblyIgnoredError =
@Override executor.scheduleAtFixedRate(
public void run() { () -> {
// This is not exactly thread-safe, but is OK for our use. // This is not exactly thread-safe, but is OK for our use. The only
// The only thread that writes the volatile is this task. // thread that writes the volatile is this task.
generation = generation + 1; generation = generation + 1;
} },
}, checkFrequencyMillis,
checkFrequencyMillis, checkFrequencyMillis,
checkFrequencyMillis, TimeUnit.MILLISECONDS);
TimeUnit.MILLISECONDS);
} else { } else {
// Magic generation 0 triggers ProjectState to always // Magic generation 0 triggers ProjectState to always
// check on each needsRefresh() request we make to it. // check on each needsRefresh() request we make to it.