Transaction txn, String name, long oid)
{
TxnContext context = contextMap.join(txn);
long stop = context.getStopTime();
BindingKey nameKey = BindingKey.get(name);
BindingValue result;
for (int i = 0; true; i++) {
if (i >= MAX_CACHE_RETRIES) {
throw new ResourceUnavailableException("Too many retries");
}
/* Find cache entry for name or next higher name */
BindingCacheEntry entry = cache.getCeilingBindingEntry(nameKey);
final BindingKey entryKey = (entry != null) ? entry.key : LAST;
final Object lock = cache.getBindingLock(entryKey);
/* Reserve space for last entry, and requested or next name */
Cache.Reservation reserve = cache.getReservation(2);
try {
synchronized (lock) {
if (logger.isLoggable(FINEST)) {
logger.log(FINEST,
"setBindingInternal txn:" + txn +
", name:" + name + " found entry:" + entry);
}
if (entry == null) {
/* No next entry -- create last entry */
entry = context.createLastBindingEntry(reserve);
if (entry == null) {
/* Last entry already present -- try again */
continue;
} else {
/* Get information from server and try again */
entry.setPendingPrevious();
scheduleFetch(
new GetBindingForUpdateRunnable(
context, nameKey, entry.key, reserve));
continue;
}
} else if (nameKey.equals(entryKey)) {
/* Found entry for name */
if (!setBindingInternalFound(context, lock, entry)) {
/* Entry is not in cache -- try again */
continue;
} else {
/* Entry is writable */
context.noteModifiedBinding(entry, oid);
result = new BindingValue(1, null);
break;
}
} else if (!assureNextEntry(entry, nameKey, true, lock,
stop))
{
/* Entry is no longer for next name -- try again */
continue;
} else if (entry.getKnownUnbound(nameKey)) {
/* Name is unbound */
if (!setBindingInternalUnbound(
context, lock, entry, nameKey))
{
/*
* Things changed while trying to get writable next
* entry -- try again
*/
continue;
} else {
/*
* Next entry is writable and name is still known
* to be unbound -- fall through to create entry
* for the new binding
*/
context.noteAccess(entry);
}
} else {
/* Get information from server and try again */
entry.setPendingPrevious();
context.noteAccess(entry);
scheduleFetch(
new GetBindingForUpdateRunnable(
context, nameKey, entry.key, reserve));
continue;
}
}
} finally {
reserve.done();
}
/* Get access coordinator lock for the next entry */
reportNameAccess(txnProxy.getCurrentTransaction(),
entryKey.getNameAllowLast(), WRITE);
/* Verify the next entry and mark it pending previous */
BindingKey entryPreviousKey;
boolean entryPreviousKeyUnbound;
synchronized (lock) {
entry = cache.getBindingEntry(entryKey);
if (entry == null ||
!assureNextEntry(entry, nameKey, true, lock, stop))
{
/* Next entry changed -- try again */
continue;
}
entry.setPendingPrevious();
entryPreviousKey = entry.getPreviousKey();
entryPreviousKeyUnbound = entry.isPreviousKeyUnbound();
}
/* Create a new entry for the requested name */
reserve = cache.getReservation(1);
try {
synchronized (cache.getBindingLock(nameKey)) {
BindingCacheEntry nameEntry =
context.createNewBindingEntry(nameKey, oid, reserve);
if (entryPreviousKey != null &&
entryPreviousKey.compareTo(nameKey) < 0)
{
nameEntry.setPreviousKey(
entryPreviousKey, entryPreviousKeyUnbound);
}
}
} finally {
reserve.done();
}
/* Update the next entry */
synchronized (lock) {
entry = cache.getBindingEntry(entryKey);
assert entry != null : "No entry for " + entryKey;
/*
* It's important that we clear the pending previous field on
* the entry after setting it above. Currently, none of the
* intervening calls can fail, but need to make certain this
* stays true, or else put unwind logic in place.
* -tjb@sun.com (12/14/2009)
*/
entry.setNotPendingPrevious(lock);
context.updatePreviousKey(entry, nameKey, BOUND);
}
/* Name was unbound */
result = new BindingValue(-1, entryKey.getNameAllowLast());
break;
}
maybeCheckBindings(CheckBindingsType.OPERATION);
return result;
}