Node ceilingNode(double key) { for (; ; ) { Node b = skipListMap.findGrandPredecessor(key, refCntCallback); if (b == null) b = head; if (b != head && key <= b.last()) { return b; } assert b == head || b.last() < key; Node n = safeNext(b); release(b); if (n == null) { return null; } else if (key <= n.last()) { return n; } Node f = safeNext(n); release(n); if (f == null) { return null; } else { if (isInconsistentNextNode(f, key)) { release(f); continue; } return f; } } }
static Node safeNext(Node b) { while (true) { Node n = b.next; if (n == null) return null; n.incRefCnt(); if (n == b.next) return n; else release(n); } }
int findBeginIndex(Node n, double key, boolean inclusive) { boolean exclusive = !inclusive; if (key <= n.first()) { if (key == n.first() && exclusive) return 1; return 0; } int pos = n.findKeyIndex(key); if (pos < 0) pos = -(pos + 1); else if (!inclusive) pos++; return pos; }
// concurrent read/write access is allowed boolean appendNewAtomic(double key, Object value, ConcurrentDoubleOrderedListMap orderedMap) { synchronized (this) { if (isMarked()) return false; if (next != null) return false; Node n = Node.alloc(); n.put(key, value, orderedMap); // assert n.len()==1; n.next = null; boolean success = casNext(null, n); assert success; return true; } }
private boolean emptySlotInNextTwo() { if (next == null) return false; if (!next.isFull()) return true; if (next.next == null) return false; if (!next.next.isFull()) return true; return false; }
// concurrent read/write access is allowed Object putAtomic( double key, Object value, Node b, boolean onlyIfAbsent, ConcurrentDoubleOrderedListMap orderedMap) { synchronized (b) { if (b.isMarked()) return Retry; synchronized (this) { if (isMarked()) return Retry; int len = len(); int pos = Arrays.binarySearch(keys, 0, len, key); if (pos >= 0) { Object old = vals[pos]; if (onlyIfAbsent) { if (old == null) { vals[pos] = value; return null; } else { return old; } } vals[pos] = value; return old; } pos = -(pos + 1); putAtomicReally(b, pos, key, value, orderedMap); return null; } } }
private boolean isInconsistentNextNode(Node f, double key) { /** * With skipListMap.findGrandPredecessor, we expect the key of the target node (next of next * node of returned node) to be greater than or equal to the given key. If not, there is * inconsistency between skipListMap and this ordered list map. * * @see Node#putAtomicReally for the cause of this inconsistency */ return f.next != null && f.first() < key; }
public void cancel(Node n) { // It's possible (although very unlikely) that this release operation sets n.refcnt to 0, // in which case n.refcnt could have been incremented from 0 by found(Node n) above. // Hence, n.refcnt can be decremented to 0 two times; so even when n.refcnt is 0, // we don't decrement n.next.refcnt immediately since it can double-decrement // n.next.refcnt. // We decrement n.next.refcnt in Node.alloc(), where we know that only a single thread is // accessing the node. Node.release(n); }
static Node alloc() { final int threshold = 2; int tryCnt = 0; while (true) { tryCnt++; Node n = pool.poll(); if (n == null) { return new Node(); } else { if (n.getRefCnt() > 0) { pool.add(n); if (tryCnt <= threshold) continue; else return new Node(); } else { if (n.next != null) n.next.decRefCnt(); n.init(); return n; } } } }
public void clear() { for (Node n = head.next; n != null; n = n.next) { Node.release(n); assert n.getRefCnt() == 0; n.clearEntry(); Node.free(n); } head.next = null; }
int findEndIndex(Node n, double key, boolean inclusive) { boolean exclusive = !inclusive; if (key < n.first()) return -1; if (key >= n.last()) { if (key == n.last() && exclusive) return n.len() - 2; return n.len() - 1; } int pos = n.findKeyIndex(key); if (pos < 0) pos = -(pos + 1); else if (!inclusive) pos--; if (pos == n.len()) pos--; return pos; }
public void doSanityCheck() { int count = 0; double prevKey = Double.MIN_VALUE; Node n = safeNext(head); while (n != null) { count++; if (!skipListMap.containsKey(n.first())) { System.out.println("Node n not in skiplistMap:" + n.first()); n._print(); } if (n.first() < prevKey) { System.out.println("Error in ordering, prevKey:" + prevKey + ", n:" + n); } prevKey = n.first(); Node next = safeNext(n); release(n); n = next; } if (skipListMap.size() != count) { System.out.println("skipListMap.size:" + skipListMap.size() + ", count:" + count); } }
/* * Methods for fetching next pointer of a node, preventing the node from being reclaimed by the pool. * release() simply decrements the reference count of the node. */ Node safeNext(Node b) { return Node.safeNext(b); }
public void found(Node n) { n.incRefCnt(); }
void release(Node b, Node n) { Node.release(n); Node.release(b); }
// only used by putAtomic and PutAtomicIfAbsent. Inside synchronized(b) and synchronized(this). private void putAtomicReally( Node b, int pos, double key, Object value, ConcurrentDoubleOrderedListMap orderedMap) { int len = len(); if (len + 1 <= keys.length) { if (pos == len) { // in-place append in the current node keys[pos] = key; vals[pos] = value; length = len + 1; if (pos == 0) { orderedMap.skipListMap.put(keys[0], this); } } else { // copied to a new node, replacing the current node mark(); Node n = Node.alloc(); n.next = this.next; if (next != null) next.incRefCnt(); double[] nkeys = n.keys; Object[] nvals = n.vals; n.length = len + 1; nodeCopy(keys, vals, 0, nkeys, nvals, 0, pos); nkeys[pos] = key; nvals[pos] = value; nodeCopy(keys, vals, pos, nkeys, nvals, pos + 1, len - pos); orderedMap.skipListMap.put(nkeys[0], n); b.casNext(this, n); // should always succeed. if (pos == 0) { orderedMap.skipListMap.remove(keys[0], this); } release(this); free(this); } } else { // requires 2 new nodes, to replace the current node mark(); Node n1 = Node.alloc(); double[] n1keys = n1.keys; Object[] n1vals = n1.vals; Node n2 = Node.alloc(); double[] n2keys = n2.keys; Object[] n2vals = n2.vals; int l1 = len / 2, l2 = len - l1; if (pos < l1) { // key, value stored in n1 n1.length = l1 + 1; n2.length = l2; nodeCopy(keys, vals, 0, n1keys, n1vals, 0, pos); n1keys[pos] = key; n1vals[pos] = value; nodeCopy(keys, vals, pos, n1keys, n1vals, pos + 1, l1 - pos); nodeCopy(keys, vals, l1, n2keys, n2vals, 0, l2); n1.next = n2; n2.next = this.next; if (next != null) next.incRefCnt(); orderedMap.skipListMap.put(n1keys[0], n1); orderedMap.skipListMap.put(n2keys[0], n2); b.casNext(this, n1); // should always succeed. if (pos == 0) { orderedMap.skipListMap.remove(keys[0], this); } release(this); free(this); } else { // key,value is stored in n2 n1.length = l1; n2.length = l2 + 1; int newpos = pos - l1; nodeCopy(keys, vals, 0, n1keys, n1vals, 0, l1); nodeCopy(keys, vals, l1, n2keys, n2vals, 0, newpos); n2keys[newpos] = key; n2vals[newpos] = value; nodeCopy(keys, vals, pos, n2keys, n2vals, newpos + 1, l2 - newpos); n1.next = n2; n2.next = this.next; if (next != null) next.incRefCnt(); orderedMap.skipListMap.put(n1keys[0], n1); orderedMap.skipListMap.put(n2keys[0], n2); b.casNext(this, n1); // should always succeed. release(this); free(this); } } }
void release(Node b, Node n, Node f) { Node.release(f); Node.release(n); Node.release(b); }
public void _print() { for (Node n = head; n != null; n = n.next) { n._print(); } }
// no concurrent read/write access is assumed private void putReally( int pos, double key, Object value, ConcurrentDoubleOrderedListMap orderedMap) { int len = len(); if (len + 1 <= keys.length) { // inserted in the current node nodeCopy(keys, vals, pos, keys, vals, pos + 1, len - pos); keys[pos] = key; vals[pos] = value; length = len + 1; if (pos == 0) { orderedMap.skipListMap.put(keys[0], this); if (len != 0) orderedMap.skipListMap.remove(keys[1], this); } } else if (emptySlotInNextTwo()) { if (pos == len) { next.put(key, value, orderedMap); return; } next.put(keys[len - 1], vals[len - 1], orderedMap); nodeCopy(keys, vals, pos, keys, vals, pos + 1, len - pos - 1); keys[pos] = key; vals[pos] = value; if (pos == 0) { orderedMap.skipListMap.remove(keys[1], this); orderedMap.skipListMap.put(keys[0], this); } } else { // current node is full, so requires a new node Node n = Node.alloc(); double[] nkeys = n.keys; Object[] nvals = n.vals; int l1 = len / 2, l2 = len - l1; if (next == null && pos == len) { // this is the last node, simply add to the new node. nkeys[0] = key; nvals[0] = value; n.length = 1; orderedMap.skipListMap.put(nkeys[0], n); } else if (pos < l1) { // key,value is stored in the current node length = l1 + 1; n.length = l2; nodeCopy(keys, vals, l1, nkeys, nvals, 0, l2); nodeCopy(keys, vals, pos, keys, vals, pos + 1, l1 - pos); keys[pos] = key; vals[pos] = value; if (pos == 0) { orderedMap.skipListMap.remove(keys[1]); orderedMap.skipListMap.put(keys[0], this); } orderedMap.skipListMap.put(nkeys[0], n); } else { // key,value is stored in the new node length = l1; n.length = l2 + 1; int newpos = pos - l1; nodeCopy(keys, vals, l1, nkeys, nvals, 0, newpos); nkeys[newpos] = key; nvals[newpos] = value; nodeCopy(keys, vals, pos, nkeys, nvals, newpos + 1, l2 - newpos); orderedMap.skipListMap.put(nkeys[0], n); } n.next = this.next; this.next = n; } }
static void release(Node n) { n.decRefCnt(); }
void release(Node n) { Node.release(n); }
V putAtomic(double key, V value, boolean onlyIfAbsent) { if (value == null) throw new NullPointerException(); for (; ; ) { Node b = skipListMap.findGrandPredecessor(key, refCntCallback); if (b == null) b = head; if (b != head && b.first() <= key && key <= b.last()) { release(b); continue; } assert b == head || b.last() < key; Node n = safeNext(b); if (n == null) { if (b.appendNewAtomic(key, value, this)) { release(b); return null; } else { release(b); continue; } } else if (n.first() <= key && key <= n.last()) { Object val = n.putAtomic(key, value, b, onlyIfAbsent, this); release(b, n); if (val == Retry) continue; return (V) val; } Node f = safeNext(n); if (f == null) { Object val = n.putAtomic(key, value, b, onlyIfAbsent, this); release(b, n); if (val == Retry) continue; return (V) val; } else { if (f.first() == key) { Object val = f.putAtomic(key, value, n, onlyIfAbsent, this); release(b, n, f); if (val == Retry) continue; return (V) val; } else { if (key > f.first()) { // inconsistent read, retry release(b, n, f); continue; } Object val = n.putAtomic(key, value, b, onlyIfAbsent, this); release(b, n, f); if (val == Retry) continue; return (V) val; } } } }
// no concurrent read/write is assumed public V put(double key, V value) { if (value == null) throw new NullPointerException(); // not incrementing b's refcnt, so need not release it. Node b = skipListMap.findGrandPredecessor(key); if (b == null) b = head; Node n = b.next; if (n == null) { n = Node.alloc(); n.put(key, value, this); n.next = null; b.next = n; return null; } Node f = n.next; if (f == null) { // put (key,value) into node n Object val = n.put(key, value, this); return (V) val; } else { assert f.len() > 0 && f.first() >= key; if (f.first() == key) { Object val = f.put(key, value, this); return (V) val; } else { Object val = n.put(key, value, this); return (V) val; } } }
V doReplace(double key, V old, V value) { for (; ; ) { Node b = skipListMap.findGrandPredecessor(key, refCntCallback); if (b == null) b = head; if (b != head && b.first() <= key && key <= b.last()) { // can happen if there's inconsistency between skipListMap and orderedMap(this). // The inconsistency can happen from Node.putAtomicReally(), // where it adds to, and removes from skiplistMap. Object val = b.replace(key, old, value); release(b); if (val == Retry) continue; return (V) val; } assert b == head || b.last() < key; Node n = safeNext(b); release(b); if (n == null) { return null; } else if (n.first() <= key && key <= n.last()) { Object val = n.replace(key, old, value); release(n); if (val == Retry) continue; return (V) val; } Node f = safeNext(n); release(n); if (f == null) { return null; } else { if (isInconsistentNextNode(f, key)) { release(f); continue; } Object val = f.replace(key, old, value); release(f); if (val == Retry) continue; return (V) val; } } }