Package org.deuce.transaction.lsacm

Source Code of org.deuce.transaction.lsacm.Context

package org.deuce.transaction.lsacm;

import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.ConcurrentHashMap;

import org.deuce.transaction.TransactionException;
import org.deuce.transaction.lsacm.field.Field;
import org.deuce.transaction.lsacm.field.Field.Type;
import org.deuce.transaction.lsacm.field.WriteFieldAccess;
import org.deuce.transaction.lsacm.ReadSet;
import org.deuce.transaction.lsacm.WriteSet;
import org.deuce.transaction.lsacm.ContentionManager;
import org.deuce.transaction.lsacm.ContentionManager.ConflictType;
import org.deuce.transaction.util.BooleanArrayList;
import org.deuce.transform.Exclude;

import edu.vt.realtime.Timespec;

/**
* LSA implementation
*
* @author Pascal Felber
*/
@Exclude
final public class Context extends org.deuce.transaction.AbstractContext {

  final private static int TX_IDLE = 0;
  final private static int TX_ACTIVE = 1;
  final private static int TX_COMMITTING = 2;
  final private static int TX_ABORTING = 3;
  final private static int TX_COMMITTED = 4;
  final private static int TX_ABORTED = 5;

  final private static int STATUS_BITS = 3;
  final private static int STATUS_MASK = (1 << STATUS_BITS) - 1;

  final private static TransactionException WRITE_FAILURE_EXCEPTION =
    new TransactionException("Fail on write (read previous version).");

  final private static TransactionException EXTEND_FAILURE_EXCEPTION =
    new TransactionException("Fail on extend.");

  final private static TransactionException READ_ONLY_FAILURE_EXCEPTION =
    new TransactionException("Fail on write (read-only hint was set).");

  final private static TransactionException KILLED_EXCEPTION =
    new TransactionException("Transaction has been killed.");

  final private static AtomicLong clock = new AtomicLong(0);
  final private static AtomicInteger threadID = new AtomicInteger(0);

  final private static Map<Integer, Context> threads = new ConcurrentHashMap<Integer, Context>();

  final private static boolean RO_HINT = Boolean.getBoolean("org.deuce.transaction.lsacm.rohint");

  final private static int VR_THRESHOLD = Integer.getInteger("org.deuce.transaction.lsacm.vr", 0);

  final private static ContentionManager cm;

  final private ReadSet readSet = new ReadSet(1024);
  final private WriteSet writeSet = new WriteSet(32);

  // Keep per-thread read-only hints (uses more memory but faster)
  final private BooleanArrayList readWriteMarkers = new BooleanArrayList();
  private boolean readWriteHint = true;
  private int atomicBlockId;

  private int readHash;
  private long readLock;
  private Object readValue;

  // Can be accessed (read) by other transaction during contention management
  private AtomicLong startTime;
  private long endTime;
  private int id;

  private int attempts;
  private boolean vr;

  private AtomicInteger status;

  static {
    // Set contention manager
    String s = System.getProperty("org.deuce.transaction.lsacm.cm");
    Object o = null;
    if (s != null) {
      try {
        o = Class.forName(s).newInstance();
      } catch (Exception e) {
        System.err.println("Cannot create contention manager: " + s);
      }
    }
    cm = (ContentionManager)o;
  }

  public Context() {
    // Unique identifier among active threads
    id = threadID.incrementAndGet();
    threads.put(id, this);
    attempts = 0;
    vr = false;
    startTime = new AtomicLong(0L);
    status = new AtomicInteger(TX_IDLE);
  }

  protected void finalize() throws Throwable {
    threads.remove(id);
    super.finalize();
  }

  public void init(int blockId) {
    readSet.clear();
    writeSet.clear();
    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);
  }

  public boolean commit() {
    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)) {
          rollback();
          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;
  }

  public boolean commit(Timespec deadline_ts, Timespec period_ts, int tid) {
    // dummy implementation to accomodate dtl2realtime
    return true;
  }
 
  public boolean abort() {
    // Proxy method implemented - For real implementation -come later - Sachin
    return true;
  }
 
  public boolean rollback() {
    if (!writeSet.isEmpty()) {
      int v = status.get();
      int s = v & STATUS_MASK;
      if (s == TX_COMMITTING) {
        // Release locks
        writeSet.rollback();
        status.set(v + (TX_ABORTED - TX_COMMITTING));
      } else if (s == TX_ACTIVE && status.compareAndSet(v, v + (TX_ABORTING - TX_ACTIVE))) {
        // Release locks
        writeSet.rollback();
        status.set(v + (TX_ABORTED - TX_ACTIVE));
      } else {
        // We have been killed: wait for our locks to have been released
        while (s != TX_ABORTED)
          s = status.get() & STATUS_MASK;
      }
    } else {
      // No need to set status to ABORTED (at that point we do not hold locks anymore)
    }
    return true;
  }

  public boolean conflict(int other, ConflictType type, int hash, long lock) {
    if (cm != null) {
      Context tx = threads.get(other);
      if (cm.arbitrate(this, tx, type) == ContentionManager.KILL_OTHER) {
        // We win
        synchronized (tx.writeSet) {
          // Mutual exclusion on write set to drop locks
          if (lock == LockTable.readLock(hash)) {
            // The other transaction still owns the lock
            kill(tx);
          }
        }
        return true;
      }
    }
    return false;
  }

  public static void kill(Context tx) {
    // Kill other transaction
    int v = tx.status.get();
    int s = v & STATUS_MASK;
    if (s == TX_ACTIVE && tx.status.compareAndSet(v, v + (TX_ABORTING - TX_ACTIVE))) {
      // Release locks
      tx.writeSet.rollback();
      tx.status.set(v + (TX_ABORTED - TX_ACTIVE));
    }
  }

  public long getStartTime() {
    return startTime.get();
  }

  public int getId() {
    return id;
  }

  public boolean isActive() {
    return (status.get() & STATUS_MASK) == TX_ACTIVE;
  }

  private boolean extend() {
    long now = clock.get();
    if (readSet.validate(this, id)) {
      endTime = now;
      return true;
    }
    return false;
  }

  public void beforeReadAccess(Object obj, long field) {
    if (vr) {
      readHash = LockTable.hash(obj, field);
      // Lock entry in read mode (might throw an exception)
      readLock = LockTable.lock(this, readHash, id, false);
      if (readLock >= 0) {
        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: drop lock we just acquired (not in write set)
            LockTable.setAndReleaseLock(readHash, readLock);
            // Abort
            throw KILLED_EXCEPTION;
          }
          // Add to write set (for being able to drop lock later)
          writeSet.addRead(readHash, obj, field, readLock);
        }
      }
    } else {
      readHash = LockTable.hash(obj, field);
      // Check if the field is locked (may throw an exception)
      readLock = LockTable.checkLock(this, readHash, id);
    }
  }

  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;
        }
      }
    }
  }

  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);
      }
    }
  }

  public Object onReadAccess(Object obj, Object value, long field) {
    return (onReadAccess(obj, field, Type.OBJECT) ? readValue : value);
  }

  public boolean onReadAccess(Object obj, boolean value, long field) {
    return (onReadAccess(obj, field, Type.BOOLEAN) ? (Boolean) readValue : value);
  }

  public byte onReadAccess(Object obj, byte value, long field) {
    return (onReadAccess(obj, field, Type.BYTE) ? ((Number) readValue).byteValue() : value);
  }

  public char onReadAccess(Object obj, char value, long field) {
    return (onReadAccess(obj, field, Type.CHAR) ? (Character) readValue : value);
  }

  public short onReadAccess(Object obj, short value, long field) {
    return (onReadAccess(obj, field, Type.SHORT) ? ((Number) readValue).shortValue() : value);
  }

  public int onReadAccess(Object obj, int value, long field) {
    return (onReadAccess(obj, field, Type.INT) ? ((Number) readValue).intValue() : value);
  }

  public long onReadAccess(Object obj, long value, long field) {
    return (onReadAccess(obj, field, Type.LONG) ? ((Number) readValue).longValue() : value);
  }

  public float onReadAccess(Object obj, float value, long field) {
    return (onReadAccess(obj, field, Type.FLOAT) ? ((Number) readValue).floatValue() : value);
  }

  public double onReadAccess(Object obj, double value, long field) {
    return (onReadAccess(obj, field, Type.DOUBLE) ? ((Number) readValue).doubleValue() : value);
  }

  public void onWriteAccess(Object obj, Object value, long field) {
    onWriteAccess(obj, field, value, Type.OBJECT);
  }

  public void onWriteAccess(Object obj, boolean value, long field) {
    onWriteAccess(obj, field, (Object) value, Type.BOOLEAN);
  }

  public void onWriteAccess(Object obj, byte value, long field) {
    onWriteAccess(obj, field, (Object) value, Type.BYTE);
  }

  public void onWriteAccess(Object obj, char value, long field) {
    onWriteAccess(obj, field, (Object) value, Type.CHAR);
  }

  public void onWriteAccess(Object obj, short value, long field) {
    onWriteAccess(obj, field, (Object) value, Type.SHORT);
  }

  public void onWriteAccess(Object obj, int value, long field) {
    onWriteAccess(obj, field, (Object) value, Type.INT);
  }

  public void onWriteAccess(Object obj, long value, long field) {
    onWriteAccess(obj, field, (Object) value, Type.LONG);
  }

  public void onWriteAccess(Object obj, float value, long field) {
    onWriteAccess(obj, field, (Object) value, Type.FLOAT);
  }

  public void onWriteAccess(Object obj, double value, long field) {
    onWriteAccess(obj, field, (Object) value, Type.DOUBLE);
  }

  @Override
  public boolean wakeupWaitingTasks(int tid) {
    // TODO Auto-generated method stub
    return false;
  }

  @Override
  public boolean abort(int tid) {
    // TODO Auto-generated method stub
    return false;
  }
}
TOP

Related Classes of org.deuce.transaction.lsacm.Context

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.