Skip to content

Commit cc870d4

Browse files
committed
8352088: Call of com.sun.jdi.ThreadReference.threadGroups() can lock up target VM
Reviewed-by: alanb, jpai, sspitsyn
1 parent d979bd8 commit cc870d4

File tree

2 files changed

+130
-2
lines changed

2 files changed

+130
-2
lines changed

src/java.base/share/classes/java/lang/ThreadGroup.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -658,10 +658,17 @@ private List<ThreadGroup> synchronizedSubgroups() {
658658

659659
/**
660660
* Returns a snapshot of the subgroups as an array, used by JVMTI.
661+
* WARNING: Make sure this method does not trigger any class loading,
662+
* because a ClassPrepare event can deadlock the debugger and debug agent.
661663
*/
662664
private ThreadGroup[] subgroupsAsArray() {
663665
List<ThreadGroup> groups = synchronizedSubgroups();
664-
return groups.toArray(new ThreadGroup[0]);
666+
int count = groups.size();
667+
var array = new ThreadGroup[count];
668+
for (int i = 0; i < count; i++) {
669+
array[i] = groups.get(i);
670+
}
671+
return array;
665672
}
666673

667674
/**
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/**
25+
* @test
26+
* @bug 8352088
27+
* @summary If ThreadGroupReference.groups() is called very early on from an
28+
* event handler, it can cause a deadlock because the call can result
29+
* in ClassPrepareEvents, which the debug agent will block on until
30+
* the debugger handles them, which it won't because the event handler
31+
* thread is waiting for a reply to ThreadGroupReference.groups().
32+
*
33+
* @run build TestScaffold VMConnection TargetListener TargetAdapter
34+
* @run compile -g EarlyThreadGroupChildrenTest.java
35+
* @run main/othervm/timeout=20 EarlyThreadGroupChildrenTest
36+
*/
37+
38+
import com.sun.jdi.*;
39+
import com.sun.jdi.event.*;
40+
import com.sun.jdi.request.*;
41+
42+
import java.util.*;
43+
44+
class EarlyThreadGroupChildrenTestTarg {
45+
public static void main(String[] args) throws InterruptedException {
46+
System.out.println("Start");
47+
System.out.println("Finish");
48+
}
49+
}
50+
51+
/********** test program **********/
52+
53+
public class EarlyThreadGroupChildrenTest extends TestScaffold {
54+
EarlyThreadGroupChildrenTest(String args[]) {
55+
super(args);
56+
}
57+
58+
public static void main(String[] args) throws Exception {
59+
new EarlyThreadGroupChildrenTest(args).startTests();
60+
}
61+
62+
/********** event handlers **********/
63+
64+
ClassPrepareRequest cpRequest;
65+
ThreadStartRequest tsRequest;
66+
67+
@Override
68+
public void threadStarted(ThreadStartEvent event) {
69+
System.out.println("Got ThreadStartEvent: " + event);
70+
cpRequest = eventRequestManager().createClassPrepareRequest();
71+
cpRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL);
72+
cpRequest.enable();
73+
}
74+
75+
static volatile int classPreparedCount = 0;
76+
77+
@Override
78+
public void classPrepared(ClassPrepareEvent event) {
79+
try {
80+
++classPreparedCount;
81+
System.out.println("ClassPreparedEvent " + classPreparedCount +
82+
" on thread " + event.thread() +
83+
": " + event.referenceType());
84+
List<ThreadGroupReference> groups = vm().topLevelThreadGroups();
85+
ThreadGroupReference systemThreadGroup = groups.get(0);
86+
groups = systemThreadGroup.threadGroups();
87+
System.out.println("system child ThreadGroups: " + groups);
88+
} catch (VMDisconnectedException e) {
89+
// This usually eventually happens during shutdown.
90+
System.out.println("ClassPreparedEvent " + classPreparedCount +
91+
": Got VMDisconnectedException");
92+
}
93+
}
94+
95+
public void vmDisconnected(VMDisconnectEvent event) {
96+
System.out.println("Got VMDisconnectEvent");
97+
}
98+
99+
/********** test core **********/
100+
101+
protected void runTests() throws Exception {
102+
connect(new String[]{"EarlyThreadGroupChildrenTestTarg"});
103+
System.out.println("Connected: ");
104+
addListener(this);
105+
106+
// Create a ThreadStartRequest for the first ThreadStartEvent. When this event is
107+
// received, we will enable ClassPrepareEvents.
108+
tsRequest = eventRequestManager().createThreadStartRequest();
109+
tsRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL);
110+
tsRequest.addCountFilter(1);
111+
tsRequest.enable();
112+
113+
waitForVMStart();
114+
System.out.println("VM Started: ");
115+
116+
resumeToVMDisconnect();
117+
118+
// Failure mode for this test is deadlocking, so there is no error to check for.
119+
System.out.println("EarlyThreadGroupChildrenTest: passed");
120+
}
121+
}

0 commit comments

Comments
 (0)