/**
* JSTM (http://xstm.net)
* Distributed under the Apache License Version 2.0
* Copyright xstm.net
*/
package jstm4gwt.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import jstm4gwt.misc.ConcurrentHashMap;
import jstm4gwt.misc.AtomicInteger;
import com.google.gwt.user.client.rpc.AsyncCallback;
import jstm4gwt.misc.Debug;
import jstm4gwt.misc.GWTAdapter;
import jstm4gwt.misc.Log;
final class TransactionHelper {
public static boolean ValidateOnce = true;
public static boolean ValidateOnceFailed;
public static boolean AbortRandom, AbortAlways;
public static final AtomicInteger ActiveVisitors = new AtomicInteger();
private final TransactionManager _manager;
private final ConcurrentHashMap<Thread, Thread> _allowedThreads = new ConcurrentHashMap<Thread, Thread>();
private final HashMap<TransactionCommit, ArrayList<Object>> _watchers;
private final ConcurrentHashMap<Transaction, ConcurrentHashMap<TransactionCommit, TransactionCommit>> _validated;
public TransactionHelper(TransactionManager manager) {
if (!Debug.ENABLED)
throw new IllegalStateException();
_manager = manager;
_watchers = new HashMap<TransactionCommit, ArrayList<Object>>();
_validated = new ConcurrentHashMap<Transaction, ConcurrentHashMap<TransactionCommit, TransactionCommit>>();
}
public boolean idle() {
synchronized (_watchers) {
if (_watchers.size() != 1)
return false;
Debug.assertion(_manager.getSnapshot().getCommits().length == 1);
TransactionCommit last = _manager.getSnapshot().getLast();
Debug.assertion(_watchers.containsKey(last));
}
if (_validated.size() > 0)
return false;
return true;
}
public static TransactionCommit[] insertCommit(TransactionCommit[] commits, TransactionCommit commit, int index) {
TransactionCommit[] newCommits = new TransactionCommit[commits.length + 1];
// Copy commits already acknowledged
GWTAdapter.copy(commits, 0, newCommits, 0, index);
// Insert new commit after last acknowledged one
newCommits[index] = commit;
// Copy remaining with offset 1
int length = commits.length - index;
if (length > 0) { // Only if there is an interceptor
if (Debug.ENABLED)
Debug.assertion(length == newCommits.length - (index + 1));
GWTAdapter.copy(commits, index, newCommits, index + 1, length);
if (Debug.ENABLED) {
Debug.assertion(newCommits[newCommits.length - 1] != null);
Debug.assertion(newCommits[newCommits.length - 1] == commits[commits.length - 1]);
}
}
// Search nulls to assert indexes, races etc...
if (Debug.ENABLED)
Debug.assertion(!Arrays.asList(newCommits).contains(null));
return newCommits;
}
public static TransactionEntry[][] insertDelta(TransactionEntry[][] deltas, TransactionEntry[] delta, int index) {
TransactionEntry[][] newDeltas = new TransactionEntry[deltas.length + 1][];
// Copy commits already acknowledged
GWTAdapter.copy(deltas, 0, newDeltas, 0, index);
// Insert new commit after last acknowledged one
newDeltas[index] = delta;
// Copy remaining with offset 1
int length = deltas.length - index;
if (length > 0) { // Only if there is an interceptor
if (Debug.ENABLED)
Debug.assertion(length == newDeltas.length - (index + 1));
GWTAdapter.copy(deltas, index, newDeltas, index + 1, length);
if (Debug.ENABLED) {
Debug.assertion(newDeltas[newDeltas.length - 1] != null);
Debug.assertion(newDeltas[newDeltas.length - 1] == deltas[deltas.length - 1]);
}
}
// Search nulls to assert indexes, races etc...
if (Debug.ENABLED) {
Debug.assertion(newDeltas[TransactionManager.OBJECTS_VERSIONS_INDEX] == TransactionManager.OBJECTS_DELTA);
Debug.assertion(!Arrays.asList(newDeltas).contains(null));
}
return newDeltas;
}
public static TransactionCommit[] moveCommit(TransactionCommit[] commits, int index, int newIndex) {
TransactionCommit[] newCommits;
if (newIndex == index)
newCommits = commits;
else if (newIndex < index) {
newCommits = new TransactionCommit[commits.length];
GWTAdapter.copy(commits, 0, newCommits, 0, newIndex);
newCommits[newIndex] = commits[index];
GWTAdapter.copy(commits, newIndex, newCommits, newIndex + 1, index - newIndex);
GWTAdapter.copy(commits, index + 1, newCommits, index + 1, commits.length - 1 - index);
} else
throw new UnsupportedOperationException();
// Search nulls to assert indexes, races etc...
if (Debug.ENABLED)
Debug.assertion(!Arrays.asList(newCommits).contains(null));
return newCommits;
}
public static TransactionEntry[][] moveDelta(TransactionEntry[][] deltas, int index, int newIndex) {
TransactionEntry[][] newDeltas;
if (newIndex == index)
newDeltas = deltas;
else if (newIndex < index) {
newDeltas = new TransactionEntry[deltas.length][];
GWTAdapter.copy(deltas, 0, newDeltas, 0, newIndex);
newDeltas[newIndex] = deltas[index];
GWTAdapter.copy(deltas, newIndex, newDeltas, newIndex + 1, index - newIndex);
GWTAdapter.copy(deltas, index + 1, newDeltas, index + 1, deltas.length - 1 - index);
} else
throw new UnsupportedOperationException();
// Search nulls to assert indexes, races etc...
if (Debug.ENABLED) {
Debug.assertion(newDeltas[TransactionManager.OBJECTS_VERSIONS_INDEX] == TransactionManager.OBJECTS_DELTA);
Debug.assertion(!Arrays.asList(newDeltas).contains(null));
}
return newDeltas;
}
public static TransactionCommit[] removeCommit(TransactionCommit[] commits, int index) {
TransactionCommit[] newCommits = new TransactionCommit[commits.length - 1];
GWTAdapter.copy(commits, 0, newCommits, 0, index);
int remaining = commits.length - 1 - index;
if (remaining > 0)
GWTAdapter.copy(commits, index + 1, newCommits, index, remaining);
// Search nulls to assert indexes, races etc...
if (Debug.ENABLED)
Debug.assertion(!Arrays.asList(newCommits).contains(null));
return newCommits;
}
public static TransactionEntry[][] removeDelta(TransactionEntry[][] deltas, int index) {
TransactionEntry[][] newDeltas = new TransactionEntry[deltas.length - 1][];
GWTAdapter.copy(deltas, 0, newDeltas, 0, index);
int remaining = deltas.length - 1 - index;
if (remaining > 0)
GWTAdapter.copy(deltas, index + 1, newDeltas, index, remaining);
// Search nulls to assert indexes, races etc...
if (Debug.ENABLED)
Debug.assertion(!Arrays.asList(newDeltas).contains(null));
return newDeltas;
}
/**
* TODO: hash or sort?
*/
public static int getIndex(Snapshot snapshot, TransactionCommit commit) {
for (int i = 0; i < snapshot.getCommits().length; i++)
if (snapshot.getCommits()[i] == commit)
return i;
throw new IllegalStateException();
}
public static boolean valid(Transaction transaction, Snapshot snapshot, int lastValidated) {
int minInclusive = lastValidated + 1;
int maxInclusive = snapshot.getLastAcknowledgedIndex();
for (int index = minInclusive; index <= maxInclusive; index++) {
// Check we do not validate twice against the same commit
if (Debug.ENABLED) {
for (Transaction current : Site.getLocal().getManager().getHelper()._validated.keySet()) {
TransactionCommit.Status aborted = TransactionCommit.Status.ABORTED;
Debug.assertion(current.getCommit().getStatus() != aborted);
}
ConcurrentHashMap<TransactionCommit, TransactionCommit> commits = Site.getLocal().getManager().getHelper()._validated.get(transaction);
if (commits == null) {
commits = new ConcurrentHashMap<TransactionCommit, TransactionCommit>();
Debug.assertion(Site.getLocal().getManager().getHelper()._validated.put(transaction, commits) == null);
}
TransactionCommit commit = snapshot.getCommits()[index];
if (commits.put(commit, commit) != null)
ValidateOnceFailed = true;
}
// Validate
if (!valid(transaction, snapshot.getDeltas()[index]))
return false;
}
return true;
}
public void removeValidated(Transaction transaction) {
_validated.remove(transaction);
}
public static boolean valid(Transaction transaction, TransactionEntry[] writes) {
TransactionEntry[] reads = transaction.getDelta();
if (reads != null) {
for (int i = 0; i < reads.length; i++) {
for (TransactionEntry entry = reads[i]; entry != null; entry = entry.getNext()) {
TObject object = entry.getKey();
TObject.Version write = TransactionCommit.get(writes, object);
if (write != null) {
if (write.invalidates(entry.getValue(), transaction))
return false;
}
}
}
}
return true;
}
//
public void allowThread() {
Thread thread = Thread.currentThread();
_allowedThreads.put(thread, thread);
}
public void assertThreadIsAllowed() {
if (!Debug.THREADS)
throw new IllegalStateException();
boolean ok = false;
Thread currentThread = Thread.currentThread();
for (Thread thread : _allowedThreads.keySet()) {
if (thread == currentThread)
ok = true;
if (false)
_allowedThreads.remove(thread);
}
Debug.assertion(ok);
}
//
public void addWatcher(TransactionCommit commit, Object watcher, int previousCount, int newCount, String reason) {
if (!Debug.ENABLED)
throw new IllegalStateException();
if (Debug.STM) {
String change = previousCount + " -> " + newCount;
Log.write("+ on commit " + commit + " " + change + " (" + reason + " by " + watcher + ")");
}
synchronized (_watchers) {
ArrayList<Object> forCommit = _watchers.get(commit);
if (forCommit == null) {
forCommit = new ArrayList<Object>();
Object previous = _watchers.put(commit, forCommit);
Debug.assertion(previous == null);
}
forCommit.add(watcher);
}
}
public void removeWatcher(TransactionCommit commit, Object watcher, int previousCount, int newCount, String reason) {
if (!Debug.ENABLED)
throw new IllegalStateException();
if (Debug.STM) {
String change = previousCount + " -> " + newCount;
Log.write("- on commit " + commit + " " + change + " (" + reason + " by " + watcher + ")");
}
synchronized (_watchers) {
ArrayList<Object> transactions = _watchers.get(commit);
boolean removed = transactions.remove(watcher);
Debug.assertion(removed);
if (transactions.size() == 0)
_watchers.remove(commit);
}
}
public List<Object> getWatchers(TransactionCommit commit) {
if (!Debug.ENABLED)
throw new IllegalStateException();
List<Object> list = new ArrayList<Object>();
synchronized (_watchers) {
ArrayList<Object> forCommit = _watchers.get(commit);
if (forCommit != null)
list.addAll(forCommit);
}
return list;
}
//
public static final class RunTransactedCallback implements AsyncCallback<Void> {
private final jstm4gwt.misc.Runnable _runnable;
private final AsyncCallback<Void> _callback;
public RunTransactedCallback(jstm4gwt.misc.Runnable runnable, AsyncCallback<Void> callback) {
_runnable = runnable;
_callback = callback;
}
public void onSuccess(Void result) {
if (_callback != null)
_callback.onSuccess(result);
}
public void onFailure(Throwable t) {
Site.getLocal().runTransacted(_runnable, _callback);
}
}
}
|