project 4
This commit is contained in:
@@ -1,25 +1,418 @@
|
|||||||
package CC;
|
package CC;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class CC
|
import java.util.List;
|
||||||
{
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
enum LockState {
|
||||||
|
NONE,
|
||||||
|
SHARED,
|
||||||
|
EXCLUSIVE
|
||||||
|
}
|
||||||
|
|
||||||
|
class Lock {
|
||||||
|
|
||||||
|
private ArrayList<Integer> transactionIds;
|
||||||
|
private LockState type;
|
||||||
|
private int noReads;
|
||||||
|
|
||||||
|
public Lock() {
|
||||||
|
this.transactionIds = new ArrayList<Integer>();
|
||||||
|
this.noReads = 0;
|
||||||
|
this.type = LockState.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<Integer> getTransactionIds() {
|
||||||
|
return transactionIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTransactionId(int transactionId) {
|
||||||
|
if (!transactionIds.contains(Integer.valueOf(transactionId))) {
|
||||||
|
transactionIds.add(transactionId);
|
||||||
|
noReads++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public LockState getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(LockState type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNoReads() {
|
||||||
|
return noReads;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasLock(int transactionId) {
|
||||||
|
return transactionIds.contains(Integer.valueOf(transactionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void release(int releaseTransactionId) {
|
||||||
|
transactionIds.remove(Integer.valueOf(releaseTransactionId));
|
||||||
|
noReads--;
|
||||||
|
|
||||||
|
if (noReads == 0) {
|
||||||
|
this.type = LockState.NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LockTable {
|
||||||
|
private Lock[] locks;
|
||||||
|
private static final int NUM_RECORDS = 10;
|
||||||
|
|
||||||
|
public LockTable() {
|
||||||
|
this.locks = new Lock[NUM_RECORDS];
|
||||||
|
// initialize the locktable
|
||||||
|
for (int recordId = 0; recordId < NUM_RECORDS; recordId++) {
|
||||||
|
locks[recordId] = new Lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Lock getLock(int recordId) {
|
||||||
|
if (recordId >= 0 && recordId < locks.length) {
|
||||||
|
return locks[recordId];
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Invalid recordId: " + recordId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void releaseLocksForTransaction(int[] recordIds, int transactionId) {
|
||||||
|
for (int recordId : recordIds) {
|
||||||
|
if (recordId >= 0 && recordId < locks.length) {
|
||||||
|
locks[recordId].release(transactionId);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Invalid recordId: " + recordId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Operation {
|
||||||
|
int getRecordId();
|
||||||
|
|
||||||
|
int getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
class WriteOp implements Operation {
|
||||||
|
private int recordId;
|
||||||
|
private int value;
|
||||||
|
|
||||||
|
public WriteOp(int recordId, int value) {
|
||||||
|
this.recordId = recordId;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRecordId() {
|
||||||
|
return recordId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReadOp implements Operation {
|
||||||
|
private int recordId;
|
||||||
|
|
||||||
|
public ReadOp(int recordId) {
|
||||||
|
this.recordId = recordId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRecordId() {
|
||||||
|
return recordId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CommitOp implements Operation {
|
||||||
|
public int getRecordId() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Transaction {
|
||||||
|
private int currentOperationIndex;
|
||||||
|
private Operation[] operations;
|
||||||
|
private int transactionId;
|
||||||
|
private ArrayList<Integer> recordLocks;
|
||||||
|
private int prevTimestamp;
|
||||||
|
private boolean aborted;
|
||||||
|
|
||||||
|
public Transaction(String[] operations, int transactionId) {
|
||||||
|
this.currentOperationIndex = 0;
|
||||||
|
this.operations = new Operation[operations.length];
|
||||||
|
this.transactionId = transactionId;
|
||||||
|
this.recordLocks = new ArrayList<Integer>();
|
||||||
|
this.prevTimestamp = -1;
|
||||||
|
this.aborted = false;
|
||||||
|
|
||||||
|
int OpIndex = 0;
|
||||||
|
|
||||||
|
for (String operation : operations) {
|
||||||
|
if (operation.charAt(0) == 'W') {
|
||||||
|
this.operations[OpIndex] = new WriteOp(Character.getNumericValue(operation.charAt(2)),
|
||||||
|
Character.getNumericValue(operation.charAt(4)));
|
||||||
|
} else if (operation.charAt(0) == 'R') {
|
||||||
|
this.operations[OpIndex] = new ReadOp(Character.getNumericValue(operation.charAt(2)));
|
||||||
|
} else {
|
||||||
|
this.operations[OpIndex] = new CommitOp();
|
||||||
|
}
|
||||||
|
OpIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCurrentOperationIndex() {
|
||||||
|
return currentOperationIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTransactionId() {
|
||||||
|
return transactionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentOperationIndex(int currentOperationIndex) {
|
||||||
|
this.currentOperationIndex = currentOperationIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Operation[] getOperations() {
|
||||||
|
return operations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNextOperation() {
|
||||||
|
return (currentOperationIndex < operations.length) && !aborted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int doNextOperation(LockTable lockTable, int[] database, ArrayList<String[]> systemLog, int timestamp,
|
||||||
|
LinkedList<Integer>[] adj) {
|
||||||
|
if (hasNextOperation() && !aborted) {
|
||||||
|
Operation nextOp = operations[currentOperationIndex];
|
||||||
|
Lock nextLock = null;
|
||||||
|
if (!(nextOp instanceof CommitOp)) {
|
||||||
|
nextLock = lockTable.getLock(nextOp.getRecordId());
|
||||||
|
}
|
||||||
|
currentOperationIndex++;
|
||||||
|
|
||||||
|
if (nextOp instanceof ReadOp) {
|
||||||
|
if (!(nextLock.hasLock(transactionId))) {
|
||||||
|
// The current transaction doesn't already have the lock.
|
||||||
|
if (nextLock.getType() == LockState.EXCLUSIVE) {
|
||||||
|
currentOperationIndex--;
|
||||||
|
for (int adjTransactionId : nextLock.getTransactionIds()) {
|
||||||
|
if (adjTransactionId != transactionId
|
||||||
|
&& !adj[transactionId].contains(Integer.valueOf(adjTransactionId))) {
|
||||||
|
adj[transactionId].add(adjTransactionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return timestamp;
|
||||||
|
// The lock on the record is exclusive.
|
||||||
|
} else {
|
||||||
|
nextLock.setType(LockState.SHARED);
|
||||||
|
nextLock.addTransactionId(transactionId);
|
||||||
|
recordLocks.add(nextOp.getRecordId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String[] log = { "R", Integer.toString(timestamp), "T" + Integer.toString(transactionId),
|
||||||
|
Integer.toString(nextOp.getRecordId()), Integer.toString(database[nextOp.getRecordId()]),
|
||||||
|
Integer.toString(prevTimestamp) };
|
||||||
|
systemLog.add(log);
|
||||||
|
|
||||||
|
} else if (nextOp instanceof WriteOp) {
|
||||||
|
int prevValue = database[nextOp.getRecordId()];
|
||||||
|
if ((nextLock.hasLock(transactionId) && nextLock.getNoReads() == 1)
|
||||||
|
|| nextLock.getType() == LockState.NONE) {
|
||||||
|
nextLock.setType(LockState.EXCLUSIVE);
|
||||||
|
database[nextOp.getRecordId()] = nextOp.getValue();
|
||||||
|
nextLock.addTransactionId(transactionId);
|
||||||
|
|
||||||
|
if (!recordLocks.contains(Integer.valueOf(nextOp.getRecordId()))) {
|
||||||
|
recordLocks.add(nextOp.getRecordId());
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
currentOperationIndex--;
|
||||||
|
for (int adjTransactionId : nextLock.getTransactionIds()) {
|
||||||
|
if (adjTransactionId != transactionId
|
||||||
|
&& !adj[transactionId].contains(Integer.valueOf(adjTransactionId))) {
|
||||||
|
adj[transactionId].add(adjTransactionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
String[] log = { "W", Integer.toString(timestamp), "T" + Integer.toString(transactionId),
|
||||||
|
Integer.toString(nextOp.getRecordId()), Integer.toString(prevValue),
|
||||||
|
Integer.toString(database[nextOp.getRecordId()]),
|
||||||
|
Integer.toString(prevTimestamp) };
|
||||||
|
systemLog.add(log);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for (int recordLock : recordLocks) {
|
||||||
|
lockTable.getLock(recordLock).release(transactionId);
|
||||||
|
}
|
||||||
|
recordLocks = new ArrayList<Integer>();
|
||||||
|
|
||||||
|
String[] log = { "C", Integer.toString(timestamp), "T" + Integer.toString(transactionId),
|
||||||
|
Integer.toString(prevTimestamp) };
|
||||||
|
systemLog.add(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
prevTimestamp = timestamp;
|
||||||
|
timestamp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
currentOperationIndex = 0;
|
||||||
|
recordLocks = new ArrayList<Integer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int abortTransaction(LockTable lockTable, int[] database, ArrayList<String[]> systemLog, int timestamp,
|
||||||
|
LinkedList<Integer>[] adj) {
|
||||||
|
aborted = true;
|
||||||
|
int pastTransaction = prevTimestamp;
|
||||||
|
|
||||||
|
do {
|
||||||
|
String[] lastLog = systemLog.get(pastTransaction);
|
||||||
|
if (lastLog[0] == "W") {
|
||||||
|
database[Integer.parseInt(lastLog[3])] = Integer.parseInt(lastLog[4]);
|
||||||
|
}
|
||||||
|
pastTransaction = Integer.parseInt(lastLog[lastLog.length - 1]);
|
||||||
|
} while (pastTransaction != -1);
|
||||||
|
|
||||||
|
for (int recordLock : recordLocks) {
|
||||||
|
lockTable.getLock(recordLock).release(transactionId);
|
||||||
|
}
|
||||||
|
recordLocks = new ArrayList<Integer>();
|
||||||
|
|
||||||
|
adj[transactionId] = new LinkedList<Integer>();
|
||||||
|
|
||||||
|
String[] log = { "A", Integer.toString(timestamp), "T" + Integer.toString(transactionId),
|
||||||
|
Integer.toString(prevTimestamp) };
|
||||||
|
systemLog.add(log);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CC {
|
||||||
|
private static final int NUM_RECORDS = 10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notes:
|
* Notes:
|
||||||
* - Execute all given transactions, using locking.
|
* - Execute all given transactions, using locking.
|
||||||
* - Each element in the transactions List represents all operations performed by one transaction, in order.
|
* - Each element in the transactions List represents all operations performed
|
||||||
* - No operation in a transaction can be executed out of order, but operations may be interleaved with other
|
* by one transaction, in order.
|
||||||
* transactions if allowed by the locking.
|
* - No operation in a transaction can be executed out of order, but operations
|
||||||
* - The index of the transaction in the list is equivalent to the transaction ID.
|
* may be interleaved with other
|
||||||
* - Print the log to the console at the end of the method.
|
* transactions if allowed by the locking.
|
||||||
* - Return the new db state after executing the transactions.
|
* - The index of the transaction in the list is equivalent to the transaction
|
||||||
* @param db the initial status of the db
|
* ID.
|
||||||
* @param transactions the schedule, which basically is a {@link List} of transactions.
|
* - Print the log to the console at the end of the method.
|
||||||
|
* - Return the new db state after executing the transactions.
|
||||||
|
*
|
||||||
|
* @param db the initial status of the db
|
||||||
|
* @param transactions the schedule, which basically is a {@link List} of
|
||||||
|
* transactions.
|
||||||
* @return the final status of the db
|
* @return the final status of the db
|
||||||
*/
|
*/
|
||||||
public static int[] executeSchedule(int[] db, List<String> transactions)
|
public static int[] executeSchedule(int[] db, List<String> transactions) {
|
||||||
{
|
LockTable lockTable = new LockTable();
|
||||||
//TODO
|
Queue<Transaction> transactionQueue = new LinkedList<>();
|
||||||
return null ;
|
Transaction[] transactionsArray = new Transaction[transactions.size() + 1];
|
||||||
|
int newTransactionId = 1;
|
||||||
|
int[] database = new int[NUM_RECORDS];
|
||||||
|
ArrayList<String[]> systemLog = new ArrayList<>();
|
||||||
|
int timestamp = 0;
|
||||||
|
|
||||||
|
LinkedList<Integer>[] adj;
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_RECORDS; i++) {
|
||||||
|
database[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String transaction : transactions) {
|
||||||
|
Transaction newTransaction = new Transaction(transaction.split(";"), newTransactionId);
|
||||||
|
transactionQueue.add(newTransaction);
|
||||||
|
transactionsArray[newTransactionId] = newTransaction;
|
||||||
|
newTransactionId++;
|
||||||
|
}
|
||||||
|
adj = new LinkedList[transactions.size() + 1];
|
||||||
|
for (int i = 0; i < transactions.size() + 1; ++i) {
|
||||||
|
adj[i] = new LinkedList<Integer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!transactionQueue.isEmpty()) {
|
||||||
|
Transaction curTransaction = transactionQueue.remove();
|
||||||
|
if (curTransaction.hasNextOperation()) {
|
||||||
|
timestamp = curTransaction.doNextOperation(lockTable, database, systemLog, timestamp, adj);
|
||||||
|
|
||||||
|
// Here is where we check if there are deadlocks. We run DFS.
|
||||||
|
boolean[] visited = new boolean[transactions.size() + 1];
|
||||||
|
int[] predecessor = new int[transactions.size() + 1];
|
||||||
|
ArrayList<Integer> nodesInCycle = DFS(curTransaction.getTransactionId(), visited, adj, predecessor);
|
||||||
|
|
||||||
|
if (nodesInCycle != null) {
|
||||||
|
Collections.sort(nodesInCycle);
|
||||||
|
transactionsArray[nodesInCycle.get(nodesInCycle.size() - 1)].abortTransaction(lockTable, database,
|
||||||
|
systemLog, timestamp,
|
||||||
|
adj);
|
||||||
|
timestamp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
transactionQueue.add(curTransaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String[] log : systemLog) {
|
||||||
|
StringBuilder sb = new StringBuilder(log[0] + ":" + log[1]);
|
||||||
|
for (int k = 2; k < log.length; k++) {
|
||||||
|
sb.append("," + log[k]);
|
||||||
|
}
|
||||||
|
System.out.println(sb.toString());
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
return database;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<Integer> DFS(int v, boolean visited[], LinkedList<Integer> adj[], int predecessor[]) {
|
||||||
|
visited[v] = true;
|
||||||
|
|
||||||
|
Iterator<Integer> i = adj[v].listIterator();
|
||||||
|
while (i.hasNext()) {
|
||||||
|
int n = i.next();
|
||||||
|
if (!visited[n]) {
|
||||||
|
predecessor[n] = v;
|
||||||
|
return DFS(n, visited, adj, predecessor);
|
||||||
|
} else {
|
||||||
|
return returnNodesInCycle(predecessor, v, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<Integer> returnNodesInCycle(int[] predecessor, int parent, int start) {
|
||||||
|
ArrayList<Integer> nodesInCycle = new ArrayList<>();
|
||||||
|
nodesInCycle.add(parent);
|
||||||
|
nodesInCycle.add(start);
|
||||||
|
|
||||||
|
while (predecessor[start] != parent && predecessor[start] != 0) {
|
||||||
|
nodesInCycle.add(predecessor[start]);
|
||||||
|
start = predecessor[start];
|
||||||
|
}
|
||||||
|
return nodesInCycle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user