Author: mir
Date: Thu Sep 2 15:11:57 2010
New Revision: 991968
URL: http://svn.apache.org/viewvc?rev=991968&view=rev
Log:
CLEREZZA-295: implemented rdf lock debugging capabilities
Added:
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/ReadLockDebug.java
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/ReentrantReadWriteLockTracker.java
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/WriteLockDebug.java
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/pom.xml
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/src/
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/src/main/
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/src/main/java/
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/src/main/java/org/
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/src/main/java/org/apache/
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/src/main/java/org/apache/clerezza/
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/src/main/java/org/apache/clerezza/rdf/
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/src/main/java/org/apache/clerezza/rdf/locking/
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/src/main/java/org/apache/clerezza/rdf/locking/LockOverview.java
Modified:
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/LockableMGraphWrapper.java
Modified:
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/LockableMGraphWrapper.java
URL:
http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/LockableMGraphWrapper.java?rev=991968&r1=991967&r2=991968&view=diff
==============================================================================
---
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/LockableMGraphWrapper.java
(original)
+++
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/LockableMGraphWrapper.java
Thu Sep 2 15:11:57 2010
@@ -18,6 +18,7 @@
*/
package org.apache.clerezza.rdf.core.access;
+import org.apache.clerezza.rdf.core.access.debug.ReentrantReadWriteLockTracker;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.locks.Lock;
@@ -40,7 +41,17 @@ import org.apache.clerezza.rdf.core.even
*/
public class LockableMGraphWrapper implements LockableMGraph {
- private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+ private static final String DEBUG_MODE = "rdfLocksDebugging";
+ private final ReadWriteLock lock;
+ {
+ String debugMode = System.getProperty(DEBUG_MODE);
+ if (debugMode != null &&
debugMode.toLowerCase().equals("true")) {
+ lock = new ReentrantReadWriteLockTracker();
+ } else {
+ lock = new ReentrantReadWriteLock();
+ }
+ }
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
private final MGraph wrapped;
Added:
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/ReadLockDebug.java
URL:
http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/ReadLockDebug.java?rev=991968&view=auto
==============================================================================
---
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/ReadLockDebug.java
(added)
+++
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/ReadLockDebug.java
Thu Sep 2 15:11:57 2010
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.clerezza.rdf.core.access.debug;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
+
+/**
+ *
+ * @author mir
+ */
+public class ReadLockDebug extends ReadLock {
+
+ ReentrantReadWriteLockTracker lock;
+ StackTraceElement[] stackTrace;
+
+ ReadLock readLock;
+ public ReadLockDebug(ReentrantReadWriteLockTracker lock) {
+ super(lock);
+ this.lock = lock;
+ this.readLock = lock.realReadLock();
+ }
+
+ @Override
+ public void lock() {
+ readLock.lock();
+ lock.addLockedReadLock(this);
+ stackTrace = Thread.currentThread().getStackTrace();
+ }
+
+ @Override
+ public void lockInterruptibly() throws InterruptedException {
+ readLock.lockInterruptibly();
+ }
+
+ @Override
+ public Condition newCondition() {
+ return readLock.newCondition();
+ }
+
+ @Override
+ public String toString() {
+ return readLock.toString();
+ }
+
+ @Override
+ public boolean tryLock() {
+ return readLock.tryLock();
+ }
+
+ @Override
+ public boolean tryLock(long timeout, TimeUnit unit) throws
InterruptedException {
+ return readLock.tryLock(timeout, unit);
+ }
+
+ @Override
+ public void unlock() {
+ readLock.unlock();
+ lock.removeReadLock(this);
+ stackTrace = null;
+ }
+
+ public StackTraceElement[] getStackTrace() {
+ return stackTrace;
+ }
+
+}
Added:
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/ReentrantReadWriteLockTracker.java
URL:
http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/ReentrantReadWriteLockTracker.java?rev=991968&view=auto
==============================================================================
---
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/ReentrantReadWriteLockTracker.java
(added)
+++
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/ReentrantReadWriteLockTracker.java
Thu Sep 2 15:11:57 2010
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.clerezza.rdf.core.access.debug;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ *
+ * @author mir
+ */
+public class ReentrantReadWriteLockTracker extends ReentrantReadWriteLock {
+
+
+ private Set<ReadLockDebug> lockedReadLocks =
Collections.synchronizedSet(new HashSet<ReadLockDebug>());
+ private final WriteLockDebug writeLock = new WriteLockDebug(this);
+ @Override
+ protected Thread getOwner() {
+ return super.getOwner();
+ }
+
+ @Override
+ protected Collection<Thread> getQueuedReaderThreads() {
+ return super.getQueuedReaderThreads();
+ }
+
+ @Override
+ protected Collection<Thread> getQueuedThreads() {
+ return super.getQueuedThreads();
+ }
+
+ @Override
+ protected Collection<Thread> getQueuedWriterThreads() {
+ return super.getQueuedWriterThreads();
+ }
+
+ @Override
+ public int getReadHoldCount() {
+ return super.getReadHoldCount();
+ }
+
+ @Override
+ public int getReadLockCount() {
+ return super.getReadLockCount();
+ }
+
+ @Override
+ public int getWaitQueueLength(Condition condition) {
+ return super.getWaitQueueLength(condition);
+ }
+
+ @Override
+ protected Collection<Thread> getWaitingThreads(Condition condition) {
+ return super.getWaitingThreads(condition);
+ }
+
+ @Override
+ public int getWriteHoldCount() {
+ return super.getWriteHoldCount();
+ }
+
+ @Override
+ public boolean hasWaiters(Condition condition) {
+ return super.hasWaiters(condition);
+ }
+
+ @Override
+ public boolean isWriteLocked() {
+ return super.isWriteLocked();
+ }
+
+ @Override
+ public boolean isWriteLockedByCurrentThread() {
+ return super.isWriteLockedByCurrentThread();
+ }
+
+ @Override
+ public ReadLock readLock() {
+ return new ReadLockDebug(this);
+ }
+
+ ReadLock realReadLock() {
+ return super.readLock();
+ }
+
+ WriteLock realWriteLock() {
+ return super.writeLock();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString();
+ }
+
+ @Override
+ public WriteLockDebug writeLock() {
+ return writeLock;
+ }
+
+ void addLockedReadLock(ReadLockDebug lock) {
+ lockedReadLocks.add(lock);
+ }
+
+ void removeReadLock(ReadLockDebug lock) {
+ lockedReadLocks.remove(lock);
+ }
+
+ public Set<ReadLockDebug> getLockedReadLocks() {
+ return lockedReadLocks;
+ }
+
+
+}
Added:
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/WriteLockDebug.java
URL:
http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/WriteLockDebug.java?rev=991968&view=auto
==============================================================================
---
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/WriteLockDebug.java
(added)
+++
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.core/src/main/java/org/apache/clerezza/rdf/core/access/debug/WriteLockDebug.java
Thu Sep 2 15:11:57 2010
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.clerezza.rdf.core.access.debug;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
+
+/**
+ *
+ * @author mir
+ */
+public class WriteLockDebug extends WriteLock {
+
+ private ReentrantReadWriteLockTracker lock;
+ private WriteLock writeLock;
+ private StackTraceElement[] stackTrace;
+
+ public WriteLockDebug(ReentrantReadWriteLockTracker lock) {
+ super(lock);
+ this.lock = lock;
+ this.writeLock = lock.realWriteLock();
+ }
+
+ @Override
+ public int getHoldCount() {
+ return writeLock.getHoldCount();
+ }
+
+ @Override
+ public boolean isHeldByCurrentThread() {
+ return writeLock.isHeldByCurrentThread();
+ }
+
+ @Override
+ public void lock() {
+ writeLock.lock();
+ stackTrace = Thread.currentThread().getStackTrace();
+ }
+
+ @Override
+ public void lockInterruptibly() throws InterruptedException {
+ writeLock.lockInterruptibly();
+ }
+
+ @Override
+ public Condition newCondition() {
+ return writeLock.newCondition();
+ }
+
+ @Override
+ public boolean tryLock() {
+ return writeLock.tryLock();
+ }
+
+ @Override
+ public boolean tryLock(long timeout, TimeUnit unit) throws
InterruptedException {
+ return writeLock.tryLock(timeout, unit);
+ }
+
+ @Override
+ public void unlock() {
+ writeLock.unlock();
+ stackTrace = null;
+ }
+
+ public StackTraceElement[] getStackTrace() {
+ return stackTrace;
+ }
+
+
+}
Added:
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/pom.xml
URL:
http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/pom.xml?rev=991968&view=auto
==============================================================================
---
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/pom.xml
(added)
+++
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/pom.xml
Thu Sep 2 15:11:57 2010
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.clerezza</groupId>
+ <artifactId>org.apache.clerezza.parent</artifactId>
+ <version>0.2-incubating-SNAPSHOT</version>
+ </parent>
+ <groupId>org.apache.clerezza</groupId>
+ <artifactId>org.apache.clerezza.rdf.locking</artifactId>
+ <version>0.1-incubating-SNAPSHOT</version>
+
+ <packaging>bundle</packaging>
+ <name>Clerezza - RDF locking</name>
+ <description>Provides a web page containig an overview of graph
locks.</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+
<artifactId>org.apache.felix.scr.annotations</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>jsr311-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.clerezza</groupId>
+
<artifactId>org.apache.clerezza.platform.typerendering.core</artifactId>
+ </dependency>
+ </dependencies>
+</project>
Added:
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/src/main/java/org/apache/clerezza/rdf/locking/LockOverview.java
URL:
http://svn.apache.org/viewvc/incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/src/main/java/org/apache/clerezza/rdf/locking/LockOverview.java?rev=991968&view=auto
==============================================================================
---
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/src/main/java/org/apache/clerezza/rdf/locking/LockOverview.java
(added)
+++
incubator/clerezza/trunk/org.apache.clerezza.parent/org.apache.clerezza.rdf.locking/src/main/java/org/apache/clerezza/rdf/locking/LockOverview.java
Thu Sep 2 15:11:57 2010
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.clerezza.rdf.locking;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import org.apache.clerezza.rdf.core.UriRef;
+import org.apache.clerezza.rdf.core.access.TcManager;
+import org.apache.clerezza.rdf.core.access.debug.ReadLockDebug;
+import org.apache.clerezza.rdf.core.access.debug.ReentrantReadWriteLockTracker;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+
+/**
+ * Provides an overview page of all locks of all graphs.
+ * @author mir
+ */
+...@component()
+...@service(value = Object.class)
+...@property(name = "javax.ws.rs", boolValue = true)
+...@path("/locks")
+public class LockOverview {
+
+ @Reference
+ TcManager tcManager;
+
+ @GET
+ public String getOverview() {
+ Set<Thread> threadSet = getAllThreads();
+ Iterator<UriRef> mGraphUris =
tcManager.listMGraphs().iterator();
+
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+ while (mGraphUris.hasNext()) {
+ UriRef uriRef = mGraphUris.next();
+ ReentrantReadWriteLock lock = (ReentrantReadWriteLock)
tcManager.getMGraph(uriRef).getLock();
+ int readLockCount = lock.getReadLockCount();
+ printWriter.append(uriRef.getUnicodeString());
+ printWriter.append("\n");
+ printWriter.append("Read-Lock count: " +
readLockCount);
+ printWriter.append("\n");
+ printWriter.append("Write-Locked: " +
(lock.isWriteLocked() ? "YES" : "NO"));
+ printWriter.append("\n");
+ printWriter.append("Has queued threads: " +
(lock.hasQueuedThreads() ? "YES" : "NO"));
+ printWriter.append("\n");
+ if (readLockCount > 0 && lock instanceof
ReentrantReadWriteLockTracker) {
+ ReentrantReadWriteLockTracker debugLock =
(ReentrantReadWriteLockTracker) lock;
+ printWriter.append("Threads holding read-lock:
\n");
+ Set<ReadLockDebug> lockedReadLocks =
debugLock.getLockedReadLocks();
+ for (ReadLockDebug readLockDebug :
lockedReadLocks) {
+
printStackTrace(readLockDebug.getStackTrace(), printWriter);
+ printWriter.append("\n");
+ }
+ printWriter.append("\n");
+ }
+ if (lock.isWriteLocked() && lock instanceof
ReentrantReadWriteLockTracker) {
+ ReentrantReadWriteLockTracker debugLock =
(ReentrantReadWriteLockTracker) lock;
+ printWriter.append("Thread holding write-lock:
\n");
+
printStackTrace(debugLock.writeLock().getStackTrace(), printWriter);
+ printWriter.append("\n");
+ }
+ printWriter.append("Queue length: " +
lock.getQueueLength());
+ printWriter.append("\n");
+ printWriter.append("Queued threads: ");
+ printWriter.append("\n");
+ printQueuedThreads(lock, printWriter, threadSet);
+
printWriter.append("----------------------------------------------------");
+ printWriter.append("\n");
+
+ }
+ return stringWriter.toString();
+ }
+
+ private void printQueuedThreads(ReentrantReadWriteLock lock,
PrintWriter printWriter,
+ Set<Thread> threadSet) {
+ for (Thread thread : threadSet) {
+ if (lock.hasQueuedThread(thread)) {
+ printWriter.append("" + thread.getId());
+ printStackTrace(thread.getStackTrace(),
printWriter);
+ printWriter.append("\n");
+ }
+ }
+ }
+
+ private void printStackTrace(StackTraceElement[] stackTrace,
PrintWriter printWriter) {
+ Throwable throwable = new Throwable();
+ throwable.setStackTrace(stackTrace);
+ throwable.printStackTrace(printWriter);
+ }
+
+ public Set<Thread> getAllThreads() {
+ // Find the root thread group
+ ThreadGroup root =
Thread.currentThread().getThreadGroup().getParent();
+ while (root.getParent() != null) {
+ root = root.getParent();
+ }
+ HashSet<Thread> threadSet = new HashSet<Thread>();
+ visit(root, 0, threadSet);
+ return threadSet;
+ }
+
+ public void visit(ThreadGroup group, int level, HashSet<Thread>
threadSet) {
+ // Get threads in `group'
+ int numThreads = group.activeCount();
+ Thread[] threads = new Thread[numThreads * 2];
+ numThreads = group.enumerate(threads, false);
+ // Enumerate each thread in `group'
+ for (int i = 0; i < numThreads; i++) {
+ // Get thread
+ Thread thread = threads[i];
+ threadSet.add(thread);
+ }
+ // Get thread subgroups of `group'
+ int numGroups = group.activeGroupCount();
+ ThreadGroup[] groups = new ThreadGroup[numGroups * 2];
+ numGroups = group.enumerate(groups, false);
+ // Recursively visit each subgroup
+ for (int i = 0; i < numGroups; i++) {
+ visit(groups[i], level + 1, threadSet);
+ }
+ }
+}