/*
* Part of the CCNx Java Library.
*
* Copyright (C) 2012, 2013 Palo Alto Research Center, Inc.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 2.1
* as published by the Free Software Foundation.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. You should have received
* a copy of the GNU Lesser General Public License along with this library;
* if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
* Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.ccnx.ccn.impl.sync;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import org.ccnx.ccn.io.content.SyncNodeComposite;
/**
* Nodes can be cached by hash across different comparators. We use WeakReferences to avoid accidentally caching nodes that
* no longer have any real referents.
*
* Since we only need to request nodes once per slice, the pending mechanism should be global
*/
public class SyncNodeCache {
/**
* This mechanism is used to avoid requesting the same node more than once (see below). One of
* these objects is created for each node request and used as a java synchronization object to
* insure that we don't return without having retrieved the node when that is required.
*/
public class Pending {
boolean _pending = false;
public void setPending(boolean value) {
_pending = value;
}
public boolean getPending() {
return _pending;
}
}
// For holding objects used as locks for each pending hash
private HashMap<SyncHashEntry, Pending> _hashesPending = new HashMap<SyncHashEntry, Pending>();
protected HashMap<SyncHashEntry, WeakReference<SyncNodeComposite>> _nodes = new HashMap<SyncHashEntry, WeakReference<SyncNodeComposite>>();
/**
* Put a newly decoded node into the cache
* @param node
*/
public void putNode(SyncNodeComposite node) {
synchronized (this) {
WeakReference<SyncNodeComposite> wr = new WeakReference<SyncNodeComposite>(node);
_nodes.put((new SyncHashEntry(node.getHash())), wr);
clearPending(node.getHash());
}
}
/**
* Get a node associated with a hash if there is one
* @param hash
* @return
*/
public SyncNodeComposite getNode(byte[] hash) {
if (null == hash)
return null;
synchronized (this) {
WeakReference<SyncNodeComposite> wr = _nodes.get(new SyncHashEntry(hash));
if (null == wr)
return null;
return wr.get();
}
}
/**
* Activate the mechanism to avoid multiple requests for the same node and to wait for a
* node in the process of being fetched by another comparator if it is.
* This is a "get and set" routine which creates and stores a lock for sharing if one hasn't
* already been created for this hash. The caller must acquire or attempt to acquire the lock.
* The first caller should request the node and notify waiters when it has it. Subsequent callers
* will wait for the node to return by acquiring the lock.
*
* @param hash
* @return Lock object for waiting for the node
*/
public Pending pending(byte[] hash) {
Pending lock = null;
synchronized (this) {
SyncHashEntry she = new SyncHashEntry(hash);
lock = _hashesPending.get(she);
if (null == lock) {
lock = new Pending();
_hashesPending.put(she, lock);
}
return lock;
}
}
/**
* Call this after a node has been returned. It releases the semaphore (allowing waiters to
* continue) and removes the entry from the array of pending node requests
* @param hash
*/
public void clearPending(byte[] hash) {
Pending lock;
synchronized (this) {
SyncHashEntry she = new SyncHashEntry(hash);
lock = _hashesPending.remove(she);
}
if (null != lock) {
synchronized (lock) {
lock.setPending(false);
lock.notifyAll();
}
}
}
/**
* Wakeup waiters but leave lock pending
* @param hash
*/
public void wakeupPending(byte[] hash) {
Pending lock;
synchronized (this) {
SyncHashEntry she = new SyncHashEntry(hash);
lock = _hashesPending.get(she);
}
if (null != lock) {
synchronized (lock) {
lock.notifyAll();
}
}
}
}