[ 
https://issues.apache.org/jira/browse/GROOVY-8067?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15858506#comment-15858506
 ] 

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_r100162813
  
    --- Diff: 
src/main/org/codehaus/groovy/util/ManagedConcurrentLinkedQueue.java ---
    @@ -0,0 +1,187 @@
    +/*
    + *  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.util;
    +
    +import java.util.ArrayList;
    +import java.util.Collections;
    +import java.util.Iterator;
    +import java.util.List;
    +import java.util.NoSuchElementException;
    +import java.util.concurrent.ConcurrentLinkedQueue;
    +
    +/**
    + * A queue that stores the values wrapped in a Reference, the type of 
which is
    + * determined by the provided {@link ReferenceBundle}.  Values stored in 
this queue
    + * that are put on the {@code ReferenceQueue} will be removed from the 
list when
    + * reference processing for the {@code ReferenceQueue} is done.
    + *
    + * This queue is backed by a {@link ConcurrentLinkedQueue} and is thread 
safe.  The
    + * iterator will only return non-null values (reachable) and is based on 
the
    + * "weakly consistent" iterator of the underlying {@link 
ConcurrentLinkedQueue}.
    + *
    + * @param <T> the type of values to store
    + */
    +public class ManagedConcurrentLinkedQueue<T> implements Iterable<T> {
    +
    +    private final ReferenceBundle bundle;
    +    private final ConcurrentLinkedQueue<Element<T>> queue;
    +
    +    /**
    +     * Creates an empty ManagedConcurrentLinkedQueue that will use the 
given
    +     * {@code ReferenceBundle} to store values as the given Reference
    +     * type.
    +     *
    +     * @param bundle used to create the appropriate Reference type
    +     *               for the values stored
    +     */
    +    public ManagedConcurrentLinkedQueue(ReferenceBundle bundle) {
    +        this.bundle = bundle;
    +        this.queue = new ConcurrentLinkedQueue<Element<T>>();
    +    }
    +
    +    /**
    +     * Adds the specified value to the queue.
    +     *
    +     * @param value the value to add
    +     */
    +    public void add(T value) {
    +        Element<T> e = new Element<T>(value);
    +        queue.offer(e);
    +    }
    +
    +    /**
    +     * Returns {@code true} if this queue contains no elements.
    +     * <p>
    +     * This method does not check the elements to verify they contain
    +     * non-null reference values.
    +     */
    +    public boolean isEmpty() {
    +        return queue.isEmpty();
    +    }
    +
    +    /**
    +     * Returns an array containing all values from this queue in the 
sequence they
    +     * were added.
    +     *
    +     * @param tArray the array to populate if big enough, else a new array 
with
    +     *               the same runtime type
    +     * @return an array containing all non-null values in this queue
    +     */
    +    public T[] toArray(T[] tArray) {
    +        return values().toArray(tArray);
    +    }
    +
    +    /**
    +     * Returns an unmodifiable list containing all values from this queue 
in the
    +     * sequence they were added.
    +     */
    +    public List<T> values() {
    +        Iterator<T> itr = iterator();
    +        if (!itr.hasNext()) {
    +            return Collections.emptyList();
    +        }
    +        List<T> result = new ArrayList<T>(100);
    +        result.add(itr.next());
    +        while (itr.hasNext()) {
    +            result.add(itr.next());
    +        }
    +        return Collections.unmodifiableList(result);
    +    }
    +
    +    /**
    +     * Returns an iterator over all non-null values in this queue.  The 
values should be
    +     * returned in the order they were added.
    +     */
    +    @Override
    +    public Iterator<T> iterator() {
    +        return new Iter(queue.iterator());
    +    }
    +
    +    private final class Element<V> extends ManagedReference<V> {
    +
    +        Element(V value) {
    +            super(bundle, value);
    +        }
    +
    +        @Override
    +        public void finalizeReference() {
    +            queue.remove(this);
    +            super.finalizeReference();
    +        }
    +
    +    }
    +
    +    private final class Iter implements Iterator<T> {
    --- End diff --
    
    think this can be made a static nested class


> 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