1
1
/*
2
- * Copyright (c) 2003, 2020 , Oracle and/or its affiliates. All rights reserved.
2
+ * Copyright (c) 2003, 2023 , Oracle and/or its affiliates. All rights reserved.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* This code is free software; you can redistribute it and/or modify it
43
43
44
44
public class Locks {
45
45
46
- private static final Object OBJA = new Object ();
47
- private static final Object OBJB = new Object ();
46
+ private static class ObjectA { }
47
+ private static class ObjectB { }
48
+
49
+ private static final Object OBJA = new ObjectA ();
50
+ private static final Object OBJB = new ObjectB ();
48
51
private static final EnhancedWaiter OBJC = new EnhancedWaiter ();
49
52
private static final ThreadMXBean TM = ManagementFactory .getThreadMXBean ();
50
53
private static final LockFreeLogger LOGGER = new LockFreeLogger ();
@@ -65,6 +68,8 @@ private static void assertNoLock(Thread t) {
65
68
TM .getThreadInfo (TM .getAllThreadIds (), true , true ))
66
69
.filter (Objects ::nonNull )
67
70
.filter (i -> name .equals (i .getLockOwnerName ()))
71
+ /* Carrier Thread can hold a lock on a VirtualThread, which we ignore: */
72
+ .filter (i -> !i .getLockName ().contains ("java.lang.VirtualThread" ))
68
73
.findAny ();
69
74
if (result .isPresent ()) {
70
75
throw new RuntimeException ("Thread " + t .getName () + " is not "
@@ -117,34 +122,34 @@ private static void assertThreadState(Thread t, Thread.State expectedState) {
117
122
*/
118
123
private static void checkBlockedObject (Thread t , Object lock , Thread owner ) {
119
124
long tid = t .getId ();
120
- String result = TM .getThreadInfo (tid ).getLockName ();
125
+ String lockName = TM .getThreadInfo (tid ).getLockName ();
121
126
final String expectedLock = (lock != null ? getLockName (lock ) : null );
122
127
Predicate <String > p = (res ) -> ((res != null && !res .equals (expectedLock ))
123
128
|| (res == null && expectedLock != null ));
124
129
125
- if (p .test (result )) {
130
+ if (p .test (lockName )) {
126
131
printStackTrace (t );
127
132
int retryCount = 0 ;
128
- while (p .test (result )) {
133
+ while (p .test (lockName )) {
129
134
if (retryCount ++ > 500 ) {
130
135
printStackTrace (t );
131
136
throw new RuntimeException ("Thread " + t .getName () + " is blocked on "
132
- + expectedLock + " but got " + result );
137
+ + expectedLock + " but got " + lockName );
133
138
}
134
139
goSleep (100 );
135
- result = TM .getThreadInfo (tid ).getLockName ();
140
+ lockName = TM .getThreadInfo (tid ).getLockName ();
136
141
}
137
142
}
138
143
139
- result = TM .getThreadInfo (tid ).getLockOwnerName ();
140
- final String expectedOwner = (owner != null ? owner .getName () : null );
144
+ String lockOwnerName = TM .getThreadInfo (tid ).getLockOwnerName ();
145
+ final String expectedOwnerName = (owner != null ? owner .getName () : null );
141
146
142
- p = (res ) -> ((res != null && !res .equals (expectedOwner ))
143
- || (res == null && expectedOwner != null ));
144
- if (p .test (result )) {
147
+ p = (res ) -> ((res != null && !res .equals (expectedOwnerName ))
148
+ || (res == null && expectedOwnerName != null ));
149
+ if (p .test (lockOwnerName )) {
145
150
printStackTrace (t );
146
151
throw new RuntimeException ("Owner of " + lock + " should be "
147
- + expectedOwner + " but got " + result );
152
+ + expectedOwnerName + " but got " + lockOwnerName );
148
153
}
149
154
}
150
155
@@ -342,14 +347,13 @@ Exception result() {
342
347
public static void main (String args []) throws Exception {
343
348
try {
344
349
Thread mainThread = Thread .currentThread ();
345
-
346
350
// Test uncontested case
347
351
LockAThread t1 ;
348
352
LockBThread t2 ;
349
353
350
354
Phaser p = new Phaser (3 );
351
355
synchronized (OBJC ) {
352
- // Make sure the main thread is not holding any lock
356
+ // Make sure the main thread is not holding any lock (except possibly a VirtualThread)
353
357
assertNoLock (mainThread );
354
358
355
359
// Test deadlock case
@@ -362,15 +366,22 @@ public static void main(String args[]) throws Exception {
362
366
t2 .start ();
363
367
364
368
p .arriveAndAwaitAdvance (); // Phase 1 (blocking)
369
+
365
370
assertThreadState (t2 , Thread .State .BLOCKED );
366
- checkBlockedObject (t2 , OBJC , mainThread );
371
+ if (!mainThread .isVirtual ()) {
372
+ // ThreadInfo not available for Virtual Threads.
373
+ checkBlockedObject (t2 , OBJC , mainThread );
374
+ }
367
375
assertThreadState (t1 , Thread .State .BLOCKED );
368
376
checkBlockedObject (t1 , OBJB , t2 );
369
377
370
- long [] expectedThreads = new long [3 ];
378
+ long [] expectedThreads = new long [mainThread . isVirtual () ? 2 : 3 ];
371
379
expectedThreads [0 ] = t1 .getId (); // blocked on lockB
372
380
expectedThreads [1 ] = t2 .getId (); // owner of lockB blocking on lockC
373
- expectedThreads [2 ] = mainThread .getId (); // owner of lockC
381
+ if (!mainThread .isVirtual ()) {
382
+ // ThreadInfo not available for Virtual Threads.
383
+ expectedThreads [2 ] = mainThread .getId (); // owner of lockC
384
+ }
374
385
findThreadsBlockedOn (OBJB , expectedThreads );
375
386
}
376
387
p .arriveAndAwaitAdvance (); // Phase 2 (blocking)
@@ -400,6 +411,9 @@ private static ThreadInfo findOwnerInfo(ThreadInfo[] infos, String lock)
400
411
throws Exception {
401
412
ThreadInfo ownerInfo = null ;
402
413
for (ThreadInfo info : infos ) {
414
+ if (info == null ) {
415
+ continue ; // Missing thread, e.g. completed. Ignore.
416
+ }
403
417
String blockedLock = info .getLockName ();
404
418
if (lock .equals (blockedLock )) {
405
419
long threadId = info .getLockOwnerId ();
@@ -421,19 +435,22 @@ private static void findThreadsBlockedOn(Object o, long[] expectedThreads)
421
435
throws Exception {
422
436
String lock = getLockName (o );
423
437
// Check with ThreadInfo with no stack trace (i.e. no safepoint)
424
- ThreadInfo [] infos = TM .getThreadInfo (TM .getAllThreadIds ());
425
- doCheck (infos , lock , expectedThreads );
438
+ ThreadInfo [] allThreadInfos = TM .getThreadInfo (TM .getAllThreadIds ());
439
+ doCheck (allThreadInfos , lock , expectedThreads );
426
440
427
441
// Check with ThreadInfo with stack trace
428
- infos = TM .getThreadInfo (TM .getAllThreadIds (), 1 );
429
- doCheck (infos , lock , expectedThreads );
442
+ allThreadInfos = TM .getThreadInfo (TM .getAllThreadIds (), 1 );
443
+ doCheck (allThreadInfos , lock , expectedThreads );
430
444
}
431
445
432
446
private static void doCheck (ThreadInfo [] infos , String lock , long [] expectedThreads )
433
447
throws Exception {
434
448
ThreadInfo ownerInfo = null ;
435
449
// Find the thread who is blocking on lock
436
450
for (ThreadInfo info : infos ) {
451
+ if (info == null ) {
452
+ continue ; // Missing thread, e.g. completed. Ignore.
453
+ }
437
454
String blockedLock = info .getLockName ();
438
455
if (lock .equals (blockedLock )) {
439
456
log ("%s blocked on %s" , info .getThreadName (), blockedLock );
@@ -445,11 +462,19 @@ private static void doCheck(ThreadInfo[] infos, String lock, long[] expectedThre
445
462
"Can't retrieve ThreadInfo for the blocked thread" );
446
463
}
447
464
465
+ // Follow chain of locks:
448
466
long [] threads = new long [10 ];
449
467
int count = 0 ;
450
468
threads [count ++] = ownerInfo .getThreadId ();
451
469
while (ownerInfo .getThreadState () == Thread .State .BLOCKED ) {
452
470
ownerInfo = findOwnerInfo (infos , lock );
471
+ log ("ownerInfo = %s" , ownerInfo );
472
+ if (ownerInfo .getThreadName ().contains ("ForkJoinPool" )) {
473
+ // Ignore e.g. "ForkJoinPool-1-worker-1" waiting on a VirtualThread
474
+ log ("skipping %s" , ownerInfo );
475
+ lock = ownerInfo .getLockName ();
476
+ continue ;
477
+ }
453
478
threads [count ++] = ownerInfo .getThreadId ();
454
479
log (" Owner = %s id = %d" ,
455
480
ownerInfo .getThreadName (),
@@ -468,14 +493,19 @@ private static void doCheck(ThreadInfo[] infos, String lock, long[] expectedThre
468
493
throw new RuntimeException ("TEST FAILED: " +
469
494
"Expected chain of threads not matched; current count =" + count );
470
495
}
496
+ int failures = 0 ;
471
497
for (int i = 0 ; i < count ; i ++) {
472
498
if (threads [i ] != expectedThreads [i ]) {
473
499
log ("TEST FAILED: Unexpected thread in the chain %s expected to be %s" ,
474
500
threads [i ],
475
501
expectedThreads [i ]
476
502
);
503
+ failures ++;
477
504
}
478
505
}
506
+ if (failures > 0 ) {
507
+ throw new RuntimeException ("TEST FAILED: " + failures + " unexpected thread(s)." );
508
+ }
479
509
}
480
510
481
511
private static void log (String format , Object ... args ) {
0 commit comments