@Override public boolean commit() { try { if (!writeSet.isEmpty()) { int v = status.get(); int s = v & STATUS_MASK; if (s == TX_ACTIVE && status.compareAndSet(v, v + (TX_COMMITTING - TX_ACTIVE))) { long newClock = clock.incrementAndGet(); if (newClock != startTime.get() + 1 && !readSet.validate(this, id)) { rollback0(); return false; } // Write values and release locks writeSet.commit(newClock); status.set(v + (TX_COMMITTED - TX_ACTIVE)); } else { // We have been killed: wait for our locks to have been released while (s != TX_ABORTED) s = status.get() & STATUS_MASK; return false; } } else { // No need to set status to COMMITTED (we cannot be killed with an empty write set) } attempts = 0; return true; } finally { if (irrevocableState) { irrevocableState = false; irrevocableAccessLock.writeLock().unlock(); } else { irrevocableAccessLock.readLock().unlock(); } } }
private boolean extend() { long now = clock.get(); if (readSet.validate(this, id)) { endTime = now; return true; } return false; }
private boolean onReadAccess(Object obj, long field, Type type) { if (vr) { // Visible read if (readLock == LockTable.LOCKED_WRITE) { // We already own that lock in write mode WriteFieldAccess w = writeSet.get(readHash, obj, field); if (w == null) return false; readValue = w.getValue(); return true; } else { // We already own that lock in read mode return false; } } else { // Invisible read if ((status.get() & STATUS_MASK) != TX_ACTIVE) { // We have been killed: abort throw KILLED_EXCEPTION; } if (readLock == LockTable.LOCKED_WRITE) { // We already own that lock in write mode WriteFieldAccess w = writeSet.get(readHash, obj, field); if (w == null) return false; readValue = w.getValue(); return true; } else if (readLock == LockTable.LOCKED_READ) { // We already own that lock in read mode return false; } boolean b = false; while (true) { while (readLock <= endTime) { // Re-read timestamp (check for race) long lock = LockTable.checkLock(this, readHash, id); if (lock != readLock) { readLock = lock; readValue = Field.getValue(obj, field, type); b = true; continue; } // We have read a valid value (in snapshot) if (readWriteHint) { // Save to read set readSet.add(obj, field, readHash, lock); } return b; } // Try to extend snapshot if (!(readWriteHint && extend())) { throw EXTEND_FAILURE_EXCEPTION; } } } }
@Override public void init(int blockId, String metainf) { readSet.clear(); writeSet.clear(); // Lock according to the transaction irrevocable state if (irrevocableState) irrevocableAccessLock.writeLock().lock(); else irrevocableAccessLock.readLock().lock(); endTime = clock.get(); startTime.set(endTime); status.set(((status.get() + (1 << STATUS_BITS)) & ~STATUS_MASK) | TX_ACTIVE); if (RO_HINT) { atomicBlockId = blockId; readWriteHint = readWriteMarkers.get(atomicBlockId); } attempts++; vr = (VR_THRESHOLD > 0 && VR_THRESHOLD <= attempts); }
private void onWriteAccess(Object obj, long field, Object value, Type type) { if (!readWriteHint) { // Change hint to read-write readWriteMarkers.insert(atomicBlockId, true); throw READ_ONLY_FAILURE_EXCEPTION; } int hash = LockTable.hash(obj, field); // Lock entry in write mode (might throw an exception) long timestamp = LockTable.lock(this, hash, id, true); synchronized (writeSet) { // Mutual exclusion on write set to allow other transaction to drop locks if ((status.get() & STATUS_MASK) != TX_ACTIVE) { // We have been killed if (timestamp >= 0) { // Drop lock we just acquired (not in write set) LockTable.setAndReleaseLock(hash, timestamp); } // Abort throw KILLED_EXCEPTION; } if (timestamp < 0) { // We already own that lock writeSet.appendWrite(hash, obj, field, value, type); } else { // Add to write set if (timestamp > endTime) { // Handle write-after-read if (readSet.contains(obj, field)) { // Abort LockTable.setAndReleaseLock(hash, timestamp); throw WRITE_FAILURE_EXCEPTION; } // We delay validation until later (although we could already validate once here) } // Add to write set writeSet.addWrite(hash, obj, field, value, type, timestamp); } } }