jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mreut...@apache.org
Subject svn commit: r540944 - in /jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state: DefaultISMLocking.java FineGrainedISMLocking.java ISMLocking.java SharedItemStateManager.java
Date Wed, 23 May 2007 13:12:27 GMT
Author: mreutegg
Date: Wed May 23 06:12:26 2007
New Revision: 540944

URL: http://svn.apache.org/viewvc?view=rev&rev=540944
Log:
JCR-314: Fine grained locking in SharedItemStateManager

Added:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java
  (with props)
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/FineGrainedISMLocking.java
  (with props)
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ISMLocking.java
  (with props)
Modified:
    jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java?view=auto&rev=540944
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java
(added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java
Wed May 23 06:12:26 2007
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state;
+
+import org.apache.jackrabbit.core.ItemId;
+import EDU.oswego.cs.dl.util.concurrent.ReadWriteLock;
+import EDU.oswego.cs.dl.util.concurrent.ReentrantWriterPreferenceReadWriteLock;
+import EDU.oswego.cs.dl.util.concurrent.Sync;
+
+/**
+ * <code>DefaultISMLocking</code> implements the default locking strategy using
+ * coarse grained locking on an ItemStateManager wide read-write lock. E.g.
+ * while a write lock is held, no read lock can be acquired.
+ */
+public class DefaultISMLocking implements ISMLocking {
+
+    /**
+     * JCR-447: deadlock might occur when this manager is still write-locked and events are
dispatched.
+     */
+    private boolean noLockHack = false;
+
+    /**
+     * The internal read-write lock.
+     */
+    private final ReadWriteLock rwLock = new ReentrantWriterPreferenceReadWriteLock() {
+        /**
+         * Allow reader when there is no active writer, or current thread owns
+         * the write lock (reentrant).
+         */
+        protected boolean allowReader() {
+            return activeWriter_ == null
+                    || activeWriter_ == Thread.currentThread()
+                    || noLockHack;
+        }
+    };
+
+    /**
+     * enables or disables the write-lock hack.
+     *
+     * @param noLockHack
+     */
+    public void setNoLockHack(boolean noLockHack) {
+        this.noLockHack = noLockHack;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ReadLock acquireReadLock(ItemId id)
+            throws InterruptedException {
+        return new ReadLockImpl(rwLock.readLock());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public WriteLock acquireWriteLock(ChangeLog changeLog)
+            throws InterruptedException {
+        return new WriteLock() {
+
+            {
+                rwLock.writeLock().acquire();
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            public void release() {
+                rwLock.writeLock().release();
+            }
+
+            /**
+             * {@inheritDoc}
+             */
+            public ReadLock downgrade() throws InterruptedException {
+                ReadLock rLock = new ReadLockImpl(rwLock.readLock());
+                release();
+                return rLock;
+            }
+        };
+    }
+
+    private static final class ReadLockImpl implements ReadLock {
+
+        private final Sync readLock;
+
+        private ReadLockImpl(Sync readLock) throws InterruptedException {
+            this.readLock = readLock;
+            this.readLock.acquire();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void release() {
+            readLock.release();
+        }
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/DefaultISMLocking.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/FineGrainedISMLocking.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/FineGrainedISMLocking.java?view=auto&rev=540944
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/FineGrainedISMLocking.java
(added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/FineGrainedISMLocking.java
Wed May 23 06:12:26 2007
@@ -0,0 +1,393 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state;
+
+import org.apache.jackrabbit.core.ItemId;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.uuid.UUID;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Collections;
+
+import EDU.oswego.cs.dl.util.concurrent.ReadWriteLock;
+import EDU.oswego.cs.dl.util.concurrent.Sync;
+import EDU.oswego.cs.dl.util.concurrent.Latch;
+import EDU.oswego.cs.dl.util.concurrent.WriterPreferenceReadWriteLock;
+
+/**
+ * <code>FineGrainedISMLocking</code>...
+ */
+public class FineGrainedISMLocking implements ISMLocking {
+
+    /**
+     * Avoid creating commonly used Integer instances.
+     */
+    private static final Integer ONE = new Integer(1);
+
+    /**
+     * An anonymous read lock without an id assigned.
+     */
+    private final ReadLock ANONYMOUS_READ_LOCK = new ReadLockImpl();
+
+    /**
+     * The active writer or <code>null</code> if there is none.
+     */
+    private WriteLockImpl activeWriter;
+
+    private volatile Thread activeWriterThread;
+
+    private ReadWriteLock writerStateRWLock = new WriterPreferenceReadWriteLock();
+
+    /**
+     * Map that contains the read locks.
+     */
+    private final LockMap readLockMap = new LockMap();
+
+    /**
+     * List of waiting readers that are blocked because they conflict with
+     * the current writer.
+     */
+    private List waitingReaders = Collections.synchronizedList(new LinkedList());
+
+    /**
+     * List of waiting writers that are blocked because there is already a
+     * current writer or one of the current reads conflicts with the change log
+     * of the blocked writer.
+     */
+    private List waitingWriters = new LinkedList();
+
+    /**
+     * {@inheritDoc}
+     */
+    public ReadLock acquireReadLock(ItemId id)
+            throws InterruptedException {
+        if (activeWriterThread == Thread.currentThread()) {
+            // we hold the write lock
+            readLockMap.addLock(id);
+            return new ReadLockImpl(id);
+        }
+
+        // if we get here the following is true:
+        // - the current thread does not hold a write lock
+        for (;;) {
+            Sync signal;
+            // make sure writer state does not change
+            Sync shared = writerStateRWLock.readLock();
+            shared.acquire();
+            try {
+                if (activeWriter == null ||
+                        !hasDependency(activeWriter.changes, id)) {
+                    readLockMap.addLock(id);
+                    return new ReadLockImpl(id);
+                } else {
+                    signal = new Latch();
+                    waitingReaders.add(signal);
+                }
+            } finally {
+                shared.release();
+            }
+
+            // if we get here there was an active writer with
+            // a dependency to the current id.
+            // wait for the writer until it is done, then try again
+            signal.acquire();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public WriteLock acquireWriteLock(ChangeLog changeLog)
+            throws InterruptedException {
+        for (;;) {
+            Sync signal;
+            // we want to become the current writer
+            Sync exclusive = writerStateRWLock.writeLock();
+            exclusive.acquire();
+            try {
+                if (activeWriter == null &&
+                        !readLockMap.hasDependency(changeLog)) {
+                    activeWriter = new WriteLockImpl(changeLog);
+                    activeWriterThread = Thread.currentThread();
+                    return activeWriter;
+                } else {
+                    signal = new Latch();
+                    waitingWriters.add(signal);
+                }
+            } finally {
+                exclusive.release();
+            }
+            // if we get here there is an active writer or there is a read
+            // lock that conflicts with the change log
+            signal.acquire();
+        }
+    }
+
+    //----------------------------< internal >----------------------------------
+
+    private final class WriteLockImpl implements WriteLock {
+
+        private final ChangeLog changes;
+
+        WriteLockImpl(ChangeLog changes) {
+            this.changes = changes;
+        }
+
+        public void release() {
+            Sync exclusive = writerStateRWLock.writeLock();
+            for (;;) {
+                try {
+                    exclusive.acquire();
+                    break;
+                } catch (InterruptedException e) {
+                    // try again
+                    Thread.interrupted();
+                }
+            }
+            try {
+                activeWriter = null;
+                activeWriterThread = null;
+                notifyWaitingReaders();
+                notifyWaitingWriters();
+            } finally {
+                exclusive.release();
+            }
+        }
+
+        public ReadLock downgrade() {
+            readLockMap.addLock(null);
+            Sync exclusive = writerStateRWLock.writeLock();
+            for (;;) {
+                try {
+                    exclusive.acquire();
+                    break;
+                } catch (InterruptedException e) {
+                    // try again
+                    Thread.interrupted();
+                }
+            }
+            try {
+                activeWriter = null;
+                activeWriterThread = null;
+                // only notify waiting readers since we still hold a down
+                // graded lock, which is kind of exclusiv with respect to
+                // other writers
+                notifyWaitingReaders();
+            } finally {
+                exclusive.release();
+            }
+            return ANONYMOUS_READ_LOCK;
+        }
+
+    }
+
+    private final class ReadLockImpl implements ReadLock {
+
+        private final ItemId id;
+
+        public ReadLockImpl() {
+            this(null);
+        }
+
+        ReadLockImpl(ItemId id) {
+            this.id = id;
+        }
+
+        public void release() {
+            Sync shared = writerStateRWLock.readLock();
+            for (;;) {
+                try {
+                    shared.acquire();
+                    break;
+                } catch (InterruptedException e) {
+                    // try again
+                    Thread.interrupted();
+                }
+            }
+            try {
+                readLockMap.removeLock(id);
+                if (activeWriterThread != Thread.currentThread()) {
+                    // only notify waiting writers if we do *not* hold a write
+                    // lock at the same time. that would be a waste of cpu time.
+                    notifyWaitingWriters();
+                }
+            } finally {
+                shared.release();
+            }
+        }
+    }
+
+    private static boolean hasDependency(ChangeLog changeLog, ItemId id) {
+        try {
+            if (changeLog.get(id) == null) {
+                if (!id.denotesNode() || changeLog.get(new NodeReferencesId((NodeId) id))
== null) {
+                    // change log does not contain the item
+                    return false;
+                }
+            }
+        } catch (NoSuchItemStateException e) {
+            // is deleted
+        }
+        return true;
+    }
+
+    /**
+     * This method is not thread-safe and calling threads must ensure that
+     * only one thread calls this method at a time.
+     */
+    private void notifyWaitingReaders() {
+        for (Iterator it = waitingReaders.iterator(); it.hasNext(); ) {
+            ((Sync) it.next()).release();
+            it.remove();
+        }
+    }
+
+    /**
+     * This method may be called concurrently by multiple threads.
+     */
+    private void notifyWaitingWriters() {
+        synchronized (waitingWriters) {
+            if (waitingWriters.isEmpty()) {
+                return;
+            }
+            for (Iterator it = waitingWriters.iterator(); it.hasNext(); ) {
+                ((Sync) it.next()).release();
+                it.remove();
+            }
+        }
+    }
+
+    private static final class LockMap {
+
+        /**
+         * 16 slots
+         */
+        private final Map[] slots = new Map[0x10];
+
+        /**
+         * Flag that indicates if the entire map is locked.
+         */
+        private volatile boolean global = false;
+
+        public LockMap() {
+            for (int i = 0; i < slots.length; i++) {
+                slots[i] = new HashMap();
+            }
+        }
+
+        /**
+         * This method must be called while holding the reader sync of the
+         * {@link FineGrainedISMLocking#writerStateRWLock}!
+         *
+         * @param id the item id.
+         */
+        public void addLock(ItemId id) {
+            if (id == null) {
+                if (global) {
+                    throw new IllegalStateException(
+                            "Map already globally locked");
+                }
+                global = true;
+                return;
+            }
+            Map locks = slots[slotIndex(id)];
+            synchronized (locks) {
+                Integer i = (Integer) locks.get(id);
+                if (i == null) {
+                    i = ONE;
+                } else {
+                    i = new Integer(i.intValue() + 1);
+                }
+                locks.put(id, i);
+            }
+        }
+
+        /**
+         * This method must be called while holding the reader sync of the
+         * {@link FineGrainedISMLocking#writerStateRWLock}!
+         *
+         * @param id the item id.
+         */
+        public void removeLock(ItemId id) {
+            if (id == null) {
+                if (!global) {
+                    throw new IllegalStateException(
+                            "Map not globally locked");
+                }
+                global = false;
+                return;
+            }
+            Map locks = slots[slotIndex(id)];
+            synchronized (locks) {
+                Integer i = (Integer) locks.get(id);
+                if (i != null) {
+                    if (i.intValue() == 1) {
+                        locks.remove(id);
+                    } else {
+                        locks.put(id, new Integer(i.intValue() - 1));
+                    }
+                } else {
+                    throw new IllegalStateException(
+                            "No lock present for id: " + id);
+                }
+            }
+        }
+
+        /**
+         * This method must be called while holding the write sync of {@link
+         * FineGrainedISMLocking#writerStateRWLock} to make sure no additional
+         * read locks are added to or removed from the map!
+         *
+         * @param changes the change log.
+         * @return if the change log has a dependency to the locks currently
+         *         present in this map.
+         */
+        public boolean hasDependency(ChangeLog changes) {
+            if (global) {
+                // read lock present, which was downgraded from a write lock
+                return true;
+            }
+            for (int i = 0; i < slots.length; i++) {
+                Map locks = slots[i];
+                if (!locks.isEmpty()) {
+                    for (Iterator it = locks.keySet().iterator(); it.hasNext(); ) {
+                        ItemId id = (ItemId) it.next();
+                        if (FineGrainedISMLocking.hasDependency(changes, id)) {
+                            return true;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+
+        private static int slotIndex(ItemId id) {
+            UUID uuid;
+            if (id.denotesNode()) {
+                uuid = ((NodeId) id).getUUID();
+            } else {
+                uuid = ((PropertyId) id).getParentId().getUUID();
+            }
+            return ((int) uuid.getLeastSignificantBits()) & 0xf;
+        }
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/FineGrainedISMLocking.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ISMLocking.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ISMLocking.java?view=auto&rev=540944
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ISMLocking.java
(added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ISMLocking.java
Wed May 23 06:12:26 2007
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.state;
+
+import org.apache.jackrabbit.core.ItemId;
+
+/**
+ * <code>ISMLocking</code> defines an interface for a locking strategy of an
+ * {@link ItemStateManager}.
+ * <p/>
+ * An implementation of <code>ISMLocking</code> must meet the following
+ * requirements:
+ * <ul>
+ * <li>While a read lock is held for a given item with id <code>I</code>
an
+ * implementation must ensure that no write lock is issued for a change log
+ * that contains a reference to an item with id <code>I</code>.</li>
+ * <li>While a write lock is held for a given change log <code>C</code>
an
+ * implementation must ensure that no read lock is issued for an item that is
+ * contained in <code>C</code>, unless the current thread is the owner of the
+ * write lock!</li>
+ * <li>While a write lock is held for a given change log <code>C</code>
an
+ * implementation must ensure that no write lock is issued for a change log
+ * <code>C'</code> that intersects with <code>C</code>. That is both
change
+ * logs contain a reference to the same item. Please note that an implementation
+ * is free to block requests entirely for additional write lock while a write
+ * lock is active. It is not a requirement to support concurrent write locks.
+ * </li>
+ * </ul>
+ */
+public interface ISMLocking {
+
+    /**
+     * Acquire a read lock for the given item <code>id</code>.
+     * @param id an item id.
+     */
+    public ReadLock acquireReadLock(ItemId id) throws InterruptedException;
+
+    /**
+     * Acquires a write lock for the given <code>changeLog</code>.
+     *
+     * @param changeLog the change log
+     * @return the write lock for the given <code>changeLog</code>.
+     * @throws InterruptedException if the thread is interrupted while creating
+     *                              the write lock.
+     */
+    public WriteLock acquireWriteLock(ChangeLog changeLog)
+            throws InterruptedException;
+
+
+    public interface ReadLock {
+
+        /**
+         * Releases this lock.
+         */
+        public void release();
+    }
+
+    public interface WriteLock {
+
+        /**
+         * Releases this lock.
+         */
+        public void release();
+
+        /**
+         * Downgrades this lock into a read lock. When this method returns this
+         * write lock is effectively released and the returned read lock must be
+         * used to further release the read lock.
+         *
+         * @return the read lock downgraded from this write lock.
+         * @throws InterruptedException if the current thread is interrupted
+         *                              while downgrading the write lock.
+         */
+        public ReadLock downgrade() throws InterruptedException;
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/ISMLocking.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java?view=diff&rev=540944&r1=540943&r2=540944
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
(original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/state/SharedItemStateManager.java
Wed May 23 06:12:26 2007
@@ -16,8 +16,6 @@
  */
 package org.apache.jackrabbit.core.state;
 
-import EDU.oswego.cs.dl.util.concurrent.ReadWriteLock;
-import EDU.oswego.cs.dl.util.concurrent.ReentrantWriterPreferenceReadWriteLock;
 import org.apache.jackrabbit.core.ItemId;
 import org.apache.jackrabbit.core.NodeId;
 import org.apache.jackrabbit.core.PropertyId;
@@ -126,7 +124,7 @@
      * cache of weak references to ItemState objects issued by this
      * ItemStateManager
      */
-    private final ItemStateReferenceCache cache;
+    private final ItemStateCache cache;
 
     /**
      * Persistence Manager used for loading and storing items
@@ -156,32 +154,14 @@
             new VirtualItemStateProvider[0];
 
     /**
-     * JCR-447: deadlock might occur when this manager is still write-locked and events are
dispatched.
-     */
-    private boolean noLockHack = false;
-
-    /**
      * State change dispatcher.
      */
     private final transient StateChangeDispatcher dispatcher = new StateChangeDispatcher();
 
     /**
-     * Read-/Write-Lock to synchronize access on this item state manager.
+     * The locking strategy.
      */
-    private final ReadWriteLock rwLock =
-            new ReentrantWriterPreferenceReadWriteLock() {
-                /**
-                 * Allow reader when there is no active writer, or current
-                 * thread owns the write lock (reentrant).
-                 * <p/>
-                 * the 'noLockHack' is only temporary (hopefully)
-                 */
-                protected boolean allowReader() {
-                    return activeWriter_ == null
-                        || activeWriter_ == Thread.currentThread()
-                        || noLockHack;
-                }
-            };
+    private ISMLocking ismLocking;
 
     /**
      * Update event channel.
@@ -206,6 +186,7 @@
         this.ntReg = ntReg;
         this.usesReferences = usesReferences;
         this.rootNodeId = rootNodeId;
+        this.ismLocking = new DefaultISMLocking();
         // create root node state if it doesn't yet exist
         if (!hasNonVirtualItemState(rootNodeId)) {
             createRootNodeState(rootNodeId, ntReg);
@@ -219,7 +200,9 @@
      * @param noLockHack
      */
     public void setNoLockHack(boolean noLockHack) {
-        this.noLockHack = noLockHack;
+        if (ismLocking instanceof DefaultISMLocking) {
+            ((DefaultISMLocking) ismLocking).setNoLockHack(noLockHack);
+        }
     }
 
     /**
@@ -231,6 +214,18 @@
         this.eventChannel = eventChannel;
     }
 
+    /**
+     * Sets a new locking strategy.
+     *
+     * @param ismLocking the locking strategy for this item state manager.
+     */
+    public void setISMLocking(ISMLocking ismLocking) {
+        if (ismLocking == null) {
+            throw new NullPointerException();
+        }
+        this.ismLocking = ismLocking;
+    }
+
     //-----------------------------------------------------< ItemStateManager >
     /**
      * {@inheritDoc}
@@ -238,7 +233,7 @@
     public ItemState getItemState(ItemId id)
             throws NoSuchItemStateException, ItemStateException {
 
-        acquireReadLock();
+        ISMLocking.ReadLock readLock = acquireReadLock(id);
 
         try {
             // check the virtual root ids (needed for overlay)
@@ -258,7 +253,7 @@
                 }
             }
         } finally {
-            rwLock.readLock().release();
+            readLock.release();
         }
         throw new NoSuchItemStateException(id.toString());
     }
@@ -268,8 +263,9 @@
      */
     public boolean hasItemState(ItemId id) {
 
+        ISMLocking.ReadLock readLock;
         try {
-            acquireReadLock();
+            readLock = acquireReadLock(id);
         } catch (ItemStateException e) {
             return false;
         }
@@ -296,7 +292,7 @@
                 }
             }
         } finally {
-            rwLock.readLock().release();
+            readLock.release();
         }
         return false;
     }
@@ -307,7 +303,7 @@
     public NodeReferences getNodeReferences(NodeReferencesId id)
             throws NoSuchItemStateException, ItemStateException {
 
-        acquireReadLock();
+        ISMLocking.ReadLock readLock = acquireReadLock(id.getTargetId());
 
         try {
             // check persistence manager
@@ -325,7 +321,7 @@
                 }
             }
         } finally {
-            rwLock.readLock().release();
+            readLock.release();
         }
 
         // throw
@@ -337,8 +333,9 @@
      */
     public boolean hasNodeReferences(NodeReferencesId id) {
 
+        ISMLocking.ReadLock readLock;
         try {
-            acquireReadLock();
+            readLock = acquireReadLock(id.getTargetId());
         } catch (ItemStateException e) {
             return false;
         }
@@ -359,7 +356,7 @@
                 }
             }
         } finally {
-            rwLock.readLock().release();
+            readLock.release();
         }
         return false;
     }
@@ -424,9 +421,11 @@
      */
     public void dump(PrintStream ps) {
         ps.println("SharedItemStateManager (" + this + ")");
-        ps.println();
-        ps.print("[referenceCache] ");
-        cache.dump(ps);
+        if (cache instanceof Dumpable) {
+            ps.println();
+            ps.print("[referenceCache] ");
+            ((Dumpable) cache).dump(ps);
+        }
     }
 
     //-------------------------------------------------< misc. public methods >
@@ -494,9 +493,10 @@
         private EventStateCollection events;
 
         /**
-         * Flag indicating whether we are holding write lock.
+         * The write lock we currently hold or <code>null</code> if none is
+         * hold.
          */
-        private boolean holdingWriteLock;
+        private ISMLocking.WriteLock writeLock;
 
         /**
          * Map of attributes stored for this update operation.
@@ -531,10 +531,9 @@
             }
 
             try {
-                acquireWriteLock();
-                holdingWriteLock = true;
+                writeLock = acquireWriteLock(local);
             } finally {
-                if (!holdingWriteLock && eventChannel != null) {
+                if (writeLock == null && eventChannel != null) {
                     eventChannel.updateCancelled(this);
                 }
             }
@@ -697,14 +696,14 @@
                 }
             }
 
+            ISMLocking.ReadLock readLock = null;
             try {
                 /* Let the shared item listeners know about the change */
                 shared.persisted();
 
                 // downgrade to read lock
-                acquireReadLock();
-                rwLock.writeLock().release();
-                holdingWriteLock = false;
+                readLock = writeLock.downgrade();
+                writeLock = null;
 
                 /* notify virtual providers about node references */
                 for (int i = 0; i < virtualNodeReferences.length; i++) {
@@ -725,13 +724,15 @@
                     eventChannel.updateCommitted(this);
                 }
 
+            } catch (InterruptedException e) {
+                throw new ItemStateException("Interrupted while downgrading to read lock");
             } finally {
-                if (holdingWriteLock) {
+                if (writeLock != null) {
                     // exception occured before downgrading lock
-                    rwLock.writeLock().release();
-                    holdingWriteLock = false;
-                } else {
-                    rwLock.readLock().release();
+                    writeLock.release();
+                    writeLock = null;
+                } else if (readLock != null) {
+                    readLock.release();
                 }
             }
         }
@@ -770,9 +771,9 @@
                     state.discard();
                 }
             } finally {
-                if (holdingWriteLock) {
-                    rwLock.writeLock().release();
-                    holdingWriteLock = false;
+                if (writeLock != null) {
+                    writeLock.release();
+                    writeLock = null;
                 }
             }
         }
@@ -864,8 +865,9 @@
     public void externalUpdate(ChangeLog external, EventStateCollection events) {
         boolean holdingWriteLock = false;
 
+        ISMLocking.WriteLock wLock = null;
         try {
-            acquireWriteLock();
+            wLock = acquireWriteLock(external);
             holdingWriteLock = true;
 
             doExternalUpdate(external);
@@ -874,21 +876,25 @@
             log.error(msg);
         }
 
+        ISMLocking.ReadLock rLock = null;
         try {
-            acquireReadLock();
-            rwLock.writeLock().release();
-            holdingWriteLock = false;
-
-            events.dispatch();
-        } catch (ItemStateException e) {
+            if (wLock != null) {
+                rLock = wLock.downgrade();
+                holdingWriteLock = false;
+                events.dispatch();
+            }
+        } catch (InterruptedException e) {
             String msg = "Unable to downgrade to read lock.";
             log.error(msg);
         } finally {
             if (holdingWriteLock) {
-                rwLock.writeLock().release();
-                holdingWriteLock = false;
+                if (wLock != null) {
+                    wLock.release();
+                }
             } else {
-                rwLock.readLock().release();
+                if (rLock != null) {
+                    rLock.release();
+                }
             }
         }
 
@@ -1424,11 +1430,12 @@
     /**
      * Acquires the read lock on this item state manager.
      *
+     * @param id the id of the item for which to acquire a read lock.
      * @throws ItemStateException if the read lock cannot be acquired.
      */
-    private void acquireReadLock() throws ItemStateException {
+    private ISMLocking.ReadLock acquireReadLock(ItemId id) throws ItemStateException {
         try {
-            rwLock.readLock().acquire();
+            return ismLocking.acquireReadLock(id);
         } catch (InterruptedException e) {
             throw new ItemStateException("Interrupted while acquiring read lock");
         }
@@ -1437,11 +1444,12 @@
     /**
      * Acquires the write lock on this item state manager.
      *
+     * @param changeLog the change log for which to acquire a write lock.
      * @throws ItemStateException if the write lock cannot be acquired.
      */
-    private void acquireWriteLock() throws ItemStateException {
+    private ISMLocking.WriteLock acquireWriteLock(ChangeLog changeLog) throws ItemStateException
{
         try {
-            rwLock.writeLock().acquire();
+            return ismLocking.acquireWriteLock(changeLog);
         } catch (InterruptedException e) {
             throw new ItemStateException("Interrupted while acquiring write lock");
         }



Mime
View raw message