Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
RecursiveLockImpl01Unfairish.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.List;
30import java.util.concurrent.locks.AbstractOwnableSynchronizer;
31
32import jau.test.util.parallel.locks.RecursiveLock;
33
34/**
35 * Reentrance locking toolkit, impl a non-complete fair FIFO scheduler.
36 * <p>
37 * Fair scheduling is not guaranteed due to the usage of {@link Object#notify()},
38 * however new lock-applicants will wait if queue is not empty for {@link #lock()}
39 * and {@link #tryLock(long) tryLock}(timeout>0).</p>
40 *
41 * <p>
42 * Sync object extends {@link AbstractOwnableSynchronizer}, hence monitoring is possible.</p>
43 */
45
46 /* package */ static interface Sync {
47 Thread getOwner();
48 boolean isOwner(Thread t);
49 void setOwner(Thread t);
50
51 Throwable getLockedStack();
52 void setLockedStack(Throwable s);
53
54 int getHoldCount();
55 void incrHoldCount(Thread t);
56 void decrHoldCount(Thread t);
57
58 int getQSz();
59 void incrQSz();
60 void decrQSz();
61 }
62
63 @SuppressWarnings("serial")
64 /* package */ static class SingleThreadSync extends AbstractOwnableSynchronizer implements Sync {
65 /* package */ SingleThreadSync() {
66 super();
67 }
68 @Override
69 public final Thread getOwner() {
70 return getExclusiveOwnerThread();
71 }
72 @Override
73 public boolean isOwner(final Thread t) {
74 return getExclusiveOwnerThread()==t;
75 }
76 @Override
77 public final void setOwner(final Thread t) {
78 setExclusiveOwnerThread(t);
79 }
80 @Override
81 public final Throwable getLockedStack() {
82 return lockedStack;
83 }
84 @Override
85 public final void setLockedStack(final Throwable s) {
86 final List<Throwable> ls = LockDebugUtil.getRecursiveLockTrace();
87 if(s==null) {
88 ls.remove(lockedStack);
89 } else {
90 ls.add(s);
91 }
92 lockedStack = s;
93 }
94 @Override
95 public final int getHoldCount() { return holdCount; }
96 @Override
97 public void incrHoldCount(final Thread t) { holdCount++; }
98 @Override
99 public void decrHoldCount(final Thread t) { holdCount--; }
100
101 @Override
102 public final int getQSz() { return qsz; }
103 @Override
104 public final void incrQSz() { qsz++; }
105 @Override
106 public final void decrQSz() { qsz--; }
107
108 /** lock count by same thread */
109 private int holdCount = 0;
110 /** queue size of waiting threads */
111 private int qsz = 0;
112 /** stack trace of the lock, only used if DEBUG */
113 private Throwable lockedStack = null;
114 }
115
116 protected final Sync sync;
117
119 this.sync = sync;
120 }
121
123 this(new SingleThreadSync());
124 }
125
126 /**
127 * Returns the Throwable instance generated when this lock was taken the 1st time
128 * and if {@link jau.test.util.parallel.locks.Lock#DEBUG} is turned on, otherwise it returns always <code>null</code>.
129 * @see jau.test.util.parallel.locks.Lock#DEBUG
130 */
131 public final Throwable getLockedStack() {
132 synchronized(sync) {
133 return sync.getLockedStack();
134 }
135 }
136
137 @Override
138 public final Thread getOwner() {
139 synchronized(sync) {
140 return sync.getOwner();
141 }
142 }
143
144 @Override
145 public final boolean isOwner(final Thread thread) {
146 synchronized(sync) {
147 return sync.isOwner(thread);
148 }
149 }
150
151 @Override
152 public final boolean isLocked() {
153 synchronized(sync) {
154 return null != sync.getOwner();
155 }
156 }
157
158 @Override
159 public final boolean isLockedByOtherThread() {
160 synchronized(sync) {
161 final Thread o = sync.getOwner();
162 return null != o && Thread.currentThread() != o ;
163 }
164 }
165
166 @Override
167 public final int getHoldCount() {
168 synchronized(sync) {
169 return sync.getHoldCount();
170 }
171 }
172
173 @Override
174 public final void validateLocked() throws RuntimeException {
175 synchronized(sync) {
176 if ( !sync.isOwner(Thread.currentThread()) ) {
177 if ( null == sync.getOwner() ) {
178 throw new RuntimeException(threadName(Thread.currentThread())+": Not locked: "+toString());
179 }
180 if(null!=sync.getLockedStack()) {
181 sync.getLockedStack().printStackTrace();
182 }
183 throw new RuntimeException(Thread.currentThread()+": Not owner: "+toString());
184 }
185 }
186 }
187
188 @Override
189 public final void lock() {
190 synchronized(sync) {
191 try {
192 if(!tryLock(TIMEOUT)) {
193 if(null!=sync.getLockedStack()) {
194 sync.getLockedStack().printStackTrace();
195 }
196 throw new RuntimeException("Waited "+TIMEOUT+"ms for: "+toString()+" - "+threadName(Thread.currentThread()));
197 }
198 } catch (final InterruptedException e) {
199 throw new RuntimeException("Interrupted", e);
200 }
201 }
202 }
203
204 @Override
205 public final boolean tryLock(long timeout) throws InterruptedException {
206 synchronized(sync) {
207 final Thread cur = Thread.currentThread();
208 if(TRACE_LOCK) {
209 System.err.println("+++ LOCK 0 "+toString()+", timeout "+timeout+" ms, cur "+threadName(cur));
210 }
211 if (sync.isOwner(cur)) {
212 sync.incrHoldCount(cur);
213 if(TRACE_LOCK) {
214 System.err.println("+++ LOCK XR "+toString()+", cur "+threadName(cur));
215 }
216 return true;
217 }
218
219 if ( sync.getOwner() != null || ( 0<timeout && 0<sync.getQSz() ) ) {
220
221 if ( 0 >= timeout ) {
222 // locked by other thread and no waiting requested
223 if(TRACE_LOCK) {
224 System.err.println("+++ LOCK XY "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms");
225 }
226 return false;
227 }
228
229 sync.incrQSz();
230 do {
231 final long t0 = System.currentTimeMillis();
232 sync.wait(timeout);
233 timeout -= System.currentTimeMillis() - t0;
234 } while (null != sync.getOwner() && 0 < timeout) ;
235 sync.decrQSz();
236
237 if( 0 >= timeout && sync.getOwner() != null ) {
238 // timed out
239 if(TRACE_LOCK) {
240 System.err.println("+++ LOCK XX "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms");
241 }
242 return false;
243 }
244
245 if(TRACE_LOCK) {
246 System.err.println("+++ LOCK X1 "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms");
247 }
248 } else if(TRACE_LOCK) {
249 System.err.println("+++ LOCK X0 "+toString()+", cur "+threadName(cur));
250 }
251
252 sync.setOwner(cur);
253 sync.incrHoldCount(cur);
254
255 if(DEBUG) {
256 sync.setLockedStack(new Throwable("Previously locked by "+toString()));
257 }
258 return true;
259 }
260 }
261
262
263 @Override
264 public final void unlock() {
265 synchronized(sync) {
266 unlock(null);
267 }
268 }
269
270 @Override
271 public void unlock(final Runnable taskAfterUnlockBeforeNotify) {
272 synchronized(sync) {
274 final Thread cur = Thread.currentThread();
275
276 sync.decrHoldCount(cur);
277
278 if (sync.getHoldCount() > 0) {
279 if(TRACE_LOCK) {
280 System.err.println("--- LOCK XR "+toString()+", cur "+threadName(cur));
281 }
282 return;
283 }
284
285 sync.setOwner(null);
286 if(DEBUG) {
287 sync.setLockedStack(null);
288 }
289 if(null!=taskAfterUnlockBeforeNotify) {
290 taskAfterUnlockBeforeNotify.run();
291 }
292
293 if(TRACE_LOCK) {
294 System.err.println("--- LOCK X0 "+toString()+", cur "+threadName(cur)+", signal any");
295 }
296 sync.notify();
297 }
298 }
299
300 @Override
301 public final int getQueueLength() {
302 synchronized(sync) {
303 return sync.getQSz();
304 }
305 }
306
307 @Override
308 public String toString() {
309 return syncName()+"[count "+sync.getHoldCount()+
310 ", qsz "+sync.getQSz()+", owner "+threadName(sync.getOwner())+"]";
311 }
312
313 /* package */ final String syncName() {
314 return "<"+Integer.toHexString(this.hashCode())+", "+Integer.toHexString(sync.hashCode())+">";
315 }
316 /* package */ final String threadName(final Thread t) { return null!=t ? "<"+t.getName()+">" : "<NULL>" ; }
317}
318
Functionality enabled if Lock#DEBUG is true.
static List< Throwable > getRecursiveLockTrace()
Reentrance locking toolkit, impl a non-complete fair FIFO scheduler.
final boolean tryLock(long timeout)
Blocking until the lock is acquired by this Thread or maxwait in ms is reached.
void unlock(final Runnable taskAfterUnlockBeforeNotify)
Execute the Runnable taskAfterUnlockBeforeNotify while holding the exclusive lock.
final boolean isOwner(final Thread thread)
Query whether the lock is hold by the given thread.
final Throwable getLockedStack()
Returns the Throwable instance generated when this lock was taken the 1st time and if jau....
final int getHoldCount()
Return the number of locks issued to this lock by the same thread.
final void lock()
Blocking until the lock is acquired by this Thread or TIMEOUT is reached.
final boolean isLockedByOtherThread()
Query whether the lock is hold by the a thread other than the current 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.