com.rabbitmq.jms.util.EntryExitManager Maven / Gradle / Ivy
/* Copyright (c) 2013-2023 Broadcom. All Rights Reserved. The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. */
package com.rabbitmq.jms.util;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.jms.client.Completion;
/**
* Manages threads entering and exiting a notional region. Entry is controlled by a gate, and exit is signalled by
* {@link Completion}. Can block threads entering, abort waiting threads and wait for threads which have entered to exit.
*
* When the gate is open, threads are not prevented from entering. When the gate is closed, threads will block on
* {@link #enter enter(...)}, until the gate is opened (by some other thread).
*
*
* Threads which enter the region must leave it by calling {@link #exit()}. This will signal the exit of the thread. The
* manager offers a method {@link #waitToClear waitToClear(...)} which will block
* until all the currently entered threads have exited the region. Threads which enter during {@link #waitToClear} are not
* detected.
*
*
* - {@link #closeGate()}
* - will close the gate to all threads,
* - {@link #openGate()}
* - will open the gate, unblocking all waiting threads,
* - {@link #enter enter(...)}
* - will allow the calling thread to enter the region, or block if the gate is closed,
* - {@link #exit()}
* - will signal the calling thread to exit the region,
* - {@link #waitToClear waitToClear(...)}
* - will block until all the threads currently in the region have exited,
* - {@link #abortWaiters()}
* - will reject all waiting threads with an
AbortedException
.
*
*/
public class EntryExitManager {
private final WaiterGate gate;
private final Queue entered = new ConcurrentLinkedQueue();
private ThreadLocal threadCompletion = new ThreadLocal();
private void registerEntry() {
Completion comp = new Completion();
this.threadCompletion.set(comp); // thread-local
this.entered.add(comp);
}
/**
* Create an EntryExitManager
, initially closed.
*/
public EntryExitManager() {
this.gate = new WaiterGate(false) {
@Override public void onEntry() { /*register on entry*/ EntryExitManager.this.registerEntry();}
@Override public void onAbort() { /*noop*/ }
};
}
/**
* Is the gate closed?
* @return true
if the gate is closed, false
otherwise.
*/
public boolean isClosed() {
return !gate.isOpen();
}
/**
* Close the gate, if allowed, so subsequent enter()
ing threads will block.
* @return true
in all cases.
*/
public boolean closeGate() {
gate.close();
return true;
}
/**
* Opens the gate and wakes up all waiting threads. Does not block.
* @return true
in all cases.
*/
public boolean openGate() {
gate.open();
return true;
}
/**
* Returns true
immediately if the gate is open.
* Otherwise if the gate is closed the thread blocks until one of the following occurs:
*
* - gate is opened (by another thread);
* - timeout expires before gate is opened.
*
* @param timeout the time to wait for the gate to open.
* @param unit the time unit of the timeout
argument.
* @return false
if timeout was reached before gate opens; true
if gate is open or opens while we are waiting.
* @throws InterruptedException if the callers thread is interrupted while waiting.
* @throws AbortedException if this thread is aborted by a stop()
or close()
while waiting.
*/
public boolean enter(long timeout, TimeUnit unit) throws InterruptedException, AbortedException {
return enter(new TimeTracker(timeout, unit));
}
/**
* Returns true
immediately if the gate is open.
* Otherwise if the gate is closed the thread blocks until one of the following occurs:
*
* - gate is opened (by another thread);
* - timeout expires before gate is opened.
*
* @param tt the time tracker used to wait for the gate to open.
* @return false
if timeout was reached before gate opens; true
if gate is open or opens while we are waiting.
* @throws InterruptedException if the callers thread is interrupted while waiting.
* @throws AbortedException if this thread is aborted by a stop()
or close()
while waiting.
*/
public boolean enter(TimeTracker tt) throws InterruptedException, AbortedException {
return gate.waitForOpen(tt);
}
/**
* This thread is exiting the region. Must be called eventually by the thread that entered the region.
*/
public void exit() {
Completion comp = this.threadCompletion.get(); // this thread's completion object
if (comp != null) {
comp.setComplete();
this.entered.remove(comp);
}
}
/**
* Wait for current threads to exit region.
* @param timeout max time to wait in unit
s.
* @param unit of time measurement for timeout
.
* @return true
if they all exited in time, false
otherwise.
* @throws InterruptedException if thread is interrupted and is waiting.
*/
public boolean waitToClear(long timeout, TimeUnit unit) throws InterruptedException {
return waitToClear(new TimeTracker(timeout, unit));
}
/**
* Wait for current threads to exit region.
* @param tt timeout tracker.
* @return true
if they all exited in time, false
otherwise.
* @throws InterruptedException if thread is interrupted and is waiting.
*/
public boolean waitToClear(TimeTracker tt) throws InterruptedException {
List comps = new LinkedList(this.entered);
if (comps.isEmpty()) return true; // nothing to wait for
for (Completion c : comps) {
try {
if (tt.timedOut()) return false;
c.waitUntilComplete(tt);
} catch (TimeoutException unused) {
return false; // we ran out of time
}
}
return true;
}
/**
* Abort all threads waiting to enter with an AbortedException
.
*/
public void abortWaiters() {
gate.abort();
}
}