Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
RecursiveLockImpl01CompleteFair.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2021 Gothel Software e.K.
4 * Copyright (c) 2010 Gothel Software e.K.
5 * Copyright (c) 2010 JogAmp Community.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27package jau.test.util.parallel.locks.impl;
28
29import java.util.ArrayList;
30import java.util.List;
31import java.util.concurrent.locks.AbstractOwnableSynchronizer;
32
33import org.jau.lang.SourcedInterruptedException;
34
35import jau.test.util.parallel.locks.RecursiveLock;
36
37/**
38 * Reentrance locking toolkit, impl a complete fair FIFO scheduler
39 *
40 * <p>
41 * Sync object extends {@link AbstractOwnableSynchronizer}, hence monitoring is possible.</p>
42 */
44
45 private static class WaitingThread {
46 WaitingThread(final Thread t) {
47 thread = t;
48 signaledByUnlock = false;
49 }
50 final Thread thread;
51 boolean signaledByUnlock; // if true, it's also removed from queue
52 }
53
54 @SuppressWarnings("serial")
55 private static class Sync extends AbstractOwnableSynchronizer {
56 private Sync() {
57 super();
58 }
59 private final Thread getOwner() {
60 return getExclusiveOwnerThread();
61 }
62 private final void setOwner(final Thread t) {
63 setExclusiveOwnerThread(t);
64 }
65 private final void setLockedStack(final Throwable s) {
66 final List<Throwable> ls = LockDebugUtil.getRecursiveLockTrace();
67 if(s==null) {
68 ls.remove(lockedStack);
69 } else {
70 ls.add(s);
71 }
72 lockedStack = s;
73 }
74 /** lock count by same thread */
75 private int holdCount = 0;
76 /** waiting thread queue */
77 final ArrayList<WaitingThread> queue = new ArrayList<WaitingThread>();
78 /** stack trace of the lock, only used if DEBUG */
79 private Throwable lockedStack = null;
80 }
81 private final Sync sync = new Sync();
82
84 }
85
86 /**
87 * Returns the Throwable instance generated when this lock was taken the 1st time
88 * and if {@link jau.test.util.parallel.locks.Lock#DEBUG} is turned on, otherwise it returns always <code>null</code>.
89 * @see jau.test.util.parallel.locks.Lock#DEBUG
90 */
91 public final Throwable getLockedStack() {
92 synchronized(sync) {
93 return sync.lockedStack;
94 }
95 }
96
97 @Override
98 public final Thread getOwner() {
99 synchronized(sync) {
100 return sync.getOwner();
101 }
102 }
103
104 @Override
105 public final boolean isOwner(final Thread thread) {
106 synchronized(sync) {
107 return sync.getOwner() == thread ;
108 }
109 }
110
111 @Override
112 public final boolean isLocked() {
113 synchronized(sync) {
114 return null != sync.getOwner();
115 }
116 }
117
118 @Override
119 public final boolean isLockedByOtherThread() {
120 synchronized(sync) {
121 final Thread o = sync.getOwner();
122 return null != o && Thread.currentThread() != o ;
123 }
124 }
125
126 @Override
127 public final int getHoldCount() {
128 synchronized(sync) {
129 return sync.holdCount;
130 }
131 }
132
133 @Override
134 public final void validateLocked() throws RuntimeException {
135 synchronized(sync) {
136 if ( Thread.currentThread() != sync.getOwner() ) {
137 if ( null == sync.getOwner() ) {
138 throw new RuntimeException(threadName(Thread.currentThread())+": Not locked: "+toString());
139 }
140 if(null!=sync.lockedStack) {
141 sync.lockedStack.printStackTrace();
142 }
143 throw new RuntimeException(Thread.currentThread()+": Not owner: "+toString());
144 }
145 }
146 }
147
148 @Override
149 public final void lock() {
150 synchronized(sync) {
151 try {
152 if(!tryLock(TIMEOUT)) {
153 if(null!=sync.lockedStack) {
154 sync.lockedStack.printStackTrace();
155 }
156 throw new RuntimeException("Waited "+TIMEOUT+"ms for: "+toString()+" - "+threadName(Thread.currentThread()));
157 }
158 } catch (final InterruptedException e) {
159 throw new RuntimeException("Interrupted", e);
160 }
161 }
162 }
163
164 @Override
165 public final boolean tryLock(long timeout) throws InterruptedException {
166 synchronized(sync) {
167 final Thread cur = Thread.currentThread();
168 if(TRACE_LOCK) {
169 System.err.println("+++ LOCK 0 "+toString()+", cur "+threadName(cur));
170 }
171 if (sync.getOwner() == cur) {
172 ++sync.holdCount;
173 if(TRACE_LOCK) {
174 System.err.println("+++ LOCK XR "+toString()+", cur "+threadName(cur));
175 }
176 return true;
177 }
178
179 if ( sync.getOwner() != null || ( 0<timeout && 0<sync.queue.size() ) ) {
180
181 if ( 0 >= timeout ) {
182 // locked by other thread and no waiting requested
183 if(TRACE_LOCK) {
184 System.err.println("+++ LOCK XY "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms");
185 }
186 return false;
187 }
188
189 // enqueue at the start
190 final WaitingThread wCur = new WaitingThread(cur);
191 sync.queue.add(0, wCur);
192 do {
193 final long t0 = System.currentTimeMillis();
194 try {
195 sync.wait(timeout);
196 timeout -= System.currentTimeMillis() - t0;
197 } catch (final InterruptedException e) {
198 if( !wCur.signaledByUnlock ) {
199 sync.queue.remove(wCur); // O(n)
200 throw SourcedInterruptedException.wrap(e); // propagate interruption not send by unlock
201 } else if( cur != sync.getOwner() ) {
202 // Issued by unlock, but still locked by other thread
203 //
204 timeout -= System.currentTimeMillis() - t0;
205
206 if(TRACE_LOCK) {
207 System.err.println("+++ LOCK 1 "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms, signaled: "+wCur.signaledByUnlock);
208 }
209
210 if(0 < timeout) {
211 // not timed out, re-enque - lock was 'stolen'
212 wCur.signaledByUnlock = false;
213 sync.queue.add(sync.queue.size(), wCur);
214 }
215 } // else: Issued by unlock, owning lock .. expected!
216 }
217 } while ( cur != sync.getOwner() && 0 < timeout ) ;
218 Thread.interrupted(); // clear slipped interrupt
219
220 if( 0 >= timeout && cur != sync.getOwner() ) {
221 // timed out
222 if(!wCur.signaledByUnlock) {
223 sync.queue.remove(wCur); // O(n)
224 }
225 if(TRACE_LOCK) {
226 System.err.println("+++ LOCK XX "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms");
227 }
228 return false;
229 }
230
231 ++sync.holdCount;
232 if(TRACE_LOCK) {
233 System.err.println("+++ LOCK X1 "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms");
234 }
235 } else {
236 ++sync.holdCount;
237 if(TRACE_LOCK) {
238 System.err.println("+++ LOCK X0 "+toString()+", cur "+threadName(cur));
239 }
240 }
241
242 sync.setOwner(cur);
243 if(DEBUG) {
244 sync.setLockedStack(new Throwable("Previously locked by "+toString()));
245 }
246 return true;
247 }
248 }
249
250
251 @Override
252 public final void unlock() {
253 synchronized(sync) {
254 unlock(null);
255 }
256 }
257
258 @Override
259 public final void unlock(final Runnable taskAfterUnlockBeforeNotify) {
260 synchronized(sync) {
262 final Thread cur = Thread.currentThread();
263
264 --sync.holdCount;
265
266 if (sync.holdCount > 0) {
267 if(TRACE_LOCK) {
268 System.err.println("--- LOCK XR "+toString()+", cur "+threadName(cur));
269 }
270 return;
271 }
272
273 if(DEBUG) {
274 sync.setLockedStack(null);
275 }
276 if(null!=taskAfterUnlockBeforeNotify) {
277 taskAfterUnlockBeforeNotify.run();
278 }
279
280 if(sync.queue.size() > 0) {
281 // fair, wakeup the oldest one ..
282 // final WaitingThread oldest = queue.removeLast();
283 final WaitingThread oldest = sync.queue.remove(sync.queue.size()-1);
284 sync.setOwner(oldest.thread);
285
286 if(TRACE_LOCK) {
287 System.err.println("--- LOCK X1 "+toString()+", cur "+threadName(cur)+", signal: "+threadName(oldest.thread));
288 }
289
290 oldest.signaledByUnlock = true;
291 oldest.thread.interrupt(); // Propagate SecurityException if it happens
292 } else {
293 sync.setOwner(null);
294 if(TRACE_LOCK) {
295 System.err.println("--- LOCK X0 "+toString()+", cur "+threadName(cur)+", signal any");
296 }
297 sync.notify();
298 }
299 }
300 }
301
302 @Override
303 public final int getQueueLength() {
304 synchronized(sync) {
305 return sync.queue.size();
306 }
307 }
308
309 @Override
310 public String toString() {
311 return syncName()+"[count "+sync.holdCount+
312 ", qsz "+sync.queue.size()+", owner "+threadName(sync.getOwner())+"]";
313 }
314
315 private final String syncName() {
316 return "<"+Integer.toHexString(this.hashCode())+", "+Integer.toHexString(sync.hashCode())+">";
317 }
318 private final String threadName(final Thread t) { return null!=t ? "<"+t.getName()+">" : "<NULL>" ; }
319}
320
Functionality enabled if Lock#DEBUG is true.
static List< Throwable > getRecursiveLockTrace()
Reentrance locking toolkit, impl a complete fair FIFO scheduler.
final boolean isLockedByOtherThread()
Query whether the lock is hold by the a thread other than the current thread.
final void lock()
Blocking until the lock is acquired by this Thread or TIMEOUT is reached.
final int getHoldCount()
Return the number of locks issued to this lock by the same thread.
final void unlock(final Runnable taskAfterUnlockBeforeNotify)
Execute the Runnable taskAfterUnlockBeforeNotify while holding the exclusive lock.
final Throwable getLockedStack()
Returns the Throwable instance generated when this lock was taken the 1st time and if jau....
final boolean tryLock(long timeout)
Blocking until the lock is acquired by this Thread or maxwait in ms is reached.
final boolean isOwner(final Thread thread)
Query whether the lock is hold by the given thread.
static final long TIMEOUT
The TIMEOUT for lock() in ms, defaults to DEFAULT_TIMEOUT.
Definition: Lock.java:52
static final boolean DEBUG
Enable via the property jogamp.debug.Lock
Definition: Lock.java:37
static final boolean TRACE_LOCK
Enable via the property jogamp.debug.Lock.TraceLock
Definition: Lock.java:40
Reentrance capable locking toolkit.