2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Copyright (C) 2017 Amdocs
8 * =============================================================================
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
21 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 * ============LICENSE_END=========================================================
25 package org.openecomp.appc.concurrent;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.List;
30 import java.util.concurrent.TimeoutException;
32 import org.openecomp.appc.util.StringHelper;
35 * This class is used to synchronize signaling of status between threads.
37 * In complex multi-threaded applications it is often necessary to synchronize operations between threads. This is
38 * especially true in complex algorithms where processing dependencies exist between different threads and the
39 * synchronization of the operations of those threads is required. This class is a framework to enable multi-thread
40 * signaling and wait/post logic that makes the thread synchronization easier.
43 * Basically, in thread synchronization, one thread is the "waiter" and one or more other threads are the "notifiers".
44 * The notifiers send signals to the waiter to inform that thread that certain conditions are true, processing has been
45 * completed, or to inform the waiter of the state of the other thread(s). In the basic java framework, the waiter and
46 * notifier are simply using the wait/notify mechanism provided, which does not allow for different conditions, state,
47 * or "signals" to exist. The wait/notify mechanism, in combination with the object mutex, provides basic blocking and
48 * releasing of a thread's dispatching state.
51 * This class builds upon the java wait/notify mechanism and allows for "signals" to be defined. These signals are
52 * simply string constants that mean something to the waiter and notifier threads. Any number of signals may be defined,
53 * and it is possible to wait for more than one signal to be received, wait for any one of a set to be received, or to
54 * test if a signal has been received without blocking.
57 * Some operations are blocking operations. These stop the execution of the calling thread until the specified condition
58 * is true. These blocking methods are all named "wait...", such as {@link #waitFor(String...)} and
59 * {@link #waitForAny(String...)}. The thread making the call to these blocking methods MUST be the waiter thread (the
60 * thread registered with the signal object).
63 * Some operations are non-blocking. These operations allow for the testing or setting of signal conditions and do not
64 * block the caller. When calling these methods ({@link #isSignaled(String)}, {@link #signal(String)}, and
65 * {@link #setTimeout(long)} the waiter thread mutex will be held and may block the waiter thread for the duration of
72 * The thread must be the thread of the waiter that is waiting for the signals to be received. It is the recipient
73 * of the signaled condition. This allows any number of other threads to send signals to the recipient and have the
74 * recipient synchronize its operation with the receipt of the appropriate signal(s).
76 private Thread thread;
79 * The amount of time to wait for a signal to be receieved. Set to zero to wait forever.
81 private long timeout = 0L;
84 * The collection of all received signals. Note, this need not be a synchronized collection because it will always
85 * be accessed while holding the mutex of the thread, therefore it is implicitly synchronized.
87 private List<String> receivedSignals;
90 * A signal object must access a thread that is waiting for the receipt of the signal(s).
92 public Signal(Thread thread) {
94 receivedSignals = new ArrayList<String>();
98 * Checks the waiter to see if it has been signaled
101 * The signal to check for
102 * @return True if the signal has been received, false otherwise
104 public boolean isSignaled(String signal) {
105 synchronized (thread) {
106 return _signaled(signal);
111 * Sends the indicated signal to the waiter.
114 * The signal that is to be sent to the waiting thread and to notify it to process the signal.
116 public void signal(String signal) {
117 synchronized (thread) {
118 if (!_signaled(signal)) {
119 receivedSignals.add(signal);
126 * Blocks the waiting thread until all of the indicated signals have been received, or the wait times out.
129 * The signals to be received. The waiter is blocked forever or until all of the signals are received.
130 * @throws TimeoutException
131 * If the wait has timed out waiting for a response
133 public void waitFor(String... signals) throws TimeoutException {
134 long limit = System.currentTimeMillis() + timeout;
135 synchronized (thread) {
137 boolean complete = true;
138 for (String signal : signals) {
139 if (!_signaled(signal)) {
145 receivedSignals.removeAll(Arrays.asList(signals));
150 if (System.currentTimeMillis() > limit) {
151 throw new TimeoutException(String.format("Signals %s not received in the allotted timeout.",
152 StringHelper.asList(signals)));
157 thread.wait(timeout);
158 } catch (InterruptedException e) {
160 * Interrupted exceptions are ignored
168 * This method blocks the waiter until at least one of the indicated signals have been received.
171 * A list of signals, any one of which will satisfy the wait condition
172 * @return The signal that satisfied the wait
173 * @throws TimeoutException
174 * If none of the signals have been received within the allotted time
176 public String waitForAny(String... signals) throws TimeoutException {
177 long limit = System.currentTimeMillis() + timeout;
178 synchronized (thread) {
180 for (String signal : signals) {
181 if (!_signaled(signal)) {
182 receivedSignals.remove(signal);
188 if (System.currentTimeMillis() > limit) {
189 throw new TimeoutException(
190 String.format("One of signals \"%s\" not received in the allotted timeout.",
191 StringHelper.asList(signals)));
196 thread.wait(timeout);
197 } catch (InterruptedException e) {
199 * Interrupted exceptions are ignored
207 * This private method is used to handle the check for signaled status. Note that this method assumes the caller
208 * holds the thread mutex.
211 * The list of signals to check for
212 * @return True if any one of the signals has been received.
214 private boolean _signaled(String... signals) {
215 for (String signal : signals) {
216 if (receivedSignals.contains(signal)) {
224 * Sets the timeout value for waiting for signals to be received
228 public void setTimeout(long timeout) {
229 this.timeout = timeout;