/* * Copyright 2008-2012 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.repo.map; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import com.amazon.carbonado.FetchException; import com.amazon.carbonado.Storable; import com.amazon.carbonado.cursor.AbstractCursor; import com.amazon.carbonado.txn.TransactionScope; /** * Returns copies of Storables that it iterates over. * * @author Brian S O'Neill */ class MapCursor<S extends Storable> extends AbstractCursor<S> { private static final AtomicReferenceFieldUpdater<MapCursor, Iterator> cIteratorRef = AtomicReferenceFieldUpdater.newUpdater (MapCursor.class, Iterator.class, "mIterator"); private final MapStorage<S> mStorage; private final TransactionScope<MapTransaction> mScope; private final MapTransaction mTxn; private final boolean mIsForUpdate; private volatile Iterator<S> mIterator; MapCursor(MapStorage<S> storage, TransactionScope<MapTransaction> scope, Iterable<S> iterable) throws Exception { MapTransaction txn = scope.getTxn(); mStorage = storage; mScope = scope; mTxn = txn; if (txn == null) { mStorage.mLock.lockForRead(scope); mIsForUpdate = false; } else { // Since lock is so coarse, all reads in transaction scope are // upgrade to avoid deadlocks. txn.lockForUpgrade(mStorage.mLock, mIsForUpdate = scope.isForUpdate()); } scope.register(storage.getStorableType(), this); mIterator = iterable.iterator(); } public void close() { Iterator<S> it = mIterator; if (it != null) { if (cIteratorRef.compareAndSet(this, it, null)) { UpgradableLock lock = mStorage.mLock; if (mTxn == null) { lock.unlockFromRead(mScope); } else { mTxn.unlockFromUpgrade(lock, mIsForUpdate); } mScope.unregister(mStorage.getStorableType(), this); } } } public boolean hasNext() throws FetchException { Iterator<S> it = mIterator; try { if (it != null && it.hasNext()) { return true; } else { close(); } return false; } catch (ConcurrentModificationException e) { close(); throw new FetchException(e); } catch (Error e) { try { close(); } catch (Error e2) { // Ignore. } throw e; } } public S next() throws FetchException { Iterator<S> it = mIterator; if (it == null) { close(); throw new NoSuchElementException(); } try { S next = mStorage.copyAndFireLoadTrigger(it.next()); if (!hasNext()) { close(); } return next; } catch (ConcurrentModificationException e) { close(); throw new FetchException(e); } catch (Error e) { try { close(); } catch (Error e2) { // Ignore. } throw e; } } @Override public int skipNext(int amount) throws FetchException { if (amount <= 0) { if (amount < 0) { throw new IllegalArgumentException("Cannot skip negative amount: " + amount); } return 0; } // Skip over entries without copying them. int count = 0; Iterator<S> it = mIterator; if (it != null) { try { while (--amount >= 0 && it.hasNext()) { it.next(); count++; } } catch (ConcurrentModificationException e) { close(); throw new FetchException(e); } catch (Error e) { try { close(); } catch (Error e2) { // Ignore. } throw e; } } return count; } }