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