All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jtrim2.concurrent.WaitableSignal Maven / Gradle / Ivy

package org.jtrim2.concurrent;

import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.jtrim2.cancel.CancelableWaits;
import org.jtrim2.cancel.CancellationToken;
import org.jtrim2.utils.ExceptionHelper;

/**
 * Defines thread-safe signal for which threads can wait. That is, initially
 * all {@code WaitableSignal} is in the non-signaled state but after invoking
 * the {@link #signal()} method, it will permanently enter the signaled state.
 * Other threads can wait for this signal by calling the
 * {@link #waitSignal(CancellationToken) waitSignal} or the
 * {@link #tryWaitSignal(CancellationToken, long, TimeUnit) tryWaitSignal}
 * method.
 * 

* Note that this class is similar to a * {@code java.util.concurrent.CountDownLatch} with one as the initial "count" * but this implementation is simpler and relies on * {@link org.jtrim2.cancel.OperationCanceledException} rather than on thread * interrupts. * *

Thread safety

* The methods of this class are safe to use by multiple threads concurrently. * *

Synchronization transparency

* The methods of this interface are not synchronization transparent. */ public final class WaitableSignal { /** * A {@code WaitableSignal} which is already in the signaling state. That * is, calling any of its wait method is effectively a no-op. */ public static final WaitableSignal SIGNALING_SIGNAL = newSignalingSignal(); private final Lock lock; private final Condition waitSignal; private volatile boolean signaled; /** * Creates a new {@code WaitableSignal} in the non-signaled state. */ public WaitableSignal() { this.lock = new ReentrantLock(); this.waitSignal = lock.newCondition(); this.signaled = false; } private static WaitableSignal newSignalingSignal() { WaitableSignal result = new WaitableSignal(); result.signal(); return result; } /** * Sets the state of this {@code WaitableSignal} to the signaled state and * allows the {@code waitSignal} or the {@code tryWaitSignal} method to * return immediately. *

* This method is idempotent. That is, calling this method multiple times * has no further effect. */ public void signal() { if (!signaled) { signaled = true; lock.lock(); try { waitSignal.signalAll(); } finally { lock.unlock(); } } } /** * Returns {@code true} if {@link #signal()} has been already called on this * {@code WaitableSignal} object. *

* If this method returns {@code true}, subsequent {@code waitSignal} or * {@code tryWaitSignal} method calls will immediately return without * waiting (or throwing an exception). * * @return {@code true} if {@link #signal()} has been already called on this * {@code WaitableSignal} object, {@code false} otherwise */ public boolean isSignaled() { return signaled; } /** * Waits until another thread invokes the {@link #signal()} method or until * the specified {@code CancellationToken} signals a cancellation request. *

* Note that if the {@code signal()} method has been called prior to this * {@code waitSignal} method call, this method will always return * immediately without throwing an exception (even if the * {@code CancellationToken} signals a cancellation request). * * @param cancelToken the {@code CancellationToken} which is to be checked * for cancellation request. A cancellation request will cause this method * to throw an {@code OperationCanceledException} exception. This argument * cannot be {@code null}. * * @throws NullPointerException thrown if the specified * {@code CancellationToken} is {@code null} * @throws org.jtrim2.cancel.OperationCanceledException thrown if the * specified {@code CancellationToken} signals a cancellation request * before {@code signal()} has been called. */ public void waitSignal(CancellationToken cancelToken) { Objects.requireNonNull(cancelToken, "cancelToken"); if (signaled) { return; } lock.lock(); try { while (!signaled) { CancelableWaits.await(cancelToken, waitSignal); } } finally { lock.unlock(); } } /** * Waits until another thread invokes the {@link #signal()} method or until * the specified {@code CancellationToken} signals a cancellation request or * until the specified timeout elapses. *

* Note that if the {@code signal()} method has been called prior to this * {@code tryWaitSignal} method call, this method will always return with * {@code true} immediately without throwing an exception (even if the * {@code CancellationToken} signals a cancellation request). * * @param cancelToken the {@code CancellationToken} which is to be checked * for cancellation request. A cancellation request will cause this method * to throw an {@code OperationCanceledException} exception. This argument * cannot be {@code null}. * @param timeout the maximum time to wait for the signal in the given time * unit before returning. If the timeout elapses, this method will return * with {@code false}. This argument must be greater than or equal to * zero. * @param timeUnit the time unit of the {@code timeout} argument. This * argument cannot be {@code null} * @return {@code true} if this method has detected that the * {@code signal()} method has been called, {@code false} if the specified * timeout elapsed first. * * @throws NullPointerException thrown if the specified * {@code CancellationToken} or {@code TimeUnit} is {@code null} * @throws org.jtrim2.cancel.OperationCanceledException thrown if the * specified {@code CancellationToken} signals a cancellation request * before {@code signal()} has been called. */ public boolean tryWaitSignal(CancellationToken cancelToken, long timeout, TimeUnit timeUnit) { Objects.requireNonNull(cancelToken, "cancelToken"); ExceptionHelper.checkArgumentInRange(timeout, 0, Long.MAX_VALUE, "timeout"); Objects.requireNonNull(timeUnit, "timeUnit"); if (signaled) { return true; } long timeoutNanos = timeUnit.toNanos(timeout); long startTime = System.nanoTime(); lock.lock(); try { while (!signaled) { long elapsed = System.nanoTime() - startTime; long timeToWait = timeoutNanos - elapsed; if (timeToWait <= 0) { return false; } CancelableWaits.await(cancelToken, timeToWait, TimeUnit.NANOSECONDS, waitSignal); } return true; } finally { lock.unlock(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy