Author: manik.surtani(a)jboss.com
Date: 2008-07-23 16:41:02 -0400 (Wed, 23 Jul 2008)
New Revision: 6385
Added:
core/trunk/src/main/java/org/jboss/cache/util/concurrent/SelfInitializingConcurrentHashMap.java
Modified:
core/trunk/src/main/java/org/jboss/cache/PessimisticUnversionedNode.java
core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java
Log:
lazy initialization of child map
Modified: core/trunk/src/main/java/org/jboss/cache/PessimisticUnversionedNode.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/PessimisticUnversionedNode.java 2008-07-23
16:15:03 UTC (rev 6384)
+++ core/trunk/src/main/java/org/jboss/cache/PessimisticUnversionedNode.java 2008-07-23
20:41:02 UTC (rev 6385)
@@ -86,6 +86,7 @@
{
PessimisticUnversionedNode<K, V> n = new PessimisticUnversionedNode<K,
V>(fqn.getLastElement(), fqn, data, cache);
copyInternals(n);
+ n.children = children;
n.lockStrategyFactory = lockStrategyFactory;
return n;
}
@@ -100,7 +101,7 @@
{
synchronized (this)
{
- children().put(child.getFqn().getLastElement(), child);
+ children.put(child.getFqn().getLastElement(), child);
}
}
else
@@ -116,7 +117,7 @@
throw new IllegalArgumentException("null child name");
}
- child = (NodeSPI<K, V>) children().get(childName);
+ child = (NodeSPI<K, V>) children.get(childName);
InvocationContext ctx = cache.getInvocationContext();
if (createIfNotExists && child == null)
{
@@ -132,7 +133,7 @@
{
// check again to see if the child exists
// after acquiring exclusive lock
- child = (NodeSPI<K, V>) children().get(childName);
+ child = (NodeSPI<K, V>) children.get(childName);
if (child == null)
{
if (notify) cache.getNotifier().notifyNodeCreated(childFqn, true, ctx);
Modified: core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java 2008-07-23 16:15:03 UTC
(rev 6384)
+++ core/trunk/src/main/java/org/jboss/cache/UnversionedNode.java 2008-07-23 20:41:02 UTC
(rev 6385)
@@ -17,6 +17,7 @@
import org.jboss.cache.optimistic.DataVersion;
import org.jboss.cache.transaction.GlobalTransaction;
import org.jboss.cache.util.ImmutableSetCopy;
+import org.jboss.cache.util.concurrent.SelfInitializingConcurrentHashMap;
import java.util.Collections;
import java.util.HashMap;
@@ -24,7 +25,6 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
/**
* Basic data node class. Throws {@link UnsupportedOperationException} for
version-specific methods like {@link #getVersion()} and
@@ -82,7 +82,15 @@
setLockForChildInsertRemove(lockForChildInsertRemove);
this.fqn = fqn;
// if this is a root node, create the child map.
- if (fqn.isRoot()) children = new ConcurrentHashMap<Object, Node<K,
V>>(64, .5f, 16);
+ if (fqn.isRoot())
+ {
+ children = new ConcurrentHashMap<Object, Node<K, V>>(64, .5f, 16);
+ }
+ else
+ {
+ // this always needs to be initialized. The actual cost of the
ConcurrentHashMap, however, is deferred.
+ children = new SelfInitializingConcurrentHashMap<Object, Node<K,
V>>();
+ }
}
public UnversionedNode(Fqn fqn, CacheSPI<K, V> cache, boolean
lockForChildInsertRemove, Map<K, V> data)
@@ -130,21 +138,6 @@
return cache.peek(fqn.getParent(), true);
}
- protected final ConcurrentMap<Object, Node<K, V>> children()
- {
- if (children == null) initChildMap();
- return children;
- }
-
- private synchronized void initChildMap()
- {
- if (children == null)
- {
- // Fewer segments to save memory
- children = new ConcurrentHashMap<Object, Node<K, V>>(4, .75f, 4);
- }
- }
-
// does not need to be synchronized since this will only be accessed by a single
thread in MVCC thanks to the write lock.
private void initDataMap()
{
@@ -202,14 +195,14 @@
throw new IllegalArgumentException("null child name");
}
- child = (NodeSPI<K, V>) children().get(childName);
+ child = (NodeSPI<K, V>) children.get(childName);
InvocationContext ctx = cache.getInvocationContext();
if (createIfNotExists && child == null)
{
Fqn childFqn = Fqn.fromRelativeElements(fqn, childName);
NodeSPI<K, V> newChild = nodeFactory.createNode(childFqn, delegate);
- child = (NodeSPI<K, V>) children().putIfAbsent(childName, newChild);
+ child = (NodeSPI<K, V>) children.putIfAbsent(childName, newChild);
if (child == null)
{
@@ -349,7 +342,7 @@
Fqn childFqn = child.getFqn();
if (childFqn.isDirectChildOf(fqn))
{
- children().put(childFqn.getLastElement(), child);
+ children.put(childFqn.getLastElement(), child);
}
else
{
@@ -449,7 +442,7 @@
this.children = null;
else
{
- this.children().clear();
+ this.children.clear();
this.children.putAll(children);
}
}
@@ -500,7 +493,7 @@
{
if (childName != null)
{
- children().put(childName, n);
+ children.put(childName, n);
}
}
@@ -632,7 +625,7 @@
if (trace) log.trace("Marking node " + getFqn() + " as " +
(valid ? "" : "in") + "valid");
if (recursive)
{
- for (Node<K, V> child : children().values())
+ for (Node<K, V> child : children.values())
{
((NodeSPI<K, V>) child).setValid(valid, recursive);
}
@@ -661,7 +654,7 @@
protected void copyInternals(UnversionedNode n)
{
// direct reference to child map
- n.children = children();
+ n.children = children;
n.commandsFactory = commandsFactory;
n.delegate = delegate;
n.nodeFactory = nodeFactory;
Added:
core/trunk/src/main/java/org/jboss/cache/util/concurrent/SelfInitializingConcurrentHashMap.java
===================================================================
---
core/trunk/src/main/java/org/jboss/cache/util/concurrent/SelfInitializingConcurrentHashMap.java
(rev 0)
+++
core/trunk/src/main/java/org/jboss/cache/util/concurrent/SelfInitializingConcurrentHashMap.java 2008-07-23
20:41:02 UTC (rev 6385)
@@ -0,0 +1,192 @@
+package org.jboss.cache.util.concurrent;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Primarily used to hold child maps for nodes. Underlying CHM is null initially, and
once threads start
+ * writing to this map, the CHM is initialized.
+ *
+ * @author Manik Surtani (<a
href="mailto:manik@jboss.org">manik@jboss.org</a>)
+ * @since 3.0
+ */
+public class SelfInitializingConcurrentHashMap<K, V> implements ConcurrentMap<K,
V>
+{
+ private volatile ConcurrentMap<K, V> delegate;
+// private int initialCapacity = -1, concurrencyLevel = -1;
+// private float loadFactor = -1;
+
+ /**
+ * Creates a new delegate map and will use a default constructor to initialize the
{@link java.util.concurrent.ConcurrentMap}
+ * it will eventually delegate to.
+ */
+ public SelfInitializingConcurrentHashMap()
+ {
+
+ }
+
+ /**
+ * Creates a new delegate map and will use the params passed in to initialize the
{@link java.util.concurrent.ConcurrentMap}
+ * it will eventually delegate to.
+ *
+ * @param initialCapacity the initial capacity. The implementation
+ * performs internal sizing to accommodate this many
elements.
+ * @param loadFactor the load factor threshold, used to control resizing.
+ * Resizing may be performed when the average number of
elements per
+ * bin exceeds this threshold.
+ * @param concurrencyLevel the estimated number of concurrently
+ * updating threads. The implementation performs internal
sizing
+ * to try to accommodate this many threads.
+ * @throws IllegalArgumentException if the initial capacity is
+ * negative or the load factor or concurrencyLevel
are
+ * nonpositive.
+ */
+// public SelfInitializingConcurrentHashMap(int initialCapacity, float loadFactor, int
concurrencyLevel)
+// {
+// this.initialCapacity = initialCapacity;
+// this.loadFactor = loadFactor;
+// this.concurrencyLevel = concurrencyLevel;
+// }
+
+ // -------------- initialization methods and helpers ----------------------
+ private ConcurrentMap<K, V> getDelegate()
+ {
+ if (delegate == null) init();
+ return delegate;
+ }
+
+ private synchronized void init()
+ {
+ // Reminiscent of DCL but the delegate here is volatile so construction reordering
should not affect.
+ if (delegate == null)
+ {
+ delegate = new ConcurrentHashMap<K, V>(4, 0.75f, 4);
+ // hard-coded for node child maps for now
+
+// if (initialCapacity == -1 || concurrencyLevel == -1 || loadFactor == -1)
+// {
+// delegate = new ConcurrentHashMap<K, V>();
+// }
+// else
+// {
+// delegate = new ConcurrentHashMap<K, V>(initialCapacity, loadFactor,
concurrencyLevel);
+// }
+ }
+ }
+
+ // -------------- Public API methods that will trigger initialization
----------------------
+
+ public final V put(K key, V value)
+ {
+ return getDelegate().put(key, value);
+ }
+
+ public final V remove(Object key)
+ {
+ return getDelegate().remove(key);
+ }
+
+ public final void putAll(Map<? extends K, ? extends V> m)
+ {
+ getDelegate().putAll(m);
+ }
+
+ public final V putIfAbsent(K key, V value)
+ {
+ return getDelegate().putIfAbsent(key, value);
+ }
+
+ public final boolean replace(K key, V oldValue, V newValue)
+ {
+ return getDelegate().replace(key, oldValue, newValue);
+ }
+
+ public final V replace(K key, V value)
+ {
+ return getDelegate().replace(key, value);
+ }
+
+ // -------------- Public API methods that won't trigger initialization
----------------------
+
+ public final boolean remove(Object key, Object value)
+ {
+ return delegate != null && delegate.remove(key, value);
+ }
+
+ public final int size()
+ {
+ return delegate == null ? 0 : delegate.size();
+ }
+
+ public final boolean isEmpty()
+ {
+ return delegate == null || delegate.isEmpty();
+ }
+
+ public final boolean containsKey(Object key)
+ {
+ return delegate != null && delegate.containsKey(key);
+ }
+
+ public final boolean containsValue(Object value)
+ {
+ return delegate != null && delegate.containsValue(value);
+ }
+
+ public final V get(Object key)
+ {
+ return delegate == null ? null : delegate.get(key);
+ }
+
+ public final void clear()
+ {
+ if (delegate != null) delegate.clear();
+ }
+
+ public final Set<K> keySet()
+ {
+ if (delegate == null) return Collections.emptySet();
+ return delegate.keySet();
+ }
+
+ public final Collection<V> values()
+ {
+ if (delegate == null) return Collections.emptySet();
+ return delegate.values();
+ }
+
+ public final Set<Entry<K, V>> entrySet()
+ {
+ if (delegate == null) return Collections.emptySet();
+ return delegate.entrySet();
+ }
+
+ @Override
+ public String toString()
+ {
+ return "SelfInitializingConcurrentHashMap{" +
+ "delegate=" + delegate +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o)
+ {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ SelfInitializingConcurrentHashMap that = (SelfInitializingConcurrentHashMap) o;
+ return !(delegate != null ? !delegate.equals(that.delegate) : that.delegate !=
null);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result;
+ result = (delegate != null ? delegate.hashCode() : 0);
+ return result;
+ }
+}