[ 
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)

Reply via email to