a76f0f78ad
Reviewed-by: dholmes, iignatyev
272 lines
8.4 KiB
Java
272 lines
8.4 KiB
Java
/*
|
|
* Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package nsk.share;
|
|
|
|
import java.io.PrintStream;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.locks.Condition;
|
|
import java.util.concurrent.locks.Lock;
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
|
|
/**
|
|
* Wicket provides a means for one or more threads to suspend execution
|
|
* (to wait) until notified by one or more other threads that some set
|
|
* of locks is now open.
|
|
*
|
|
* <p>Wicket instances are intended to be used generally in the following
|
|
* scenarios:
|
|
*
|
|
* <ul><li>One thread starts one or more child threads and waits until the
|
|
* child threads to be started.
|
|
*
|
|
* <li>One thread starts one or more child threads and waits until at least
|
|
* one of the child threads to be started.
|
|
*
|
|
* <li>One or more child threads wait until a main thread lets them
|
|
* to finish.
|
|
*
|
|
* <li>Disable the current thread for thread scheduling purposes, for up to
|
|
* the specified waiting time.</ul>
|
|
*/
|
|
|
|
public class Wicket {
|
|
|
|
/** Number of closed locks, can be greater or equal to zero */
|
|
private int count;
|
|
|
|
/** Number of waiters **/
|
|
private int waiters = 0;
|
|
|
|
/** Enable debug output */
|
|
private PrintStream debugOutput = null;
|
|
|
|
/** Wicket's string identifier */
|
|
private String name = "";
|
|
|
|
private final Lock lock = new ReentrantLock();
|
|
private final Condition condition = lock.newCondition();
|
|
|
|
/**
|
|
* Construct a Wicket with only one closed lock.
|
|
*/
|
|
public Wicket() {
|
|
this(1);
|
|
}
|
|
|
|
/**
|
|
* Construct a Wicket with the given number of closed locks.
|
|
*
|
|
* @param _name Wicket's identifier
|
|
* @param _count the initial number of closed locks
|
|
* @param _debugOutput whether to print debug info or not
|
|
* @throws IllegalArgumentException if count is less than 1
|
|
*/
|
|
public Wicket(String _name, int _count, PrintStream _debugOutput) {
|
|
this(_count);
|
|
name = _name;
|
|
debugOutput = _debugOutput;
|
|
}
|
|
|
|
/**
|
|
* Construct a Wicket with the given number of closed locks.
|
|
*
|
|
* @param count the initial number of closed locks
|
|
* @throws IllegalArgumentException if count is less than 1
|
|
*/
|
|
public Wicket(int count) {
|
|
if (count < 1)
|
|
throw new IllegalArgumentException(
|
|
"count is less than one: " + count);
|
|
this.count = count;
|
|
}
|
|
|
|
/**
|
|
* Wait for all locks of this Wicket to be open.
|
|
*
|
|
* <p>If all locks are already open then returns immediately.
|
|
*
|
|
* <p>If at least one lock is still closed then the current thread becomes
|
|
* disabled for thread scheduling purposes and lies dormant until all
|
|
* the locks will be open by some other threads. One lock can be open
|
|
* by invoking the unlock method for this Wicket.
|
|
*
|
|
* <p>Please note, that the method would ignore Thread.interrupt() requests.
|
|
*/
|
|
public void waitFor() {
|
|
long id = System.currentTimeMillis();
|
|
|
|
lock.lock();
|
|
try {
|
|
++waiters;
|
|
if (debugOutput != null) {
|
|
debugOutput.printf("Wicket %d %s: waitFor(). There are %d waiters totally now.\n", id, name, waiters);
|
|
}
|
|
|
|
while (count > 0) {
|
|
try {
|
|
condition.await();
|
|
} catch (InterruptedException e) {
|
|
}
|
|
}
|
|
--waiters;
|
|
} finally {
|
|
lock.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wait for all locks of this Wicket to be open within the given
|
|
* period of time.
|
|
*
|
|
* <p>If all locks are already open then returns immediately with zero.
|
|
*
|
|
* <p>If the time is equal to zero, the method will not
|
|
* wait and returns a number of closed locks,
|
|
* if all locks are open, the return value is zero.
|
|
*
|
|
* <p>If at least one lock is still closed then the current thread becomes
|
|
* disabled for thread scheduling purposes and lies dormant until
|
|
* of the two things happens:
|
|
*
|
|
* <ul><li>Some other threads invoke the unlock method for this Wicket
|
|
* to open all the closed locks; or
|
|
*
|
|
* <li>The specified waiting time elapses.</ul>
|
|
*
|
|
* <p>If all locks are open then the return value is 0.
|
|
*
|
|
* <p>If the specified waiting time elapses and some locks are still closed
|
|
* then the return value is equal to number of closed locks.
|
|
*
|
|
* <p>Please note, that the method would ignore Thread.interrupt() requests.
|
|
*
|
|
* @param timeout the maximum time to wait in milliseconds
|
|
* @return the number of closed locks
|
|
* @throws IllegalArgumentException if timeout is less than 0
|
|
*/
|
|
public int waitFor(long timeout) {
|
|
if (timeout < 0)
|
|
throw new IllegalArgumentException(
|
|
"timeout value is negative: " + timeout);
|
|
|
|
long id = System.currentTimeMillis();
|
|
|
|
lock.lock();
|
|
try {
|
|
++waiters;
|
|
if (debugOutput != null) {
|
|
debugOutput.printf("Wicket %d %s: waitFor(). There are %d waiters totally now.\n", id, name, waiters);
|
|
}
|
|
|
|
long waitTime = timeout;
|
|
long startTime = System.currentTimeMillis();
|
|
|
|
while (count > 0 && waitTime > 0) {
|
|
try {
|
|
condition.await(waitTime, TimeUnit.MILLISECONDS);
|
|
} catch (InterruptedException e) {
|
|
}
|
|
waitTime = timeout - (System.currentTimeMillis() - startTime);
|
|
}
|
|
--waiters;
|
|
return count;
|
|
} finally {
|
|
lock.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unlock one closed lock.
|
|
*
|
|
* <p>Open a lock, reducing the number of closed locks by one.
|
|
*
|
|
* <p>If last closed lock is opened then all of the threads waiting
|
|
* by invoking the waitFor method for this Wicket will be released
|
|
* and re-enabled for thread scheduling purposes.
|
|
*
|
|
* @throws IllegalStateException if there is no one closed lock
|
|
*/
|
|
public void unlock() {
|
|
|
|
lock.lock();
|
|
try {
|
|
if (count == 0)
|
|
throw new IllegalStateException("locks are already open");
|
|
|
|
--count;
|
|
if (debugOutput != null) {
|
|
debugOutput.printf("Wicket %s: unlock() the count is now %d\n", name, count);
|
|
}
|
|
|
|
if (count == 0) {
|
|
condition.signalAll();
|
|
}
|
|
} finally {
|
|
lock.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unlock all closed locks.
|
|
*
|
|
* <p>Open all closed locks, setting the number of closed locks to zero.
|
|
*
|
|
* <p>If any threads are waiting by invoking the waitFor method for
|
|
* this Wicket then they will be released and re-enabled for thread
|
|
* scheduling purposes.
|
|
*/
|
|
public void unlockAll() {
|
|
if (debugOutput != null) {
|
|
debugOutput.printf("Wicket %s: unlockAll()\n", name);
|
|
}
|
|
|
|
lock.lock();
|
|
try {
|
|
count = 0;
|
|
condition.signalAll();
|
|
} finally {
|
|
lock.unlock();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return current number of waiters - threads that are currently
|
|
* waiting using one of waitFor methods.
|
|
*
|
|
* @return number of waiters
|
|
*/
|
|
public int getWaiters() {
|
|
|
|
lock.lock();
|
|
try {
|
|
if (debugOutput != null) {
|
|
debugOutput.printf("Wicket %s: getWaiters()\n", name);
|
|
}
|
|
return waiters;
|
|
} finally {
|
|
lock.unlock();
|
|
}
|
|
}
|
|
}
|