예제 #1
0
  // returns the most recent val
  Object lock(Ref ref) {
    // can't upgrade readLock, so release it
    releaseIfEnsured(ref);

    boolean unlocked = true;
    try {
      tryWriteLock(ref);
      unlocked = false;

      if (ref.tvals != null && ref.tvals.point > readPoint) throw retryex;
      Info refinfo = ref.tinfo;

      // write lock conflict
      if (refinfo != null && refinfo != info && refinfo.running()) {
        if (!barge(refinfo)) {
          ref.lock.writeLock().unlock();
          unlocked = true;
          return blockAndBail(refinfo);
        }
      }
      ref.tinfo = info;
      return ref.tvals == null ? null : ref.tvals.val;
    } finally {
      if (!unlocked) ref.lock.writeLock().unlock();
    }
  }
예제 #2
0
 Object doSet(Ref ref, Object val) {
   if (!info.running()) throw retryex;
   if (commutes.containsKey(ref)) throw new IllegalStateException("Can't set after commute");
   if (!sets.contains(ref)) {
     sets.add(ref);
     lock(ref);
   }
   vals.put(ref, val);
   return val;
 }
예제 #3
0
  void doEnsure(Ref ref) {
    if (!info.running()) throw retryex;
    if (ensures.contains(ref)) return;
    ref.lock.readLock().lock();

    // someone completed a write after our snapshot
    if (ref.tvals != null && ref.tvals.point > readPoint) {
      ref.lock.readLock().unlock();
      throw retryex;
    }

    Info refinfo = ref.tinfo;

    // writer exists
    if (refinfo != null && refinfo.running()) {
      ref.lock.readLock().unlock();

      if (refinfo != info) // not us, ensure is doomed
      {
        blockAndBail(refinfo);
      }
    } else ensures.add(ref);
  }
예제 #4
0
 Object doGet(Ref ref) {
   if (!info.running()) throw retryex;
   if (vals.containsKey(ref)) return vals.get(ref);
   try {
     ref.lock.readLock().lock();
     if (ref.tvals == null) throw new IllegalStateException(ref.toString() + " is unbound.");
     Ref.TVal ver = ref.tvals;
     do {
       if (ver.point <= readPoint) return ver.val;
     } while ((ver = ver.prior) != ref.tvals);
   } finally {
     ref.lock.readLock().unlock();
   }
   // no version of val precedes the read point
   ref.faults.incrementAndGet();
   throw retryex;
 }
예제 #5
0
 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;
 }
예제 #6
0
  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;
  }