/* * 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.ArrayList; import java.util.Iterator; import java.util.List; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; /** * 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 {@link * http://www.cs.umd.edu/~pugh/java/memoryModel/ work} 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. * */ 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"); 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: */ 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. */ 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). */ 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() { this.list.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)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); } /** * 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; } /** * 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 removed = true; // mark first, unlink later } this.list.reapable(); return true; } /** 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; } 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"); } node.list = this; 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. */ 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 } t.next = node; this.tail = 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(); 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; } } } } 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; } } } /** * 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 } // 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; } } } 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; } } 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); } }