io.trino.operator.DriverYieldSignal Maven / Gradle / Ivy
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.trino.operator;
import com.google.common.annotations.VisibleForTesting;
import com.google.errorprone.annotations.ThreadSafe;
import com.google.errorprone.annotations.concurrent.GuardedBy;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Preconditions.checkState;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
/**
* Methods setWithDelay and reset should be used in pairs;
* usually follow the following idiom:
* {@code
* DriverYieldSignal signal = ...;
* signal.setWithDelay(duration, executor);
* try {
* // block
* } finally {
* signal.reset();
* }
* }
*/
@ThreadSafe
public class DriverYieldSignal
{
@GuardedBy("this")
private long runningSequence;
@GuardedBy("this")
private boolean terminationStarted;
@GuardedBy("this")
private ScheduledFuture> yieldFuture;
private final AtomicBoolean yield = new AtomicBoolean();
public synchronized void setWithDelay(long maxRunNanos, ScheduledExecutorService executor)
{
checkState(yieldFuture == null, "there is an ongoing yield");
checkState(!isSet(), "yield while driver was not running");
if (terminationStarted) {
return;
}
this.runningSequence++;
long expectedRunningSequence = this.runningSequence;
yieldFuture = executor.schedule(() -> {
synchronized (this) {
if (expectedRunningSequence == runningSequence && yieldFuture != null) {
yield.set(true);
}
}
}, maxRunNanos, NANOSECONDS);
}
public synchronized void reset()
{
if (terminationStarted) {
return;
}
checkState(yieldFuture != null, "there is no ongoing yield");
yield.set(false);
yieldFuture.cancel(true);
yieldFuture = null;
}
public boolean isSet()
{
return yield.get();
}
/**
* Signals an immediate yield to the driver to improve responsiveness to termination commands that may arrive while drivers are
* still running. After calling this method, the driver should not attempt to start another interval of running and attempting
* to call {@link DriverYieldSignal#setWithDelay(long, ScheduledExecutorService)} will fail.
*/
public synchronized void yieldImmediatelyForTermination()
{
terminationStarted = true;
yield.set(true);
if (yieldFuture != null) {
yieldFuture.cancel(true);
yieldFuture = null;
}
}
@Override
public synchronized String toString()
{
return toStringHelper(this)
.add("yieldScheduled", yieldFuture != null)
.add("yield", yield.get())
.toString();
}
@VisibleForTesting
public synchronized void forceYieldForTesting()
{
yield.set(true);
}
@VisibleForTesting
public synchronized void resetYieldForTesting()
{
yield.set(false);
}
}