private boolean step() { if (next != NONE) return true; while (next == NONE) { if (buffer.isEmpty()) { if (completed) { return false; } else if (sourceIter.hasNext()) { Object iter = null; if (multi) iter = xf.applyTo(RT.cons(null, sourceIter.next())); else iter = xf.invoke(null, sourceIter.next()); if (RT.isReduced(iter)) { xf.invoke(null); completed = true; } } else { xf.invoke(null); completed = true; } } else { next = buffer.remove(); } } return true; }
Object doCommute(Ref ref, IFn fn, ISeq args) { if (!info.running()) throw retryex; if (!vals.containsKey(ref)) { Object val = null; try { ref.lock.readLock().lock(); val = ref.tvals == null ? null : ref.tvals.val; } finally { ref.lock.readLock().unlock(); } vals.put(ref, val); } ArrayList<CFn> fns = commutes.get(ref); if (fns == null) commutes.put(ref, fns = new ArrayList<CFn>()); fns.add(new CFn(fn, args)); Object ret = fn.applyTo(RT.cons(vals.get(ref), args)); vals.put(ref, ret); return ret; }
public ISeq cons(Object o) { return RT.cons(o, seq()); }
Object run(Callable fn) throws Exception { boolean done = false; Object ret = null; ArrayList<Ref> locked = new ArrayList<Ref>(); ArrayList<Notify> notify = new ArrayList<Notify>(); for (int i = 0; !done && i < RETRY_LIMIT; i++) { try { getReadPoint(); if (i == 0) { startPoint = readPoint; startTime = System.nanoTime(); } info = new Info(RUNNING, startPoint); ret = fn.call(); // make sure no one has killed us before this point, and can't from now on if (info.status.compareAndSet(RUNNING, COMMITTING)) { for (Map.Entry<Ref, ArrayList<CFn>> e : commutes.entrySet()) { Ref ref = e.getKey(); if (sets.contains(ref)) continue; boolean wasEnsured = ensures.contains(ref); // can't upgrade readLock, so release it releaseIfEnsured(ref); tryWriteLock(ref); locked.add(ref); if (wasEnsured && ref.tvals != null && ref.tvals.point > readPoint) throw retryex; Info refinfo = ref.tinfo; if (refinfo != null && refinfo != info && refinfo.running()) { if (!barge(refinfo)) throw retryex; } Object val = ref.tvals == null ? null : ref.tvals.val; vals.put(ref, val); for (CFn f : e.getValue()) { vals.put(ref, f.fn.applyTo(RT.cons(vals.get(ref), f.args))); } } for (Ref ref : sets) { tryWriteLock(ref); locked.add(ref); } // validate and enqueue notifications for (Map.Entry<Ref, Object> e : vals.entrySet()) { Ref ref = e.getKey(); ref.validate(ref.getValidator(), e.getValue()); } // at this point, all values calced, all refs to be written locked // no more client code to be called long commitPoint = getCommitPoint(); for (Map.Entry<Ref, Object> e : vals.entrySet()) { Ref ref = e.getKey(); Object oldval = ref.tvals == null ? null : ref.tvals.val; Object newval = e.getValue(); int hcount = ref.histCount(); if (ref.tvals == null) { ref.tvals = new Ref.TVal(newval, commitPoint); } else if ((ref.faults.get() > 0 && hcount < ref.maxHistory) || hcount < ref.minHistory) { ref.tvals = new Ref.TVal(newval, commitPoint, ref.tvals); ref.faults.set(0); } else { ref.tvals = ref.tvals.next; ref.tvals.val = newval; ref.tvals.point = commitPoint; } if (ref.getWatches().count() > 0) notify.add(new Notify(ref, oldval, newval)); } done = true; info.status.set(COMMITTED); } } catch (RetryEx retry) { // eat this so we retry rather than fall out } finally { for (int k = locked.size() - 1; k >= 0; --k) { locked.get(k).lock.writeLock().unlock(); } locked.clear(); for (Ref r : ensures) { r.lock.readLock().unlock(); } ensures.clear(); stop(done ? COMMITTED : RETRY); try { if (done) // re-dispatch out of transaction { for (Notify n : notify) { n.ref.notifyWatches(n.oldval, n.newval); } for (Agent.Action action : actions) { Agent.dispatchAction(action); } } } finally { notify.clear(); actions.clear(); } } } if (!done) throw Util.runtimeException("Transaction failed after reaching retry limit"); return ret; }