summaryrefslogtreecommitdiff
path: root/src/main/java/com/amazon/carbonado/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/amazon/carbonado/util')
-rw-r--r--src/main/java/com/amazon/carbonado/util/AbstractPool.java94
-rw-r--r--src/main/java/com/amazon/carbonado/util/AbstractWeakPool.java143
-rw-r--r--src/main/java/com/amazon/carbonado/util/WeakReentrantLockPool.java45
-rw-r--r--src/main/java/com/amazon/carbonado/util/WeakReentrantReadWriteLockPool.java45
4 files changed, 327 insertions, 0 deletions
diff --git a/src/main/java/com/amazon/carbonado/util/AbstractPool.java b/src/main/java/com/amazon/carbonado/util/AbstractPool.java
new file mode 100644
index 0000000..896802d
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/util/AbstractPool.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.amazon.carbonado.util;
+
+import java.util.Collection;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * A concurrent pool of strongly referenced values mapped by key. Values are
+ * lazily created and pooled.
+ *
+ * @author Brian S O'Neill
+ */
+public abstract class AbstractPool<K, V, E extends Exception> {
+ private final ConcurrentMap<K, V> mValues;
+ private final WeakReentrantLockPool<K> mLockPool;
+
+ protected AbstractPool() {
+ mValues = new ConcurrentHashMap<K, V>();
+ mLockPool = new WeakReentrantLockPool<K>();
+ }
+
+ /**
+ * Returns a value for the given key, which is lazily created and
+ * pooled. If multiple threads are requesting upon the same key
+ * concurrently, at most one thread attempts to lazily create the
+ * value. The others wait for it to become available.
+ */
+ public V get(K key) throws E {
+ // Quick check without locking.
+ V value = mValues.get(key);
+ if (value != null) {
+ return value;
+ }
+
+ // Check again with key lock held.
+ Lock lock = mLockPool.get(key);
+ lock.lock();
+ try {
+ value = mValues.get(key);
+ if (value == null) {
+ try {
+ value = create(key);
+ mValues.put(key, value);
+ } catch (Exception e) {
+ // Workaround compiler bug.
+ ThrowUnchecked.fire(e);
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+
+ return value;
+ }
+
+ /**
+ * Remove a value, returning the old value.
+ */
+ public V remove(Object key) {
+ return mValues.remove(key);
+ }
+
+ /**
+ * Returns the pool values, which may be concurrently modified.
+ */
+ public Collection<V> values() {
+ return mValues.values();
+ }
+
+ /**
+ * Return a new value instance.
+ */
+ protected abstract V create(K key) throws E;
+}
diff --git a/src/main/java/com/amazon/carbonado/util/AbstractWeakPool.java b/src/main/java/com/amazon/carbonado/util/AbstractWeakPool.java
new file mode 100644
index 0000000..8ae5476
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/util/AbstractWeakPool.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.amazon.carbonado.util;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * A concurrent pool of weakly referenced values mapped by key. Values are
+ * created (and recreated) as needed.
+ *
+ * @author Brian S O'Neill
+ * @see AbstractPool
+ */
+abstract class AbstractWeakPool<K, V, E extends Exception> {
+ private final ConcurrentMap<K, ValueRef<K, V>> mValues;
+ private final ReferenceQueue<V> mValueRefQueue;
+
+ protected AbstractWeakPool() {
+ mValues = new ConcurrentHashMap<K, ValueRef<K, V>>();
+ mValueRefQueue = new ReferenceQueue<V>();
+ }
+
+ /**
+ * Returns a value for the given key. Unused values are automatically
+ * cleared to free up memory, even if they are still held. Repeat calls are
+ * guaranteed to return the same value instance only if the value is
+ * strongly reachable. The following idiom should be used when using the
+ * pool for maintaining locks:
+ *
+ * <pre>
+ * // Store lock in local variable to be strongly reachable.
+ * Lock lock = lockPool.get(key);
+ * lock.lock();
+ * try {
+ * // access the resource protected by this lock
+ * ...
+ * } finally {
+ * lock.unlock();
+ * }
+ * </pre>
+ */
+ public V get(K key) throws E {
+ clean();
+
+ ValueRef<K, V> valueRef = mValues.get(key);
+ V value;
+
+ if (valueRef == null || (value = valueRef.get()) == null) {
+ try {
+ value = create(key);
+ } catch (Exception e) {
+ // Workaround compiler bug.
+ ThrowUnchecked.fire(e);
+ return null;
+ }
+ valueRef = new ValueRef<K, V>(value, mValueRefQueue, key);
+ while (true) {
+ ValueRef<K, V> existingRef = mValues.putIfAbsent(key, valueRef);
+ if (existingRef == null) {
+ // Newly created value is now the official value.
+ break;
+ }
+ V existing = existingRef.get();
+ if (existing != null) {
+ // Someone else just created value before us. Use that
+ // instead and chuck the new value object.
+ value = existing;
+ valueRef.clear();
+ break;
+ }
+ // Reference just got cleared. Try again. Explicitly remove it
+ // to prevent an infinite loop. Note that the two argument
+ // remove method is called to ensure that what is being removed
+ // is not a new value.
+ mValues.remove(((ValueRef<K, V>) existingRef).mKey, existingRef);
+ }
+ }
+
+ return value;
+ }
+
+ /**
+ * Manually remove a value, returning the old value.
+ */
+ public V remove(Object key) {
+ clean();
+
+ ValueRef<K, V> valueRef = mValues.remove(key);
+ V value;
+
+ if (valueRef != null && (value = valueRef.get()) != null) {
+ valueRef.clear();
+ return value;
+ }
+
+ return null;
+ }
+
+ /**
+ * Return a new value instance.
+ */
+ protected abstract V create(K key) throws E;
+
+ private void clean() {
+ // Clean out cleared values.
+ Reference<? extends V> ref;
+ while ((ref = mValueRefQueue.poll()) != null) {
+ // Note that the two argument remove method is called to ensure
+ // that what is being removed is not a new value.
+ mValues.remove(((ValueRef<K, V>) ref).mKey, ref);
+ }
+ }
+
+ private static class ValueRef<K, V> extends WeakReference<V> {
+ final K mKey;
+
+ ValueRef(V value, ReferenceQueue<V> queue, K key) {
+ super(value, queue);
+ mKey = key;
+ }
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/util/WeakReentrantLockPool.java b/src/main/java/com/amazon/carbonado/util/WeakReentrantLockPool.java
new file mode 100644
index 0000000..5bfd58c
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/util/WeakReentrantLockPool.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.amazon.carbonado.util;
+
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * A concurrent pool of weakly referenced {@link ReentrantLock} instances
+ * mapped by key. Locks are created (and recreated) as needed.
+ *
+ * @author Brian S O'Neill
+ */
+class WeakReentrantLockPool<K>
+ extends AbstractWeakPool<K, ReentrantLock, RuntimeException>
+{
+ private final boolean mFair;
+
+ public WeakReentrantLockPool() {
+ this(false);
+ }
+
+ public WeakReentrantLockPool(boolean fair) {
+ mFair = fair;
+ }
+
+ protected ReentrantLock create(K key) {
+ return new ReentrantLock(mFair);
+ }
+}
diff --git a/src/main/java/com/amazon/carbonado/util/WeakReentrantReadWriteLockPool.java b/src/main/java/com/amazon/carbonado/util/WeakReentrantReadWriteLockPool.java
new file mode 100644
index 0000000..b41ad1f
--- /dev/null
+++ b/src/main/java/com/amazon/carbonado/util/WeakReentrantReadWriteLockPool.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed 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.amazon.carbonado.util;
+
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * A concurrent pool of weakly referenced {@link ReentrantReadWriteLock}
+ * instances mapped by key. Locks are created (and recreated) as needed.
+ *
+ * @author Brian S O'Neill
+ */
+class WeakReentrantReadWriteLockPool<K>
+ extends AbstractWeakPool<K, ReentrantReadWriteLock, RuntimeException>
+{
+ private final boolean mFair;
+
+ public WeakReentrantReadWriteLockPool() {
+ this(false);
+ }
+
+ public WeakReentrantReadWriteLockPool(boolean fair) {
+ mFair = fair;
+ }
+
+ protected ReentrantReadWriteLock create(K key) {
+ return new ReentrantReadWriteLock(mFair);
+ }
+}