[
https://issues.apache.org/jira/browse/GROOVY-8067?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15858505#comment-15858505
]
ASF GitHub Bot commented on GROOVY-8067:
----------------------------------------
Github user jwagenleitner commented on a diff in the pull request:
https://github.com/apache/groovy/pull/489#discussion_r100162935
--- Diff:
subprojects/stress/src/test/java/org/codehaus/groovy/reflection/ClassInfoDeadlockStressTest.java
---
@@ -0,0 +1,140 @@
+/*
+ * 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.codehaus.groovy.reflection;
+
+import groovy.lang.GroovyClassLoader;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.groovy.stress.util.GCUtils;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ * Tests for deadlocks in the ClassInfo caching.
+ *
+ */
+public class ClassInfoDeadlockStressTest {
+
+ private static final int DEADLOCK_TRIES = 8;
+ private static final int THREAD_COUNT = 8;
+
+ private final CountDownLatch startLatch = new CountDownLatch(1);
+ private final CountDownLatch completeLatch = new
CountDownLatch(THREAD_COUNT);
+ private final GroovyClassLoader gcl = new GroovyClassLoader();
+ private final AtomicInteger counter = new AtomicInteger();
+
+ /**
+ * We first generate a large number of ClassInfo instances for classes
+ * that are no longer reachable. Then queue up threads to all request
+ * ClassInfo instances for new classes simultaneously to ensure that
+ * clearing the old references wont deadlock the creation of new
+ * instances.
+ * <p>
+ * GROOVY-8067
+ */
+ @Test
+ public void testDeadlock() throws Exception {
+ for (int i = 1; i <= DEADLOCK_TRIES; i++) {
+ System.out.println("Test Number: " + i);
+ generateGarbage();
+ GCUtils.gc();
+ attemptDeadlock(null);
+ }
+ }
+
+ @Test
+ public void testRequestsForSameClassInfo() throws Exception {
+ Class<?> newClass = createRandomClass();
+ for (int i = 1; i <= DEADLOCK_TRIES; i++) {
+ System.out.println("Test Number: " + i);
+ generateGarbage();
+ GCUtils.gc();
+ attemptDeadlock(newClass);
+ }
+ ClassInfo newClassInfo = ClassInfo.getClassInfo(newClass);
+ for (ClassInfo ci : ClassInfo.getAllClassInfo()) {
+ if (ci.getTheClass() == newClass && ci != newClassInfo) {
+ fail("Found multiple ClassInfo instances for class");
+ }
+ }
+ }
+
+ private void attemptDeadlock(final Class<?> cls) throws Exception {
+ for (int i = 0; i < THREAD_COUNT; i++) {
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ Class<?> newClass = (cls == null) ?
createRandomClass() : cls;
+ try {
+ startLatch.await();
+ } catch (InterruptedException ie) {
--- End diff --
can use `ThreadUtils.awaite()`
> Possible deadlock when creating new ClassInfo entries in the cache
> ------------------------------------------------------------------
>
> Key: GROOVY-8067
> URL: https://issues.apache.org/jira/browse/GROOVY-8067
> Project: Groovy
> Issue Type: Bug
> Components: groovy-runtime
> Affects Versions: 2.4.8
> Reporter: John Wagenleitner
> Priority: Critical
> Attachments: ClassInfoDeadlockTest.java
>
>
> When running Groovy without {{-Dgroovy.use.classvalue=true}} the ClassInfo
> instances are cached in a {{ManagedConcurrentMap}} (MCM). New values are
> computed on demand and computation involves both a lock on a segment within
> the MCM and a lock on the {{GlobalClassSet}} (GCS) which is backed by a
> {{ManagedLinkedList}}. The problem is that both the ManagedConcurrentMap and
> the GlobalClassSet share the same ReferenceQueue.
> Assume there is an enqueued {{ClassInfo}} value that is stored in Segment2 of
> the MCM. Now assume that Thread1 and Thread2 both request
> {{ClassInfo.getClassInfo(..)}} for two different classes that do not
> currently exist in the cache. Assume that based on hashing Thread1 gets a
> lock on Segment1 and Thread2 gets a lock on Segment2. Assume that Thread1 is
> the first to call computeValue which in turn calls
> {{GlobalClassSet.add(..)}}. This call adds a new value to a
> {{ManagedLinkedList}}, and since it's managed the add operation will process
> the ReferenceQueue. So Thread1 will attempt to dequeue the ClassInfo and the
> finalizeReference method on it's entry will attempt to remove it from
> Segment2. Thread2 holds the lock for Segment2 and Thread2 is blocked and
> can't progress it's waiting on the the lock Thread1 holds the lock for the
> GlobalClassSet, so deadlock occurs.
> The attached test case includes a thread dump at the bottom.
--
This message was sent by Atlassian JIRA
(v6.3.15#6346)