Author: pats Date: Tue Jan 25 04:03:48 2011 New Revision: 1063132 URL: http://svn.apache.org/viewvc?rev=1063132&view=rev Log: Reimplement outrigger's FastList, based on java.util.ConcurrentLinkedQueue. Modify its users to reflect the switch from explicit link-chasing to Iterator.
Modified: incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/EntryHolder.java incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/FastList.java incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/WatchersForTemplateClass.java Modified: incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/EntryHolder.java URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/EntryHolder.java?rev=1063132&r1=1063131&r2=1063132&view=diff ============================================================================== --- incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/EntryHolder.java (original) +++ incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/EntryHolder.java Tue Jan 25 04:03:48 2011 @@ -18,6 +18,7 @@ package com.sun.jini.outrigger; import java.util.Hashtable; +import java.util.Iterator; import java.util.Set; import java.util.WeakHashMap; import java.util.logging.Level; @@ -38,7 +39,7 @@ import net.jini.core.transaction.server. */ class EntryHolder implements TransactionConstants { /** The list that holds the handles */ - private final FastList contents = new FastList(); + private final FastList<EntryHandle> contents = new FastList<EntryHandle>(); /** * The map of cookies to handles, shared with the @@ -114,44 +115,44 @@ class EntryHolder implements Transaction * @see #attemptCapture */ EntryHandle hasMatch(EntryRep tmpl, TransactableMgr txn, boolean takeIt, - Set conflictSet, Set lockedEntrySet, - WeakHashMap provisionallyRemovedEntrySet) - throws CannotJoinException - { - matchingLogger.entering("EntryHolder", "hasMatch"); - - EntryHandle handle = (EntryHandle) contents.head(); - if (handle == null) - return null; // Nothing here - - final EntryHandleTmplDesc desc = - EntryHandle.descFor(tmpl, handle.rep().numFields()); - final long startTime = System.currentTimeMillis(); - - for (;handle != null; handle = (EntryHandle) handle.next()) { - if (handle.removed()) - continue; - - // Quick reject -- see the if handle mask is incompatible - if ((handle.hash() & desc.mask) != desc.hash) - continue; + Set conflictSet, Set lockedEntrySet, + WeakHashMap provisionallyRemovedEntrySet) + throws CannotJoinException { + matchingLogger.entering("EntryHolder", "hasMatch"); + EntryHandleTmplDesc desc = null; + long startTime = 0; + + for (EntryHandle handle : contents) { + + if (startTime == 0) { + // First time through + desc = EntryHandle.descFor(tmpl, handle.rep().numFields()); + startTime = System.currentTimeMillis(); + } + + if (handle.removed()) + continue; + + // Quick reject -- see the if handle mask is incompatible + if ((handle.hash() & desc.mask) != desc.hash) + continue; + + final EntryRep rep = handle.rep(); + + if (!tmpl.matches(rep)) + continue; + + final boolean available = confirmAvailabilityWithTxn(rep, handle, + txn, takeIt, startTime, conflictSet, lockedEntrySet, + provisionallyRemovedEntrySet); + + if (available) + return handle; + } - final EntryRep rep = handle.rep(); - - if (!tmpl.matches(rep)) - continue; - - final boolean available = - confirmAvailabilityWithTxn(rep, handle, txn, - takeIt, startTime, conflictSet, lockedEntrySet, - provisionallyRemovedEntrySet); - - if (available) - return handle; - } - - return null; + return null; } + /** * Debug method: Dump out the state of this holder, printing out @@ -160,9 +161,7 @@ class EntryHolder implements Transaction void dump(String name) { try { System.out.println(name); - for (EntryHandle handle = (EntryHandle) contents.head(); - handle != null; - handle = (EntryHandle) handle.next()) + for (EntryHandle handle : contents) { EntryRep rep = handle.rep(); System.out.println(" " + rep + ", " + rep.entry()); @@ -495,6 +494,18 @@ class EntryHolder implements Transaction } /** + * Get the head of the contents list + * @return The head of the contents list, if it exists. + * null if the list is empty. + */ + private EntryHandle getContentsHead(){ + for(EntryHandle head : contents){ + return head; + } + return null; + } + + /** * Add new new entry to the holder. Assumes the lock * on the handle is held if there is a possibility * of concurrent access. @@ -530,7 +541,7 @@ class EntryHolder implements Transaction * exposing it to others (even though shareWith will * not change handle's state materially). */ - final EntryHandle head = (EntryHandle) contents.head(); + final EntryHandle head = getContentsHead(); if (head != null && head != handle) rep.shareWith(head.rep()); @@ -547,7 +558,7 @@ class EntryHolder implements Transaction * empty. */ String[] supertypes() { - final EntryHandle head = (EntryHandle)contents.head(); + final EntryHandle head = getContentsHead(); if (head == null) return null; return head.rep().superclasses(); @@ -565,56 +576,36 @@ class EntryHolder implements Transaction * The class that implements <code>RepEnum</code> for this class. */ private class SimpleRepEnum implements RepEnum { - /** The last node we saw */ - private EntryHandle handle; + private Iterator<EntryHandle> contentsIterator; private TransactableMgr mgr; private long startTime; - int count = 0; - - SimpleRepEnum(TransactableMgr mgr) { - this.mgr = mgr; - handle = (EntryHandle) contents.head(); - startTime = System.currentTimeMillis(); - } - - // inherit doc comment from superclass - public EntryRep nextRep() { - iteratorLogger.entering("SimpleRepEnum", "nextRep"); - - /* Since we might be accessing our iteration from a - * different thread than the one that began the traversal, - * we need to inform the FastList that this thread should - * be allowed to assume that traversal without throwing - * an exception. FastList must therefore update its data - * structures to accommodate the traversal from this new - * thread. [It would be better if did this once per a thread...] - */ - - if (handle != null) - handle.restart(); - /* Skip over handles which are either removed or unable - * to perform a READ operation. - */ - - while (handle != null && - (!handle.canPerform(mgr, TransactableMgr.READ) || - isExpired(startTime, handle) || - handle.removed() - )) - { - handle = (EntryHandle) handle.next(); - iteratorLogger.log(Level.FINEST, - "advanced current handle to {0}", handle); - } + SimpleRepEnum(TransactableMgr mgr) { + this.mgr = mgr; + startTime = System.currentTimeMillis(); + contentsIterator = contents.iterator(); + } + + // inherit doc comment from superclass + public EntryRep nextRep() { + iteratorLogger.entering("SimpleRepEnum", "nextRep"); + while (contentsIterator.hasNext()) { + EntryHandle handle = contentsIterator.next(); + iteratorLogger.log(Level.FINEST, + "advanced current handle to {0}", handle); + /* + * Skip over handles which are either removed or unable to + * perform a READ operation. + */ + if (handle.canPerform(mgr, TransactableMgr.READ) + && !isExpired(startTime, handle) && !handle.removed()) { + return handle.rep(); + } - if (handle == null) - return null; + } - EntryHandle h = handle; - handle = (EntryHandle) h.next(); - return h.rep(); - } + return null; + } } @@ -661,7 +652,7 @@ class EntryHolder implements Transaction final private boolean takeThem; /** <code>EntryHandleTmplDesc</code> for the templates */ - final private EntryHandleTmplDesc[] descs; + private EntryHandleTmplDesc[] descs; /** Time used to weed out expired entries, ok if old */ @@ -671,7 +662,7 @@ class EntryHolder implements Transaction * Current position in parent <code>EntryHolder</code>'s * <code>contents</code> */ - private EntryHandle handle; + private Iterator<EntryHandle> contentsIterator; /** * Create a new <code>ContinuingQuery</code> object. @@ -695,19 +686,7 @@ class EntryHolder implements Transaction this.txn = txn; this.takeThem = takeThem; this.now = now; - handle = (EntryHandle)contents.head(); - - if (handle == null) { - // nothing to search - first next will return null - descs = null; // keep compiler happy - return; - } - - descs = new EntryHandleTmplDesc[tmpls.length]; - for (int i=0; i<tmpls.length; i++) { - descs[i] = EntryHandle.descFor(this.tmpls[i], - handle.rep().numFields()); - } + contentsIterator = contents.iterator(); } /** @@ -722,11 +701,10 @@ class EntryHolder implements Transaction * expired entries, ok if old */ void restart(long now) { - if (handle == null) + if (!contentsIterator.hasNext()) return; this.now = now; - handle.restart(); } /** @@ -772,11 +750,19 @@ class EntryHolder implements Transaction throws CannotJoinException { matchingLogger.entering("ContinuingQuery", "next"); - if (handle == null) - return null; // done - for (; handle != null; handle = (EntryHandle)handle.next()) { - if (handleMatch()) { + + while (contentsIterator.hasNext()) { + EntryHandle handle = contentsIterator.next(); + if(descs == null){ + // first time + descs = new EntryHandleTmplDesc[tmpls.length]; + for (int i=0; i<tmpls.length; i++) { + descs[i] = EntryHandle.descFor(tmpls[i], + handle.rep().numFields()); + } + } + if (handleMatch(handle)) { final boolean available = confirmAvailabilityWithTxn(handle.rep(), handle, txn, @@ -784,10 +770,7 @@ class EntryHolder implements Transaction provisionallyRemovedEntrySet); if (available) { - // Don't want to return this guy twice. - final EntryHandle rslt = handle; - handle = (EntryHandle)handle.next(); - return rslt; + return handle; } } } @@ -799,7 +782,7 @@ class EntryHolder implements Transaction * Returns <code>true</code> if handle has not been removed * and matches one or more of the templates */ - private boolean handleMatch() { + private boolean handleMatch(EntryHandle handle) { if (handle.removed()) return false; @@ -868,10 +851,7 @@ class EntryHolder implements Transaction // removed ("reaped") from the collection. long now = System.currentTimeMillis(); - for (EntryHandle handle = (EntryHandle) contents.head(); - handle != null; - handle = (EntryHandle) handle.next()) - { + for(EntryHandle handle : contents){ // Don't try to remove things twice if (handle.removed()) { continue; Modified: incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/FastList.java URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/FastList.java?rev=1063132&r1=1063131&r2=1063132&view=diff ============================================================================== --- incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/FastList.java (original) +++ incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/FastList.java Tue Jan 25 04:03:48 2011 @@ -1,929 +1,188 @@ -/* - * 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 com.sun.jini.outrigger; -import java.util.Map; -import java.util.WeakHashMap; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicLong; + /** - * This class provides a low-synchronization list of objects. It is - * designed to allow proper traversal of the list while objects are - * being added and/or removed, and to allow simultaneous adds and - * removals. Traversals must be done in a single thread; no fair handing - * off a <code>Node</code> pointer to another thread to continue the - * traversal! A single thread cannot perform two or more simultaneous - * traversals, even if traversing different lists. - * <p> - * All this means that users of the list will occasionally see a - * removed node in the list, because it may be marked removed after it - * has been reached while traversing the list. This will be rare, but - * it is up to the user of the list to skip over nodes that have been - * removed if necessary, and to handle or avoid attempted re-removal of - * a node. This is why <code>remove</code> returns a boolean -- if the - * node has already been removed by the time the attempt to remove it - * happens, <code>remove</code> will return <code>false</code>. This - * is done synchronizing on the node itself. A typical piece of code - * to remove would look like this: - * - * <pre> - * public synchronized Value findMatch(Value matchFor) { - * for (Value val = list.head(); val != null; val = val.next()) { - * if (matchFor.matches(val)) { - * if (!list.remove(n)) // oh, well -- someone got to it first - * continue; // keep looking - * return val; // it's ours to take! - * } - * } - * return null; - * } - * </pre> - * - * This code assumes that getting a removed node will be rare enough - * that there is no point in testing for it until a match is found. - * If testing for a match was expensive, one might - * <code>synchronize</code> on the node object and test it for being - * removed and then try for matching, thus preventing any other - * thread from attempting a match at the same time. In any case, the - * <code>remove</code> test is itself atomic -- either it succeeds, or - * it fails because someone got there first. - * - * <p> - * <i>The Java(TM) Language Specification<i> details a memory model based on - * local and global memories, apparently intended as a model for - * multiprocessor caches. The specified memory model should be - * supplemented by William Pugh's - * <a href="http://www.cs.umd.edu/~pugh/java/memoryModel/">work</a> - * on the Java - * programming language memory model and JSR 133. - * <p> - * In particular, the <code>volatile</code> keyword offers no practical - * guarantees. The lock and unlock operations underlying the - * <code>synchronized</code> language construct are the primary mechanisms - * for communicating between threads. A lock operation forces stale values - * to be flushed from the locking thread's cache: any values subsequently - * read by the thread are either read from main memory or are cached values - * no older than the time the lock was acquired. An unlock operation forces - * dirty cache entries to be written to main memory: any values written by - * the thread up to then must be force-written to main memory before the - * unlock can complete. - * <p> - * In every other respect, access to shared memory by threads is - * arbitrary. Updates can happen at any time and in any order. In - * particular, if one thread initializes a new Node and adds it to the end - * of the list, it's possible for a different thread to see the pointer to - * the new Node but read uninitialized (stale) values for the fields of the - * new Node (including, ominously, the instance's <code>vptr</code>), - * because writes can be seen in a different order by another thread. Even - * on a uniprocessor, compiler optimizations can cause similar surprises. - * <p> - * The removal of a node is decoupled from the unlinking of that node from - * the list. The <code>next</code> method unlinks some nodes that have been - * removed; with reaping, the number of nodes which remain in a list for a - * long time will be less than the number of threads in the VM. - * <p> - * No node can be a member of more than one list. Nodes which have been - * added to a list can never be added to another list, even if they are - * removed from the first list. - * <p> - * Never synchronize on an instance of this class - it could lead to - * deadlock. (See the paragraph on locking order below for explanation.) - * <p> - * You can synchronize on an instance of a <code>Node</code> subclass - * provided that the synchronizing thread does not already hold a lock on - * another <code>Node</code> instance taken from the same - * <code>FastList</code>. Note, many of the methods on <code>FastList</code> - * synchronize on <code>Node</code>s in the - * list, thus you should not call one of these methods if you - * already hold a lock on a <code>Node</code> in the list. Similarly - * for methods on <code>Node</code>. - * <p> - * The remainder of this comment discusses the internal structure - * and assumptions of this class. - * <p> - * The design is to have a list of <code>FastList.Node</code> nodes, - * each of which has a <code>next</code> reference, a <code>removed</code> - * flag, and a <code>guardSet</code> (indicating which traversals - * depend on a future synchronization on that node). - * <p> - * There is a special sublist which is important to assuring the integrity - * of this data structure. The chain of nodes starting at <code>head</code> - * and continuing to the <code>tail</code> via <code>Node.next</code> - * references is called the <dfn>main sequence</dfn>. The fundamental - * constraints on the main sequence are: <ul> - * <li> The <code>tail</code> is always reachable from the <code>head</code>. - * <li> Every <code>Node</code> for which <code>removed==false</code> is - * an element of the main sequence. - * <li> Every <code>Node</code> for which - * <code>guardSet.isEmpty()==false</code> is an element of the main sequence - * (regardless of <code>removed</code>). - * <li> <code>tail.next==null</code> whenever <code>tail != null</code>. - * <li> <code>tail==null</code> if and only if <code>head==null</code>. - * </ul> - * <p> - * Note that removed non-guard nodes can still be in the main sequence: - * they do not have to be unlinked immediately. - * <p> - * We say <q><var>Y</var> is <dfn>reachable</dfn> from <var>X</var></q> - * when <var>X</var> equals <var>Y</var> or when <var>Y</var> is - * reachable from <code><var>X</var>.next</code>. - * <p> - * New nodes are added at the tail, requiring synchronization on the list. - * A node is said to be <dfn>removed<dfn> if its <code>removed</code> - * field is <code>true</code>. Such a node may still exist and be - * part of the main sequence. Removed nodes will eventually be unlinked - * from their neighbors, if the <code>reap</code> method is called - * occasionally. - * <p> - * Any element can become removed (by the <code>remove</code> method), - * but once that happens, that element can never revert to being unremoved. - * This means that the <code>removed</code> field can be read without - * synchronization: a <code>true</code> value indicates that the node has - * been removed; a <code>false</code> value is inconclusive. - * This in turn means that a removed element cannot be added to a - * <code>FastList</code>, because some other threads may read a stale cached - * value for its <code>removed</code> field. (It may be possible, one day, - * to liberate removed nodes from this restriction, given the guarantees of - * guarded traversal.) - * <p> - * The list can be traversed without much synchronization. - * When a traversal begins, a node near the end of the main sequence is - * specially marked (<i>guarded</i>) in order that the traversal will - * stop and synchronize when it reaches that node. When the guard node is - * reached, and there are more nodes in the list to traverse, the - * procedure is repeated: the (new) last node in the list - * is marked, and traversal continues. This procedure ends when the - * traversal reaches its guard node but finds out that it is still the - * last node in the list. In this way, much of the list can be traversed - * without synchronization. - * <p> - * It is possible for a traversal to find itself on an orphaned branch - * (one from which the <code>tail</code> is not reachable). This can - * happen when the <code>restart</code> method is called by a different - * thread. Even a node on an orphaned branch is still useful for - * traversal: every unremoved node which was inserted after the orphaned - * node but before the traversal originally began is still reachable - * from the orphaned node. - * <p> - * Because every traversal has a guard, a list with at least - * one node can not become empty by traversal alone, even if the node - * is removed. The <code>reap</code> method can remove such nodes. - * <p> - * Main-sequence nodes are stored in FIFO order. - * <p> - * Synchronized locks are acquired in accordance with the following - * partial order, in order to avoid deadlocks. - * <ul><li>Earlier nodes precede later nodes (i.e. where a thread holds - * the lock on a node <var>X</var>, it must not wait for a lock on any - * node <var>Y</var> unless <var>Y</var> is reachable from - * <var>X</var>).</li> - * <li>Every node precedes the list (i.e. where a thread holds the lock on - * the <code>FastList</code> itself, it must not wait for a lock on any node - * which has been added to that list).</li></ul> - * <p> - * Each node contain a field (<code>guardSet</code>) which - * indicates how many different threads are using the node as a guard node. - * If the field's value is <code>null</code>, the number is zero and the node - * is not a guard node. If the field's value is nonnull, the value is a - * <code>WeakHashMap</code> whose <code>keySet</code> is the set of threads - * using the node as a guard node. If that <code>keySet</code> is empty, - * the node is not a guard node. - * <p> - * There is a global table (actually a ThreadLocal) called - * <code>DynamicGuard</code> which is used to - * retrieve the guard node for the current thread. The number of entries - * in a guard node's <code>guardSet</code> generally corresponds to the - * number of times that node appears in the global table (the - * <code>guardSet</code> size temporarily becomes greater - * than the number of table entries, but it can never be less). - * The list need not be synchronized while the global table is being accessed. - * <p> - * Unsynchronized access may be made to the <code>guardSet</code>, - * <code>removed</code>, and <code>next</code> fields. In the case of - * <code>next</code>, the field may be modified so as to unlink any removed - * non-guard nodes. This action does not invalidate the main-sequence - * invariant, and can only be done by a thread which is holding a lock on - * the node being unlinked. - * <p> - * In the case of <code>guardSet</code> and <code>removed</code>, the fields - * are read as an optimization. If the values read from the fields might - * be stale, then synchronization is done and the old values are not - * relied on. The value of <code>guardSet</code> is read without - * synchronization to see if it's null or not, as part of a check to detect - * the traversing thread's guard node. The unsynchronized value of - * <code>removed</code> is untrusted when it's <code>false</code>; however, - * a <code>true</code> value is trustworthy, since such a value could only - * be read after the node is irrevocably marked removed. - * - * @author Sun Microsystems, Inc. - * + * Simplified list for rapid append, removal, and scanning from multiple + * threads. It is intended as a substitute for the previous FastList, but uses + * an Iterator rather than limiting a thread to a single scan at a time. + * + * This version is completely rewritten, based on + * java.util.concurrent.ConcurrentLinkedQueue. + * + * @param <T> Node type, required to extend FastList.Node so that the + * FastList can keep working data for each Node without using mapping or extra + * data structures. */ +class FastList<T extends FastList.Node> implements Iterable<T> { -class FastList { - - /** - * A single node in a <code>FastList</code>. - */ - static abstract class Node { - /** The next youngest Node in the list. - * If this node is the last in the list, this field is null. - * Otherwise, it's a pointer to the next Node that was added - * to the list (and not unlinked in the meantime). - * <p> - * This field may be changed only while synchronized on - * <code>this</code>, or when unlinking a node from the middle - * of the list. This field may be read without - * synchronization if the reading thread has a guard node which - * is not <code>this</code>. - */ - private volatile Node next; - - /** True only if this node has been removed. - * Every node starts out unremoved. Once this field becomes - * true, it can never become false again. You must be - * synchronized on this before you can modify this field. - * You can read this field without synchronization if you don't - * mind reading an old <code>false</code> instead of a more - * recent <code>true</code>. - */ - private boolean removed; - - /** The list containing this node. - * Every node in a list has the same value for this field. - * This field must not be changed after the node is added to - * a list. The field can be read without synchronization - * provided that the reading thread has first synchronized on - * something since <code>this</code> node was added to its list - * (that is, during a traversal). - */ - private FastList list; - - /** Set of threads for which this is a guard. - * This is a weak map from Thread to nulls, and the key set is - * the important set of threads. The values are unimportant. - * You must be synchronized on this node to access the map. - * This field may be <code>null</code>, which is semantically - * equivalent to the empty set. - * <p> - * This field is read without synchronization in <code>next</code>. - * That method is searching for a particular node where the value - * of this field is known to be continuously nonnull since the last - * synchronization by the reading thread, so it is safe. - * <p> - * Note to maintainer: it may be better to use a reference count - * and rely on the user to use a finally-clause to effect the - * necessary decrement. In that case, head() should verify that - * any previous traversal has been properly terminated. - */ - private transient volatile WeakHashMap guardSet; - - /** Default constructor. This is called by subclasses. */ - protected Node() { - this.removed = false; - this.guardSet = null; - this.list = null; // will be filled in by FastList.add() - this.next = null; - } /** - * Traverse to the next node. Note that between the time that the - * next node is returned and the time you look at it someone may - * have removed it. Returns <code>null</code> if there are no more - * nodes. - * <p> - * Although this method attempts to skip removed nodes, callers - * <em>must not</em> assume that removed nodes have been skipped. - * Use the {@link #removed() removed} method to tell when a node - * returned by this method is in fact removed, and - * <code>synchronize</code> on the node if you want to prevent - * anyone else from removing the node while you are in the - * synchronized block. - * <p> - * Do not call this method twice on the same node unless each call is - * part of a separate traversal; a traversal starts with a call to - * <code>List.head</code>). - * <p> - * Note this method should not be called by threads that - * hold locks on <code>Node</code>s that appear in the list - * after this <code>Node</code>. - */ - public Node next() { - - this.list.checkPanic(); // abort if malfunctioned - - // Ensure the list has a guard and it is for THIS list - Node traversalGuard = (Node)FastList.DynamicGuard.get(); - if (traversalGuard == null) - this.list.panic("Unguarded FastList traversal - 345"); - else if (traversalGuard.list != this.list) - this.list.panic("Illegal traversal of 2 FastLists"); - - /* When this thread last synchronized, Node.guardSet on - * the thread's guard node was nonempty because the - * traversing thread was added to guardSet by newGuardFor. - * Since then, it cannot have been emptied, and therefore - * cannot have been replaced with null. Therefore the - * guard node must appear to have a nonnull Node.guardSet - * field, even if accessed without locking. This means we - * can safely conclude, without locking, that a Node whose - * guardSet field is null is not the guard node for the - * current thread. - */ - if (this.guardSet != null) { - /* This may or may not be the guard node; lock it - * so we can be sure that reading `next' is safe: + * The type parameter for the FastList must extend this type, and + * all nodes added to the list are of that parameter type. + * + * A node can be added to a list at most once. Any attempt to add it again + * will result in an IllegalStateException. + * + */ + static class Node { + /** + * True if this node has been removed from its list. */ - synchronized (this) { - if (this.guardSet != null && traversalGuard == this) { - /* This is the guard node for the current - * thread; drop it and return null. - * (We don't need to find nodes added - * after the traversal was started) - */ - FastList.DynamicGuard.set(null); - traversalGuard.unguard(Thread.currentThread()); - return null; - - /* If we did want nodes that were added - * since the traversal started we - * want to try and get a new guard node - * with code like this : - * - * if (this.next != null) { - * if (!this.list.newGuardFor(this)) { - * // If it returned false, newGuardFor() - * // has already cleaned up--we just return - * return null; // traversal is over - * } else { - * // We can extend the traversal : proceed! - * } - * } else { - * // We're at the end of the list. Clean up. - * FastList.DynamicGuard.set(null); - * traversalGuard.unguard( - * Thread.currentThread()); - * return null; - * } - */ - } - } - } - - // At this point, there is a guard node reachable from this.next. - - /* This loop repeatedly looks at this.next to see if there's a - * possibility of unlinking the next node. It stops at the - * first sign of trouble (a guard node, an unremoved node, or - * a concurrency conflict): - */ - Node n = this.next; - while (true) { - if (n == null) { - // We're on an orphaned branch. Remove our guard. - traversalGuard = (Node)FastList.DynamicGuard.get(); - if (traversalGuard != null) { - FastList.DynamicGuard.set(null); - traversalGuard.unguard(Thread.currentThread()); - } - return null; - } - - // We can only unlink a removed node which is not a guard: - if (n.guardSet != null || !n.removed) - break; - - /* Lock n and see if we can find an excuse to unlink it. - * While n is locked, no other thread can change this.next. + private volatile boolean removed; + /** + * This node does not need to be shown to scans + * with index greater than or equal to this index. */ - synchronized (n) { - // Break if this.next changed. - if (this.next != n) - break; - // Verify that n is not a guard: - if (n.guardSet != null && !n.guardSet.isEmpty()) { - break; - } - // reduce empty set to null - n.guardSet = null; - } - /* n is removed and not a guard, so it's a candidate - * for removal. If we got this far, then we have a - * guard node. Therefore, n is not the last node in - * the main sequence (although it may be the last node - * on an orphaned branch). + private volatile long index; + @SuppressWarnings("unchecked") + private FastList list; + + + /** + * Remove this node from its list. + * + * @return true if this node has never previously been removed false if + * it has already been removed. */ - this.next = n = n.next; - } - - return n; - } - - /** - * Resume a traversal abandoned by another thread. - * This is used by remote iterators. When a traversal was - * started (and abandoned) by thread A, and a thread B wants - * to continue the traversal from where A left off, then B - * must call this method on the most recent <code>Node</code> - * traversed by thread A. After this method returns normally, - * B can call the <code>next</code> method on the same - * <code>Node</code> to continue the traversal. - * <p> - * Note this method should not be called by threads that - * hold locks on <code>Node</code>s that appear in the list - * after this <code>Node</code>. - */ - public synchronized void restart() { + private synchronized boolean remove() { + if (removed) { + return false; + } + removed = true; + return true; + } - this.list.checkPanic(); // abort if malfunctioned. + @SuppressWarnings("unchecked") + synchronized void markOnList(FastList list) { + this.list = list; + } - /* If the current thread still has a guard from an earlier - * abandoned traversal, then clean up that guard. - */ - Node oldguard = (Node)FastList.DynamicGuard.get(); - if (oldguard != null) { - FastList.DynamicGuard.set(null); - oldguard.unguard(Thread.currentThread()); - } - - /* There must be a synchronized block here before calling - * newGuardFor, because newGuardFor reads FastList.tail - * without synchronization. That's why this whole method - * is synchronized. - */ - - /* Attempt to get a guard. If newGuardFor returns false, no - * guard has been allocated, but that isn't a problem because - * it also means that this node is on an orphaned branch where - * a guard node is not necessary. - */ - this.list.newGuardFor(null); + /** + * Report whether the node has been removed. If the result is true the + * node has definitely been removed. If it is false, the node may still + * have been removed. To get a fully reliable result, synchronize on the + * node. + */ + public boolean removed() { + return removed; + } } - /** - * Return <code>true</code> if the object has been removed - * from the list. If called while synchronized on - * <code>this</code>, this will return true if the object has - * been removed and false otherwise. - */ - public boolean removed() { - - this.list.checkPanic(); // abort if malfunctioned. - return removed; + interface FastListIterator<T> extends Iterator<T> { + boolean lastRemoveSucceeded(); } - /** - * Remove the node from the list. This will be unsuccessful if the - * node was removed by someone else after you got your hands on - * it. This must be synchronized because the test to check if it - * is already removed and the following decision to remove it if it - * hasn't been removed must be unitary. - */ - private boolean remove() { - synchronized (this) { - if (removed) - return false; // beaten to it + private class FastListIteratorImpl implements Iterator<T> { + private T removable; + private T next; + private final long index; + private final Iterator<T> baseIterator; - removed = true; // mark first, unlink later - } + private FastListIteratorImpl(long index) { + this.index = index; + baseIterator = baseQueue.iterator(); + next = getNext(); + } - this.list.reapable(); - return true; - } + public boolean hasNext() { + return next != null; + } - /** Remove a thread from guardSet. The indicated thread is - * removed from the guardSet of the node, and the guardSet is - * reduced to <code>null</code> if it's empty. - * @param traverser the Thread to use as a key - */ - private synchronized void unguard(Thread traverser) { - if (this.guardSet == null) - return; - - this.guardSet.remove(traverser); - if (this.guardSet.isEmpty()) - this.guardSet = null; - } + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } - final Node dump(java.io.PrintWriter out) { //DEBUG - if (this.removed) //DEBUG - out.print('!'); //DEBUG - out.print(toString()); //DEBUG - Map g = this.guardSet; //DEBUG - if (g != null) //DEBUG - out.print("[g=" + g.size() + "]"); //DEBUG - return this.next; //DEBUG - } //DEBUG - } // end class Node - - /** The guard node for the calling thread's traversal, if any. - * The value is <tt>null</tt> for a thread which is not traversing - * anything, and is a <code>Node</code> for other threads. No - * thread can be involved in two concurrent traversals. This field - * can be accessed without synchronization since it's thread-local. - */ - private static final ThreadLocal DynamicGuard = new ThreadLocal(); - - /** Flag to indicate that removed elements exist in the list and - * that reaping is not necessarily a waste of time. This is set - * in <code>remove</code> and cleared in <code>reap</code>. - */ - private boolean reapable; - - /** The pointer to the last element in the list. */ - private Node tail; // synchronize on list to read or write - - /** The pointer to the first element in the list. */ - private Node head; // synchronize on list to read or write - - /** An Error that indicated a fundamental malfunction in the list. */ - private transient Error firstPanic = null; - - /** - * Create an new empty list. - */ - public FastList() { - this.reapable = false; - this.tail = null; - this.head = null; - this.firstPanic = null; - } - - /** - * Add a given node to the end of the list. Note, this method - * generally attempts to lock <code>Node</code>s in the list and - * should not be called from a thread that holds a lock on a node - * already in the list - it is ok if the calling thread holds the - * lock on <code>node</code>. - */ - public void add(Node node) { - - this.checkPanic(); // abort if malfunctioned. - if (node.removed) { - throw new IllegalArgumentException( - "FastList.add cannot accept a removed node"); - } - if (node.list != null) { - throw new IllegalArgumentException( - "FastList.add cannot accept a node from another FastList"); - } + removable = next; + next = getNext(); + return removable; + } - node.list = this; + public void remove() { + if (removable != null) { + removable.remove(); + removable = null; + } else { + throw new IllegalStateException(); + } + } - while (true) { - Node t = this.tail; - if (t == null) { - // List appears to be empty - lock list and insert first node - synchronized (this) { - if (this.tail != null) { - //System.err.print('$'); //DBG - continue; // try again - } - this.tail = node; - this.head = node; - } - } else { - /* List is not empty - lock tail & list and insert the - * node at the end. + /** + * Find the next eligible node, null if there is none. + * skip over removed nodes, and + * @return */ - synchronized (t) { - if (t != this.tail) { // optimization - //System.err.print('#'); //DBG - continue; // try again - } - synchronized (this) { - if (t != this.tail) { - //System.err.print('&'); //DBG - continue; // try again + private T getNext(){ + T node = null; + while(baseIterator.hasNext()){ + node = baseIterator.next(); + if(node.removed()){ + /* Tell the base list to drop it */ + baseIterator.remove(); + } + if(node.index >= index){ + /* Seen all the relevant nodes. */ + node = null; + break; + } + if(!node.removed()){ + /* Got a keeper. */ + break; + } } - t.next = node; - this.tail = node; - } + return node; } - } - return; } - } - /** - * Allocate a new guard node for a traversing thread. - * This method selects a guard node on the main sequence, as close as - * practically possible to the tail. Returns true iff a guard was - * allocated, and false if the list appeared to contain no elements - * after the oldGuard. This routine must acquire a new guard at - * the same time as or before releasing the old one, in order to - * guarantee that the new guard will be reachable from the old. - * <p> - * Because this method reads <code>FastList.tail</code> without - * synchronization, the guard could be stale. It's important that - * it not be too stale, or else nodes added before the traversal - * began might not appear in the traversal. For this reason, the - * caller must have recently acquired a lock (i.e. synchronized on - * any object). The guard allocated by this method will have been the - * tail at least once since that lock was acquired. - * <p> - * Note: the oldGuard will always be dropped as the guard node for - * this thread. Further, should this method return <code>false</code> - * the DynamicGuard will be set to <code>null</code>. - */ - private boolean newGuardFor(Node oldGuard) { - - /* tGood becomes true when it's determined that a given node - * is good enough to be a guard node. "Good enough" means - * that it's reachable from the head, and that it was the tail - * at least once since this method was entered. - */ - Thread current = Thread.currentThread(); + private final AtomicLong index = new AtomicLong(Long.MIN_VALUE); + private final Queue<T> baseQueue = new ConcurrentLinkedQueue<T>(); - while (true) { - boolean tGood = false; // is tGood a useable guard? - boolean noMoreElements = false; // is oldGuard the tail node? - Node t = this.tail; - - /* A new guard is not necessary for an empty list. - * Further, an empty list has no guard nodes (or indeed - * any nodes) so there is no need to call unguard() on - * anything -- even the parameter "oldGuard". - */ - if (t == null) - return false; - - /* Optimistically presume that t will be good enough, and add - * to its guardSet. If the optimism isn't justified, then the - * addition will be undone later. If t is on the main sequence - * and after oldGuard, then it's OK for use as a new guard. - * This is because traversals must be guaranteed to hit the - * guard node, and the easiest way to do that is to ensure - * that the guard node is on the main sequence. - */ - synchronized (t) { - if ( (t.guardSet != null && !t.guardSet.isEmpty()) - || !t.removed()) - { - if (t != oldGuard) // can't use same guard again - tGood = true; - } - // Make sure t can't be unlinked after we release the lock. - if (t.guardSet == null) - t.guardSet = new WeakHashMap(); - t.guardSet.put(current, null); - } - - /* If t isn't good enough, then we have to try the real tail. If - * the tail isn't good enough, then we're at the end of the list, - * and no guard is required. - */ - if (!tGood) { - synchronized (this) { - if (this.tail == t) { - if (t != oldGuard) { // t is reachable from oldGuard - tGood = true; - } else { // oldGuard == this.tail - noMoreElements = true; + /** + * Add a node to the tail of the list. + * @param node Each node can only be added once, regardless of removal. + * @throws IllegalArgumentException The node has been added to a list previously. + */ + public void add(T node) { + synchronized (node) { + if (node.list == null) { + node.list = this; + } else { + throw new IllegalArgumentException("Attempt to reuse node " + + node); } - } } - } - - if (tGood) { - DynamicGuard.set(t); - if (oldGuard != null) - oldGuard.unguard(current); - return true; - } - - //System.err.print('*'); //DBG - - // Undo the optimistic work if it turns out t isn't good enough. - t.unguard(current); - - if (noMoreElements) { - DynamicGuard.set(null); - if (oldGuard != null && oldGuard != t) - oldGuard.unguard(current); - return false; - } + node.index = index.getAndIncrement(); + baseQueue.add(node); } - } - - /** - * Return the start of the list. Returns null if the list is - * empty. Note, this method generally attempts to lock - * <code>Node</code>s in the list and should not be called from a - * thread that holds a lock on a node already in the list. - */ - public Node head() { - - this.checkPanic(); // Abort if malfunctioned. - /* If the current thread still has a guard from an earlier abandoned - * traversal, then clean up that guard. - */ - Node oldguard = (Node)DynamicGuard.get(); - if (oldguard != null) { - DynamicGuard.set(null); - oldguard.unguard(Thread.currentThread()); - } - - Node h; - Node t; - - synchronized (this) { - h = this.head; - t = this.tail; - if (h == null) - return null; // nothing in list + + public boolean remove(T node){ + if(node.list != this){ + throw new IllegalArgumentException("Cannot remove a node from a list it is not on"); + } + return node.remove(); } - // There must be a synchronized block here before calling - // newGuardFor, because newGuardFor reads FastList.tail - // without synchronization. - - // Every traversal needs a guard. - if (!newGuardFor(null)) - return null; // nothing in list, suddenly - - // Make an attempt to unlink removed non-guard nodes from the head - while (h.removed()) { - synchronized (h) { - /* - * We stop if we find any node that is a guard node. Of - * course, eventually we'll reach the node which is OUR - * guard node and at that point we'll break from this loop. - */ - if (h.guardSet != null && !h.guardSet.isEmpty()) - break; // can't unlink guard nodes - - synchronized (this) { - if (h != this.head) - return this.head; // someone else just did it - this.head = h = h.next; + public void reap() { + Iterator<T> it = baseQueue.iterator(); + while(it.hasNext()){ + T node = it.next(); + if(node.removed()){ + it.remove(); + } } - } - } - - return h; - } - - /** - * Remove the given node from this list. - */ - public boolean remove(Node node) { - - this.checkPanic(); // Abort if malfunctioned. - return node.remove(); - } - - /** Remember that there's something for the reaper to do. - */ - private void reapable() { - this.reapable = true; - } - - /** - * Perform a normal traversal to help clear out some removed - * nodes. It also moves the tail. Note, this method generally - * attempts to lock <code>Node</code>s in the list and should not - * be called from a thread that holds a lock on a node already in - * the list. - */ - public void reap() { - - if (!this.reapable) - return; - - this.reapable = false; - // reapable could be concurrently set to true, which is OK. - - reapCnt++; - - // Normal traversal (unlinks some nodes as a side effect). - Node last = null; - Node secondLast = null; - for (Node n = this.head(); n != null; n = n.next()) { - secondLast = last; - last = n; } - - // Clear out the last node only if it's removed and not a guard - if (last == null || !last.removed || last.guardSet != null) - return; - if (secondLast == null) { - // probably the only one in the list - synchronized (last) { - if (last.next != null) - return; // changed while waiting - if (last.guardSet != null && !last.guardSet.isEmpty()) - return; // last became a guard while we waited - synchronized (this) { - if (last != this.tail || last != this.head) - return; // a node was added while we waited - // remove the only node from the list - this.head = this.tail = null; - } - } - } else { - /* Move the tail to secondLast. We can only do this if - * secondLast is on the main sequence and last is both - * removed and not a guard. - */ - if (secondLast.removed && secondLast.guardSet == null) - return; // not sure secondLast is on main sequence - synchronized (secondLast) { - if (secondLast.removed) { - if (secondLast.guardSet == null) - return; // not sure secondLast is on main sequence - if (secondLast.guardSet.isEmpty()) { - secondLast.guardSet = null; - return; // ditto - } - } - // We're sure now that secondLast is on the main sequence. - synchronized (last) { - if (secondLast.next != last) - return; // last has already been unlinked - if (last.next != null) - return; // a node was added while we waited - if (last.guardSet != null && !last.guardSet.isEmpty()) - return; // last is a guard - // We tested last.removed and it must still be true now. - synchronized (this) { - if (last != this.tail) - return; // shouldn't happen - // Unlink last (secondLast is the new tail): - secondLast.next = null; - this.tail = secondLast; - } - } - } - } - } // end reap - - int reapCnt = 0; //DEBUG - public final int reapCnt() { return reapCnt; } //DEBUG - - - public void dump(java.io.PrintWriter out) { //DEBUG - Node n = this.head; //DEBUG - while (n != null) { //DEBUG - if (this.tail == n) //DEBUG - out.print("[tail]"); //DEBUG - n = n.dump(out); //DEBUG - out.print(' '); //DEBUG - } //DEBUG - out.print("null"); //DEBUG - } //DEBUG - - /** Shut down the list due to an error. The argument is used to - * construct an Error which is thrown. The FastList then becomes - * unusable because every public method calls checkPanic which - * then throws another exception. - * - * @param condition identifying string which will help debugging - */ - private void panic(String condition) - throws Error - { - InternalError err = new InternalError(condition); - - // Record only the first panic so that subsequent ones don't confuse: - if (firstPanic == null) { - synchronized (this) { - if (firstPanic == null) - firstPanic = err; - } + public Iterator<T> iterator(){ + return new FastListIteratorImpl(index.get()); } - throw err; - } - - /** Abort if the list has been shut down. This does nothing in - * normal operation. If the list has undergone a panic, then this - * throws an exception which says so. - */ - private void checkPanic() - throws Error - { - if (firstPanic == null) - return; - throw new InternalError("FastList panicked; original error was:" + - firstPanic); - } - } Modified: incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/WatchersForTemplateClass.java URL: http://svn.apache.org/viewvc/incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/WatchersForTemplateClass.java?rev=1063132&r1=1063131&r2=1063132&view=diff ============================================================================== --- incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/WatchersForTemplateClass.java (original) +++ incubator/river/jtsk/skunk/patsOutrigger/src/com/sun/jini/outrigger/WatchersForTemplateClass.java Tue Jan 25 04:03:48 2011 @@ -28,7 +28,7 @@ import java.util.Set; */ class WatchersForTemplateClass { /** All the templates we know about */ - private final FastList contents = new FastList(); + private final FastList<TemplateHandle> contents = new FastList<TemplateHandle>(); /** The object we are inside of */ private final TransitionWatchers owner; @@ -65,8 +65,7 @@ class WatchersForTemplateClass { * if we have more than one with the same template. It * is bad if we add the watcher to a removed handle. */ - TemplateHandle handle = (TemplateHandle)contents.head(); - for (; handle != null; handle = (TemplateHandle)handle.next()) { + for(TemplateHandle handle : contents) { if (template.equals(handle.rep())) { synchronized (handle) { if (!handle.removed()) { @@ -90,7 +89,7 @@ class WatchersForTemplateClass { * template, create one, add the watcher to it, and it * to contents. */ - handle = new TemplateHandle(template, this); + TemplateHandle handle = new TemplateHandle(template, this); /* We need the sync both to prevent concurrent modification * of handle (since we add it to contents first), and to @@ -134,9 +133,7 @@ class WatchersForTemplateClass { * the changed entry and if they do ask them to * put the appropriate watchers in the set. */ - for (TemplateHandle handle = (TemplateHandle)contents.head(); - handle != null; - handle = (TemplateHandle)handle.next()) + for (TemplateHandle handle : contents) { // See the if handle mask is incompatible EntryHandleTmplDesc desc = handle.descFor(repNumFields); @@ -171,9 +168,7 @@ class WatchersForTemplateClass { */ void reap(long now) { // First remove empty handles - for (TemplateHandle handle = (TemplateHandle)contents.head(); - handle != null; - handle = (TemplateHandle)handle.next()) + for (TemplateHandle handle : contents) { // Dump any expired watchers. handle.reap(now);