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

com.rabbitmq.jms.util.WaiterGate Maven / Gradle / Ivy

There is a newer version: 3.4.0
Show newest version
/* Copyright (c) 2013-2023 Broadcom. All Rights Reserved. The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. */
package com.rabbitmq.jms.util;


/**
 * Hand-crafted gate for pausing multiple threads on entry to a region. Allows waiting threads to be aborted (return
 * with {@link AbortedException}) without using interrupts.
 * 
*
Description
*
*

* The {@link GateState} is either {@link GateState.OPENED OPENED}, {@link GateState.CLOSED CLOSED} or {@link GateState.ABORTED ABORTED}, and the main * operations are {@link #open()}, {@link #close()}, {@link #waitForOpen(TimeTracker) waitForOpen()} and * {@link #abort()}. The abstract methods {@link #onEntry()} and {@link #onAbort()} * must be defined by an implementing class and are called (under the gate {@link GateState.lock lock} at the appropriate transition points. *

*

* If the gate is {@link GateState.CLOSED CLOSED} and {@link #waitForOpen(TimeTracker) waitForOpen()} is called, the caller blocks until the * gate is {@link GateState.OPENED OPENED}, the timeout expires, or the gate is {@link GateState.ABORTED ABORTED}. If the gate is * {@link GateState.OPENED OPENED}, either already or at a later time, however briefly, the call returns true * after the {@link #onEntry()} method is called. *

*

* A gate may be {@link GateState.OPENED OPENED} and {@link GateState.CLOSED CLOSED} multiple times * ({@link WaiterGate.generation generation} counts these openings, starting at 0) * but once {@link GateState.ABORTED ABORTED} cannot be opened again. If a thread waits for a gate to open, and the * gate is opened, then the thread will enter the gate eventually, even if the gate is opened and closed rapidly * before the thread gets control again. This is the reason for the {@link WaiterGate.generation generation} state. *

*
*
*/ abstract class WaiterGate { /** possible states of the gate */ private enum GateState { OPENED, CLOSED, ABORTED }; private Object lock = new Object(); private GateState state; // @GuardedBy("lock") private long generation; // @GuardedBy("lock") /** * Create a gate, either {@link GateState.OPENED OPENED} or {@link GateState.CLOSED CLOSED}. It cannot be created {@link GateState.ABORTED ABORTED}. * @param opened - true if gate is initially {@link GateState.OPENED OPENED}; false if initially * {@link GateState.CLOSED CLOSED} */ public WaiterGate(boolean opened) { this.state = (opened ? GateState.OPENED : GateState.CLOSED); this.generation = 0; } /** * @return true if gate is open, false otherwise */ public final boolean isOpen() { synchronized(this.lock){ return this.state == GateState.OPENED; } } /** * Close the gate, provided it is {@link GateState.OPENED OPENED}. * @return true if gate {@link GateState.CLOSED CLOSED} by this call, false otherwise */ public final boolean close() { synchronized(this.lock) { if (this.state == GateState.OPENED) { this.state = GateState.CLOSED; this.generation++; return true; // no need to notify queued threads. } } return false; } /** * Open the gate. Can be done only if gate is {@link GateState.CLOSED CLOSED}. * @return true if gate {@link GateState.OPENED OPENED} by this call, false otherwise */ public final boolean open() { synchronized(this.lock) { if (this.state == GateState.CLOSED) { this.state = GateState.OPENED; this.lock.notifyAll(); // allow current queued threads to pass. return true; } } return false; } /** * Called atomically when thread passes through open gate. */ public abstract void onEntry(); /** * Called atomically when thread is aborted while waiting for open. */ public abstract void onAbort(); /** * Wait (and queue) if gate is {@link GateState.CLOSED CLOSED}; or return true if gate is * {@link GateState.OPENED OPENED} soon enough. *

Calls {@link #onEntry()} when gate is {@link GateState.OPENED OPENED} now or later and * {@link #onAbort()} when the gate is {@link GateState.ABORTED ABORTED} now or later. *

* @param tracker - timeout tracker; tracks time until gate is {@link GateState.OPENED OPENED}. * @return true if gate is {@link GateState.OPENED OPENED} now or within time limit, false if time * limit expires while waiting * @throws InterruptedException if waiting thread is interrupted. * @throws AbortedException if gate is {@link GateState.ABORTED ABORTED} now or within time limit. */ public final boolean waitForOpen(TimeTracker tracker) throws InterruptedException, AbortedException { synchronized(this.lock) { long arrivalGeneration = this.generation; while ((this.state == GateState.CLOSED) && (arrivalGeneration == this.generation) && (!tracker.timedOut())) { tracker.timedWait(this.lock); } // this.state == OPENED | ABORTED OR arrivalGeneration != generation OR timeout() GateState derivedState = this.state; if (derivedState == GateState.ABORTED) { this.onAbort(); throw new AbortedException(); } else if (derivedState == GateState.OPENED || arrivalGeneration != this.generation) { this.onEntry(); return true; } else return false; // we timed out } } /** * Abort (cause to abandon wait and throw {@link AbortedException}) all threads waiting on {@link GateState.CLOSED CLOSED} gate. * @return true if gate is {@link GateState.ABORTED ABORTED} by this call; false otherwise */ public final boolean abort() { synchronized(this.lock){ if (this.state != GateState.CLOSED) return false; this.state = GateState.ABORTED; this.lock.notifyAll(); // allow current queued threads to see abort. } return true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy