All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.stanford.ppl.concurrent.CopyOnWriteManager Maven / Gradle / Ivy

Go to download

The reference implementation of SnapTree, a concurrent AVL tree with fast cloning, snapshots, and consistent iteration.

The newest version!
/*
 * Copyright (c) 2009 Stanford University, unless otherwise specified.
 * All rights reserved.
 *
 * This software was developed by the Pervasive Parallelism Laboratory of
 * Stanford University, California, USA.
 *
 * Permission to use, copy, modify, and distribute this software in source
 * or binary form for any purpose with or without fee is hereby granted,
 * provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 *    3. Neither the name of Stanford University nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

package edu.stanford.ppl.concurrent;

import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;

/** Manages copy-on-write behavior for a concurrent tree structure.  It is
 *  assumed that the managed structure allows concurrent mutation, but that no
 *  mutating operations may be active when a copy-on-write snapshot of tree is
 *  taken.  Because it is difficult to update the size of data structure in a
 *  highly concurrent fashion, the CopyOnWriteManager also manages
 *  a running total that represents the size of the contained tree structure.
 *  

* Users should implement the {@link #freezeAndClone(Object)} and * {@link #cloneFrozen(Object)} methods. */ abstract public class CopyOnWriteManager implements Cloneable { /** This is basically a stripped-down CountDownLatch. Implementing our own * reduces the object count by one, and it gives us access to the * uninterruptable acquireShared. */ private class Latch extends AbstractQueuedSynchronizer { Latch(final boolean triggered) { setState(triggered ? 0 : 1); } public int tryAcquireShared(final int acquires) { // 1 = success, and followers may also succeed // -1 = failure return getState() == 0 ? 1 : -1; } public boolean tryReleaseShared(final int releases) { // Before, state is either 0 or 1. After, state is always 0. return compareAndSetState(1, 0); } } private static final int MUTATE = 1; private static final int MUTATE_AFTER_FREEZE = 2; private static final int BULK_READ = 3; private static final int BULK_READ_AFTER_FREEZE = 4; private class COWEpoch extends EpochNode { /** Tripped after this COWEpoch is installed as active. */ private final Latch _activated; /** True iff this is a mutating epoch. */ final boolean mutationAllowed; /** The value used by this epoch. */ E value; /** The computed size of value, as of the beginning of * this epoch. */ int initialSize; /** A frozen E equal to value, if not dirty. */ private volatile E _frozenValue; /** True if any mutations have been performed on value. */ volatile boolean dirty; /** The epoch that will follow this one, created on demand. */ final AtomicReference successorRef = new AtomicReference(null); /** A ticket on the successor, released when this epoch is closed. */ Epoch.Ticket successorTicket; /** True if the successor should freeze and clone this epoch's value. */ boolean freezeRequested; private COWEpoch(final boolean mutationAllowed) { this._activated = new Latch(false); this.mutationAllowed = mutationAllowed; } public COWEpoch(final E value, final E frozenValue, final int initialSize) { this._activated = new Latch(true); // pre-triggered this.mutationAllowed = true; this.value = value; this.initialSize = initialSize; this._frozenValue = frozenValue; this.dirty = frozenValue == null; } EpochNode attemptInitialArrive() { return super.attemptArrive(); } @Override public EpochNode attemptArrive() { final EpochNode ticket = super.attemptArrive(); if (ticket != null && !dirty) { dirty = true; _frozenValue = null; } return ticket; } private void setFrozenValue(final E v) { if (!dirty) { _frozenValue = v; if (dirty) { _frozenValue = null; } } } E getFrozenValue() { final E v = _frozenValue; return dirty ? null : v; } protected void onClosed(final int dataSum) { assert(dataSum == 0 || dirty); final COWEpoch succ = successorRef.get(); if (freezeRequested) { succ.value = freezeAndClone(value); succ.setFrozenValue(value); } else { succ.value = value; if (dirty) { succ.dirty = true; } else { succ.setFrozenValue(_frozenValue); } } succ.initialSize = initialSize + dataSum; _active = succ; successorTicket.leave(0); succ._activated.releaseShared(1); } public void awaitActivated() { _activated.acquireShared(1); } public COWEpoch getOrCreateSuccessor(final boolean preferredMutation) { final COWEpoch existing = successorRef.get(); if (existing != null) { return existing; } final COWEpoch repl = new COWEpoch(preferredMutation); if (attemptInstallSuccessor(repl)) { return repl; } return successorRef.get(); } public boolean attemptInstallSuccessor(final COWEpoch succ) { final Epoch.Ticket t = succ.attemptInitialArrive(); if (successorRef.compareAndSet(null, succ)) { successorTicket = t; beginClose(); return true; } else { return false; } } } private volatile COWEpoch _active; /** Creates a new {@link CopyOnWriteManager} holding * initialValue, with an assumed size of * initialSize. */ public CopyOnWriteManager(final E initialValue, final int initialSize) { _active = new COWEpoch(initialValue, null, initialSize); } /** The implementing method must mark value as shared, and * return a new object to use in its place. Hopefully, the majority of * the work of the clone can be deferred by copy-on-write. */ abstract protected E freezeAndClone(final E value); /** Returns a clone of a frozen E. */ abstract protected E cloneFrozen(E frozenValue); public CopyOnWriteManager clone() { final CopyOnWriteManager copy; try { copy = (CopyOnWriteManager) super.clone(); } catch (final CloneNotSupportedException xx) { throw new Error("unexpected", xx); } COWEpoch a = _active; E f = a.getFrozenValue(); while (f == null) { a.freezeRequested = true; final COWEpoch succ = a.getOrCreateSuccessor(a.mutationAllowed); succ.awaitActivated(); if (a.value != succ.value) { f = a.value; } a = succ; } copy.createNewEpoch(f, a); return copy; } private void createNewEpoch(E f, COWEpoch a) { _active = new COWEpoch(cloneFrozen(f), f, a.initialSize); } /** Returns a reference to the tree structure suitable for a read * operation. The returned structure may be mutated by operations that * have the permission of this {@link CopyOnWriteManager}, but they will * not observe changes managed by other instances. */ public E read() { return _active.value; } /** Obtains permission to mutate the copy-on-write value held by this * instance, perhaps blocking while a concurrent snapshot is being * performed. {@link Epoch.Ticket#leave} must be called exactly once on * the object returned from this method, after the mutation has been * completed. The change in size reflected by the mutation should be * passed as the parameter to leave. */ public Epoch.Ticket beginMutation() { return begin(true); } public Epoch.Ticket beginQuiescent() { return begin(false); } private Epoch.Ticket begin(final boolean mutation) { final COWEpoch active = _active; if (active.mutationAllowed == mutation) { final Epoch.Ticket ticket = active.attemptArrive(); if (ticket != null) { return ticket; } } return begin(mutation, active); } private Epoch.Ticket begin(final boolean mutation, COWEpoch epoch) { while (true) { COWEpoch succ = epoch.successorRef.get(); if (succ == null) { final COWEpoch newEpoch = new COWEpoch(mutation); final Epoch.Ticket newTicket = newEpoch.attemptArrive(); if (epoch.attemptInstallSuccessor(newEpoch)) { // can't use the ticket until the new epoch is activated newEpoch.awaitActivated(); return newTicket; } // if our CAS failed, somebody else succeeded succ = epoch.successorRef.get(); } // is the successor created by someone else suitable? if (succ.mutationAllowed == mutation) { final Epoch.Ticket ticket = succ.attemptArrive(); if (ticket != null) { succ.awaitActivated(); return ticket; } } epoch = succ; } } /** Returns a reference to the tree structure suitable for a mutating * operation. This method may only be called under the protection of a * ticket returned from {@link #beginMutation}. */ public E mutable() { return _active.value; } /** Returns a reference to a snapshot of this instance's tree structure * that may be read, but not written. This is accomplished by suspending * mutation, replacing the mutable root of this manager with the result of * freezeAndClone(root, false), and then returning a * reference to the old root. Successive calls to this method may return * the same instance. */ public E frozen() { COWEpoch a = _active; E f = a.getFrozenValue(); while (f == null) { a.freezeRequested = true; final COWEpoch succ = a.getOrCreateSuccessor(a.mutationAllowed); succ.awaitActivated(); if (a.value != succ.value) { f = a.value; } a = succ; } return f; } /** Returns a reference to a snapshot of this instance's tree structure, * if one is available without requiring any additional copying, otherwise * returns null. May be used in combination with {@link #beginQuiescent} * to perform quiescent reads with minimal cost. */ public E availableFrozen() { return _active.getFrozenValue(); } /** Returns true if the computed {@link #size} is zero. */ public boolean isEmpty() { // for a different internal implementation (such as a C-SNZI) we might // be able to do better than this return size() == 0; } /** Returns the sum of the initialSize parameter passed to the * constructor, and the size deltas passed to {@link Epoch.Ticket#leave} * for all of the mutation tickets. The result returned is linearizable * with mutations, which requires mutation to be quiesced. No tree freeze * is required, however. */ public int size() { final COWEpoch a = _active; final Integer delta = a.attemptDataSum(); if (delta != null) { return a.initialSize + delta; } // wait for an existing successor, or force one if not already in progress final COWEpoch succ = a.getOrCreateSuccessor(a.mutationAllowed); succ.awaitActivated(); return succ.initialSize; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy