Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.objectfabric.TransactionManager Maven / Gradle / Ivy
/**
* This file is part of ObjectFabric (http://objectfabric.org).
*
* ObjectFabric is licensed under the Apache License, Version 2.0, the terms
* of which may be found at http://www.apache.org/licenses/LICENSE-2.0.html.
*
* Copyright ObjectFabric Inc.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.objectfabric;
import java.util.Arrays;
import org.objectfabric.Resource.Block;
import org.objectfabric.TObject.Transaction;
import org.objectfabric.TObject.Version;
import org.objectfabric.Workspace.Granularity;
final class TransactionManager {
/*
* TODO extend AtomicReferenceArray and move all CAS here with indexes far from each
* other to avoid same cache line conflicts.
*/
public static final int OBJECTS_VERSIONS_INDEX = 0;
private static final int MAP_QUEUE_SIZE_THRESHOLD = 80;
private static final int MAP_QUEUE_SIZE_MAXIMUM = 100;
public static final TObject.Version[] OBJECTS_VERSIONS = new TObject.Version[0];
private TransactionManager() {
throw new IllegalStateException();
}
public static boolean commit(Transaction transaction) {
if (Debug.ENABLED) {
transaction.checkInvariants();
transaction.assertUpdates();
}
Transaction parent = transaction.parent();
boolean result;
if (parent == null) {
Workspace workspace = transaction.workspace();
if (transaction.getWrites() != null) {
if (Debug.ENABLED) {
Debug.assertion(!transaction.noWrites());
Helper.instance().checkVersionsHaveWrites(transaction.getWrites());
}
result = validateAndUpdateSnapshot(transaction);
} else {
Snapshot snapshot = transaction.getSnapshot();
if (Debug.ENABLED)
workspace.releaseSnapshotDebug(snapshot, transaction, "TransactionManager.commit (No write)");
workspace.releaseSnapshot(snapshot);
transaction.reset();
if (Debug.THREADS)
ThreadAssert.removePrivate(transaction);
workspace.recycle(transaction);
result = true;
}
if (Stats.ENABLED) {
if (result)
Stats.Instance.Committed.incrementAndGet();
}
} else {
parent.mergePrivate(transaction);
if (Debug.THREADS)
ThreadAssert.removePrivate(transaction);
result = true;
}
return result;
}
private static boolean validateAndUpdateSnapshot(Transaction transaction) {
final Workspace workspace = transaction.workspace();
final Version[] reads = transaction.getReads();
final Version[] writes = transaction.getWrites();
final VersionMap map = transaction.getOrCreateVersionMap();
Snapshot snapshot;
Snapshot lastValidated = transaction.getSnapshot();
int lastValidatedAddedWatchers = 1;
List lastValidatedAddedWatchersList = null;
if (Debug.ENABLED) {
Debug.assertion(transaction.parent() == null);
lastValidatedAddedWatchersList = new List();
lastValidatedAddedWatchersList.add(transaction);
}
final Snapshot newSnapshot = new Snapshot();
int retryCount = 0;
for (;;) {
int addedWatchers = 0;
int temporaryWatchers = 0;
List watchersList = null;
List temporaryWatchersList = null;
boolean preventsMerge;
for (;;) {
for (;;) {
snapshot = workspace.snapshot();
if (snapshot.last() == VersionMap.CLOSING)
throw new ClosedException();
if (Stats.ENABLED) {
for (;;) {
long max = Stats.Instance.MaxMapCount.get();
if (snapshot.getVersionMaps().length <= max)
break;
if (Stats.Instance.MaxMapCount.compareAndSet(max, snapshot.getVersionMaps().length))
break;
}
}
/*
* TODO also measure size of each map, do not allow merging of maps if
* over size of a commit using same stuff as for source splitting.
*/
if (snapshot.getVersionMaps().length >= MAP_QUEUE_SIZE_MAXIMUM)
workspace.onOverloaded();
else {
if (snapshot.getVersionMaps().length >= MAP_QUEUE_SIZE_THRESHOLD)
workspace.onOverloading();
break;
}
}
if (Debug.ENABLED) {
watchersList = new List();
temporaryWatchersList = new List();
}
Extension[] sourceSplitters = null;
Extension[] noMerge = null;
if (snapshot.slowChanging() != null) {
sourceSplitters = snapshot.slowChanging().Splitters;
if (workspace.granularity() == Granularity.ALL)
noMerge = snapshot.slowChanging().Extensions;
}
preventsMerge = noMerge != null;
if (sourceSplitters != null) {
if (snapshot.last().isRemote() != map.isRemote()) {
addedWatchers += sourceSplitters.length;
if (Debug.ENABLED)
for (Extension sourceSplitter : sourceSplitters)
watchersList.add(sourceSplitter);
}
}
if (noMerge != null) {
addedWatchers += noMerge.length;
if (Debug.ENABLED)
for (Extension extension : noMerge)
watchersList.add(extension);
}
/*
* If snapshot has changed since transaction's start or last validation,
* add at least one watcher to prevent last map from merging with future
* ones during validation.
*/
if (snapshot != lastValidated && addedWatchers == 0) {
addedWatchers++;
temporaryWatchers++;
if (Debug.ENABLED) {
watchersList.add(transaction);
temporaryWatchersList.add(transaction);
}
}
if (addedWatchers == 0 || snapshot.last().tryToAddWatchers(addedWatchers)) {
if (Debug.ENABLED)
for (int i = 0; i < watchersList.size(); i++)
Helper.instance().addWatcher(snapshot.last(), watchersList.get(i), snapshot, "validateAndUpdateSnapshot");
/*
* If we use transaction's snapshot, keep it until after commit
* instead of after validation only.
*/
if (snapshot == transaction.getSnapshot()) {
addedWatchers++;
temporaryWatchers++;
lastValidatedAddedWatchers--;
if (Debug.ENABLED) {
watchersList.add(transaction);
temporaryWatchersList.add(transaction);
if (lastValidatedAddedWatchersList == null)
throw new AssertionError();
boolean r = lastValidatedAddedWatchersList.remove(transaction);
Debug.assertion(r);
}
}
break;
}
}
Runnable delayedMerge = null;
{
boolean conflict = false;
if (snapshot.last() != lastValidated.last()) {
if (reads != null) {
int start = Helper.getIndex(snapshot, lastValidated.last()) + 1;
int stop = snapshot.writes().length;
if (!Helper.validateCheckOnce(map, reads, snapshot, start, stop)) {
if (Debug.ENABLED)
Debug.assertion(!Helper.instance().AssertNoConflict);
conflict = true;
}
}
}
if (lastValidatedAddedWatchers != 0) {
if (Debug.ENABLED) {
if (lastValidatedAddedWatchersList == null)
throw new AssertionError();
for (int i = 0; i < lastValidatedAddedWatchersList.size(); i++)
Helper.instance().removeWatcher(lastValidated.last(), lastValidatedAddedWatchersList.get(i), snapshot, "Validation done");
}
delayedMerge = lastValidated.last().removeWatchers(workspace, lastValidatedAddedWatchers, true, lastValidated);
}
if (Debug.ENABLED) {
if (Helper.instance().ConflictAlways)
conflict = true;
if (Helper.instance().ConflictRandom && Platform.get().randomBoolean())
conflict = true;
}
if (conflict) {
if (delayedMerge != null)
delayedMerge.run();
dispose(transaction, addedWatchers, watchersList, snapshot);
return false;
}
}
//
newSnapshot.setVersionMaps(Helper.addVersionMap(snapshot.getVersionMaps(), map));
if (reads != null && snapshot.getReads() == null) {
Version[][] newReads = new Version[newSnapshot.getVersionMaps().length][];
newReads[newReads.length - 1] = reads;
newSnapshot.setReads(newReads);
} else if (reads != null || snapshot.getReads() != null)
newSnapshot.setReads(Helper.addVersions(snapshot.getReads(), reads));
else
newSnapshot.setReads(null);
newSnapshot.writes(Helper.addVersions(snapshot.writes(), writes));
newSnapshot.slowChanging(snapshot.slowChanging());
if (preventsMerge) {
transaction.setSnapshot(newSnapshot);
transaction.setPublicSnapshotVersions(newSnapshot.writes());
transaction.setWrites(null);
if (Debug.ENABLED) {
Helper.instance().disableEqualsOrHashCheck();
Helper.instance().getAllowExistingReadsOrWrites().put(transaction, transaction);
Helper.instance().enableEqualsOrHashCheck();
}
transaction.addFlags(TransactionBase.FLAG_IGNORE_READS | TransactionBase.FLAG_NO_WRITES | TransactionBase.FLAG_COMMITTED);
map.setTransaction(transaction);
} else if (!map.isRemote())
map.setTransaction(null);
for (int i = writes.length - 1; i >= 0; i--)
if (writes[i] != null)
writes[i].onPublishing(newSnapshot, snapshot.writes().length);
if (Debug.THREADS) {
ThreadAssert.share(map);
if (preventsMerge)
ThreadAssert.share(transaction);
}
/*
* Try to publish the new snapshot. TODO: do merge on same CAS, only if fails
* use delayedMerge.
*/
if (!workspace.casSnapshot(snapshot, newSnapshot)) {
if (Debug.THREADS) {
if (preventsMerge) {
ThreadAssert.removeShared(transaction);
ThreadAssert.addPrivate(transaction);
}
ThreadAssert.removeShared(map);
ThreadAssert.addPrivate(map);
}
if (delayedMerge != null)
delayedMerge.run();
if (Debug.ENABLED) {
retryCount++;
if (Stats.ENABLED) {
Stats.Instance.ValidationRetries.incrementAndGet();
for (;;) {
long max = Stats.Instance.ValidationRetriesMax.get();
if (retryCount <= max)
break;
if (Stats.Instance.ValidationRetriesMax.compareAndSet(max, retryCount))
break;
}
}
Helper.instance().setRetryCount(retryCount);
}
/*
* Store added watchers so that they are removed after next validation.
*/
lastValidated = snapshot;
lastValidatedAddedWatchers = addedWatchers;
if (Debug.ENABLED)
lastValidatedAddedWatchersList = watchersList;
} else {
/*
* Success!
*/
if (Debug.ENABLED) {
/*
* Assert all watched maps are still there.
*/
for (int i = 0; i < snapshot.getVersionMaps().length; i++) {
VersionMap current = snapshot.getVersionMaps()[i];
if (!Arrays.asList(newSnapshot.getVersionMaps()).contains(current))
Debug.assertion(current.getWatchers() == 0);
}
if (Debug.THREADS)
for (int i = 0; i < newSnapshot.getVersionMaps().length; i++)
Debug.assertion(!ThreadAssert.isPrivate(newSnapshot.getVersionMaps()[i]));
Helper.instance().removeValidated(map);
}
if (delayedMerge != null)
delayedMerge.run();
if (Debug.ENABLED) {
if (temporaryWatchersList == null)
throw new AssertionError();
for (int i = 0; i < temporaryWatchersList.size(); i++)
Helper.instance().removeWatcher(snapshot.last(), temporaryWatchersList.get(i), snapshot, "Success!");
Helper.instance().removeWatcher(snapshot.last(), workspace, snapshot, "No more last");
}
snapshot.last().removeWatchers(workspace, temporaryWatchers + 1, false, snapshot);
if (snapshot.slowChanging() != null) {
Actor[] actors = snapshot.slowChanging().Actors;
if (actors != null)
for (int i = actors.length - 1; i >= 0; i--)
actors[i].requestRun();
}
if (!preventsMerge) {
transaction.reset();
if (Debug.THREADS)
ThreadAssert.removePrivate(transaction);
workspace.recycle(transaction);
}
return true;
}
}
}
// Merge with abort
private static void dispose(Transaction transaction, int addedWatchers, List watchersList, Snapshot snapshot) {
if (Debug.ENABLED)
Debug.assertion(transaction.parent() == null);
Workspace workspace = transaction.workspace();
if (addedWatchers != 0) {
snapshot.last().removeWatchers(workspace, addedWatchers, false, snapshot);
if (Debug.ENABLED)
for (int i = 0; i < watchersList.size(); i++)
Helper.instance().removeWatcher(snapshot.last(), watchersList.get(i), snapshot, "Validation failed");
}
if (Debug.ENABLED) {
if (transaction.getVersionMap() != null)
Helper.instance().removeWatcher(transaction.getVersionMap(), workspace, snapshot, "dispose uncommitted map");
Helper.instance().removeValidated(transaction.getVersionMap());
}
if (Stats.ENABLED)
Stats.Instance.Aborted.incrementAndGet();
if (Debug.THREADS)
if (transaction.getVersionMap() != null)
ThreadAssert.removePrivate(transaction.getVersionMap());
transaction.reset();
if (Debug.THREADS)
ThreadAssert.removePrivate(transaction);
workspace.recycle(transaction);
}
/**
* Commits versions without validation.
*/
public static boolean load(Workspace workspace, Version[] versions, VersionMap expect, Resource resource, List acks) {
if (Debug.ENABLED)
Debug.assertion(versions.length > 0);
final VersionMap map = new VersionMap();
map.setRemote();
final Snapshot newSnapshot = new Snapshot();
int retryCount = 0;
for (;;) {
Snapshot snapshot;
int lastAckWatchers;
List lastAckWatchersList;
Extension[] sourceSplitters = null;
Extension[] noMerge = null;
boolean preventsMerge;
for (;;) {
snapshot = workspace.snapshot();
if (snapshot.last() != expect) {
if (Debug.THREADS)
ThreadAssert.removePrivate(map);
return false;
}
if (snapshot.slowChanging() != null) {
sourceSplitters = snapshot.slowChanging().Splitters;
if (workspace.granularity() == Granularity.ALL)
noMerge = snapshot.slowChanging().Extensions;
}
preventsMerge = noMerge != null;
lastAckWatchers = 0;
lastAckWatchersList = Debug.ENABLED ? new List() : null;
if (sourceSplitters != null) {
if (!snapshot.last().isRemote()) {
lastAckWatchers += sourceSplitters.length;
if (Debug.ENABLED)
for (Extension sourceSplitter : sourceSplitters)
lastAckWatchersList.add(sourceSplitter);
}
}
if (noMerge != null) {
lastAckWatchers += noMerge.length;
if (Debug.ENABLED)
for (Extension extension : noMerge)
lastAckWatchersList.add(extension);
}
if (lastAckWatchers == 0 || snapshot.last().tryToAddWatchers(lastAckWatchers)) {
if (Debug.ENABLED)
for (int i = 0; i < lastAckWatchersList.size(); i++)
Helper.instance().addWatcher(snapshot.last(), lastAckWatchersList.get(i), snapshot, "load ackWatchersList");
break;
}
}
if (Debug.ENABLED)
Helper.instance().addWatcher(map, workspace, snapshot, "TransactionManager::load");
//
newSnapshot.setVersionMaps(Helper.addVersionMap(snapshot.getVersionMaps(), map));
if (snapshot.getReads() != null)
newSnapshot.setReads(Helper.addVersions(snapshot.getReads(), null));
else
newSnapshot.setReads(null);
newSnapshot.writes(Helper.addVersions(snapshot.writes(), versions));
newSnapshot.slowChanging(snapshot.slowChanging());
Transaction transaction = null;
if (preventsMerge) {
transaction = workspace.getOrCreateTransaction();
transaction.setSnapshot(newSnapshot);
transaction.setPublicSnapshotVersions(newSnapshot.writes());
if (Debug.ENABLED)
Helper.instance().getAllowExistingReadsOrWrites().put(transaction, transaction);
transaction.addFlags(TransactionBase.FLAG_IGNORE_READS | TransactionBase.FLAG_NO_WRITES | TransactionBase.FLAG_COMMITTED);
map.setTransaction(transaction);
transaction.setVersionMap(map);
}
// TODO: find out if publishing is still needed
for (int i = versions.length - 1; i >= 0; i--) {
if (versions[i] != null) {
int todo_merge_methods;
versions[i].onDeserialized(newSnapshot);
versions[i].onPublishing(newSnapshot, snapshot.getVersionMaps().length);
}
}
if (Debug.THREADS) {
ThreadAssert.share(map);
if (preventsMerge)
ThreadAssert.share(transaction);
}
if (!workspace.casSnapshot(snapshot, newSnapshot)) {
if (Debug.THREADS) {
if (preventsMerge) {
ThreadAssert.removeShared(transaction);
ThreadAssert.addPrivate(transaction);
}
ThreadAssert.removeShared(map);
ThreadAssert.addPrivate(map);
}
if (Debug.ENABLED) {
retryCount++;
if (Stats.ENABLED) {
Stats.Instance.ValidationRetries.incrementAndGet();
Stats.max(Stats.Instance.ValidationRetriesMax, retryCount);
}
Helper.instance().setRetryCount(retryCount);
Helper.instance().removeWatcher(map, workspace, snapshot, "TransactionManager::load");
for (int i = 0; i < lastAckWatchersList.size(); i++)
Helper.instance().removeWatcher(snapshot.last(), lastAckWatchersList.get(i), snapshot, "TransactionManager::load ackWatchersList");
}
if (lastAckWatchers != 0)
snapshot.last().removeWatchers(workspace, lastAckWatchers, false, null);
if (Debug.ENABLED)
Debug.assertion((transaction != null) == preventsMerge);
if (transaction != null) {
transaction.reset();
workspace.recycle(transaction);
map.setTransaction(null);
transaction = null;
}
} else {
resource.onLoad(newSnapshot, acks);
if (Debug.ENABLED)
Helper.instance().removeWatcher(snapshot.last(), workspace, snapshot, "TransactionManager::load old");
snapshot.last().removeWatchers(workspace, 1, false, null);
//
if (snapshot.slowChanging() != null) {
Actor[] actors = snapshot.slowChanging().Actors;
if (actors != null)
for (int i = actors.length - 1; i >= 0; i--)
actors[i].requestRun();
}
return true;
}
}
}
public static void abort(Transaction transaction) {
if (Debug.ENABLED)
transaction.checkInvariants();
Workspace workspace = transaction.workspace();
Transaction parent = transaction.parent();
if (parent == null) {
if (Debug.ENABLED) {
if (transaction.getVersionMap() != null)
Helper.instance().removeWatcher(transaction.getVersionMap(), workspace, transaction.getSnapshot(), "TransactionManager::load abort");
workspace.releaseSnapshotDebug(transaction.getSnapshot(), transaction, "TransactionManager::load abort");
}
workspace.releaseSnapshot(transaction.getSnapshot());
transaction.reset();
if (Debug.THREADS)
ThreadAssert.removePrivate(transaction);
workspace.recycle(transaction);
if (Stats.ENABLED)
Stats.Instance.Aborted.incrementAndGet();
} else {
if (Debug.ENABLED)
if (transaction.getVersionMap() != null)
Helper.instance().removeWatcher(transaction.getVersionMap(), parent, transaction.getSnapshot(), "TransactionManager::load abort private");
if (Debug.THREADS)
ThreadAssert.removePrivate(transaction);
}
}
}