From 8809341248c62b15b78d7e6d8e06ab2ec3793c8e Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Wed, 28 Mar 2007 22:00:24 +0000 Subject: Merged 1.2-dev to trunk. --- .../com/amazon/carbonado/util/AbstractPool.java | 94 ++++++++++++++ .../amazon/carbonado/util/AbstractWeakPool.java | 143 +++++++++++++++++++++ .../carbonado/util/WeakReentrantLockPool.java | 45 +++++++ .../util/WeakReentrantReadWriteLockPool.java | 45 +++++++ 4 files changed, 327 insertions(+) create mode 100644 src/main/java/com/amazon/carbonado/util/AbstractPool.java create mode 100644 src/main/java/com/amazon/carbonado/util/AbstractWeakPool.java create mode 100644 src/main/java/com/amazon/carbonado/util/WeakReentrantLockPool.java create mode 100644 src/main/java/com/amazon/carbonado/util/WeakReentrantReadWriteLockPool.java (limited to 'src/main/java/com/amazon/carbonado/util') 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 { + private final ConcurrentMap mValues; + private final WeakReentrantLockPool mLockPool; + + protected AbstractPool() { + mValues = new ConcurrentHashMap(); + mLockPool = new WeakReentrantLockPool(); + } + + /** + * 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 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 { + private final ConcurrentMap> mValues; + private final ReferenceQueue mValueRefQueue; + + protected AbstractWeakPool() { + mValues = new ConcurrentHashMap>(); + mValueRefQueue = new ReferenceQueue(); + } + + /** + * 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: + * + *
+     * // 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();
+     * }
+     * 
+ */ + public V get(K key) throws E { + clean(); + + ValueRef 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(value, mValueRefQueue, key); + while (true) { + ValueRef 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) existingRef).mKey, existingRef); + } + } + + return value; + } + + /** + * Manually remove a value, returning the old value. + */ + public V remove(Object key) { + clean(); + + ValueRef 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 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) ref).mKey, ref); + } + } + + private static class ValueRef extends WeakReference { + final K mKey; + + ValueRef(V value, ReferenceQueue 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 + extends AbstractWeakPool +{ + 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 + extends AbstractWeakPool +{ + private final boolean mFair; + + public WeakReentrantReadWriteLockPool() { + this(false); + } + + public WeakReentrantReadWriteLockPool(boolean fair) { + mFair = fair; + } + + protected ReentrantReadWriteLock create(K key) { + return new ReentrantReadWriteLock(mFair); + } +} -- cgit v1.2.3