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.Extension 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 org.objectfabric.TObject.Version;
import org.objectfabric.ThreadAssert.AllowSharedRead;
import org.objectfabric.ThreadAssert.SingleThreaded;
import org.objectfabric.Workspace.Granularity;
/**
* Plugs into an View to get notified of changes on transactional objects. Uses two
* alternative snapshots of the version queue to "walk" it and visit changes.
*/
@SingleThreaded
abstract class Extension extends Visitor {
// TODO remove?
enum Action {
VISIT, SKIP
}
@SuppressWarnings("serial")
static final class ExtensionShutdownException extends RuntimeException {
}
@AllowSharedRead
private final Workspace _workspace;
@AllowSharedRead
private final boolean _splitsSources;
private Snapshot _snapshot;
private int _mapIndex1, _mapIndex2;
//
private TObject[] _reads = new TObject[OpenMap.CAPACITY];
private TObject[] _writes = new TObject[OpenMap.CAPACITY];
private final Resources _resources = new Resources();
private TObject[][] _readList = new TObject[Resources.DEFAULT_CAPACITY][];
private TObject[][] _writeList = new TObject[Resources.DEFAULT_CAPACITY][];
private int[] _readCount = new int[Resources.DEFAULT_CAPACITY];
private int[] _writeCount = new int[Resources.DEFAULT_CAPACITY];
private boolean _visitsTransients, _visitsReads;
// TODO simplify
private boolean _visitingRead, _visitingNewObject;
private int[] _mapIndexes = new int[16];
private int _mapIndexCount;
Extension(Workspace workspace, boolean splitsSources) {
super(new List());
if (workspace == null)
throw new IllegalArgumentException();
_workspace = workspace;
_splitsSources = splitsSources;
}
// TODO move constructor?
final void init(boolean visitsTransients, boolean visitsReads) {
_visitsTransients = visitsTransients;
_visitsReads = visitsReads;
}
final Workspace workspace() {
return _workspace;
}
final boolean visitsTransients() {
return _visitsTransients;
}
final boolean visitsReads() {
return _visitsReads;
}
public final boolean splitsSources() {
return _splitsSources;
}
final boolean visitingNewObject() {
return _visitingNewObject;
}
final void visitingNewObject(boolean value) {
_visitingNewObject = value;
}
final boolean visitingRead() {
return _visitingRead;
}
final void visitingRead(boolean value) {
_visitingRead = value;
}
final Snapshot snapshot() {
return _snapshot;
}
final int mapIndex1() {
return _mapIndex1;
}
final void mapIndex1(int value) {
_mapIndex1 = value;
}
final int mapIndex2() {
return _mapIndex2;
}
final void mapIndex2(int value) {
_mapIndex2 = value;
}
//
boolean casSnapshotWithThis(Snapshot expected, Snapshot update) {
VersionMap map = expected.last();
if (_workspace.granularity() == Granularity.COALESCE) {
if (map.tryToAddWatchers(1)) {
if (Debug.ENABLED)
Helper.instance().addWatcher(map, this, expected, "casSnapshotWithThis");
} else
return false;
}
if (_workspace.casSnapshot(expected, update)) {
_snapshot = expected;
return true;
}
// If failed, remove watchers and return false to retry
if (_workspace.granularity() == Granularity.COALESCE) {
if (Debug.ENABLED)
Helper.instance().removeWatcher(map, this, expected, "casSnapshotWithThis failed");
map.removeWatchers(_workspace, 1, false, null);
}
return false;
}
boolean casSnapshotWithoutThis(Snapshot expected, Snapshot update, Exception exception) {
if (_workspace.casSnapshot(expected, update)) {
VersionMap previous = _snapshot.last();
if (_workspace.granularity() == Granularity.COALESCE) {
if (Debug.ENABLED)
Helper.instance().removeWatcher(previous, this, expected, "unregister previous");
previous.removeWatchers(_workspace, 1, false, null);
} else {
int index = Helper.getIndex(expected, _snapshot.last());
int start = expected.getVersionMaps().length - 2;
if (expected.last() == VersionMap.CLOSING)
start--;
for (int i = start; i >= index; i--) {
if (Debug.ENABLED)
Helper.instance().removeWatcher(expected.getVersionMaps()[i], this, expected, "unregister " + i);
expected.getVersionMaps()[i].removeWatchers(_workspace, 1, false, null);
}
}
_snapshot = null;
return true;
}
return false;
}
//
final void walk() {
if (!interrupted()) {
if (_snapshot == null)
return;
Snapshot snapshot = _workspace.snapshotWithoutClosing();
VersionMap previous = _snapshot.last();
if (snapshot.last() == previous)
return;
/*
* TODO Create snapshot only if a maximum number is not reached, e.g. 8 for
* all walkers, so that queue is not too large. Otherwise reuse the latest
* snapshot created.
*/
for (;;) {
if (_workspace.granularity() == Granularity.ALL)
break;
VersionMap map = snapshot.last();
if (map.tryToAddWatchers(1)) {
if (Debug.ENABLED)
Helper.instance().addWatcher(map, this, snapshot, "run");
break;
}
snapshot = _workspace.snapshotWithoutClosing();
}
_snapshot = snapshot;
if (Debug.ENABLED)
Debug.assertion(snapshot.last() != previous);
int index = Helper.getIndex(snapshot, previous);
mapIndex1(index + 1);
mapIndex2(snapshot.lastIndex() + 1);
}
visitWorkspace();
if (!interrupted()) {
if (_workspace.granularity() == Granularity.COALESCE)
releaseSnapshot(mapIndex1(), mapIndex2());
if (Debug.ENABLED)
Debug.assertion(!interrupted());
}
}
void releaseSnapshot(int start, int end) {
VersionMap[] maps = _snapshot.getVersionMaps();
VersionMap map = maps[start - 1];
if (Debug.ENABLED)
Helper.instance().removeWatcher(map, this, _snapshot, "Walker releaseSnapshot");
map.removeWatchers(_workspace, 1, false, _snapshot);
if (_splitsSources) {
for (int i = start; i < end; i++) {
if (maps[i].isRemote() != map.isRemote()) {
if (Debug.ENABLED)
Helper.instance().removeWatcher(map, this, _snapshot, "Walker releaseSnapshot 2");
map.removeWatchers(_workspace, 1, false, null);
}
map = maps[i];
}
}
}
/*
* Version gathering.
*/
final void addMapIndex(int mapIndex) {
if (_mapIndexCount == _mapIndexes.length)
_mapIndexes = Helper.extend(_mapIndexes);
_mapIndexes[_mapIndexCount++] = mapIndex;
if (Debug.ENABLED)
for (int i = 0; i < _mapIndexCount; i++)
for (int j = 0; j < _mapIndexCount; j++)
if (i != j)
Debug.assertion(_mapIndexes[i] != _mapIndexes[j]);
}
final Version getGatheredVersion(TObject object, int index) {
int mapIndex = mapIndex(index);
if (mapIndex == TransactionManager.OBJECTS_VERSIONS_INDEX)
return object.shared_();
Version[][] versions = visitingRead() ? snapshot().getReads() : snapshot().writes();
if (versions[mapIndex] != null)
return TransactionBase.getVersion(versions[mapIndex], object);
return null;
}
final int mapIndexCount() {
if (_visitingNewObject)
return mapIndex2() - mapIndex1();
return _mapIndexCount;
}
final void mapIndexCount(int value) {
if (Debug.ENABLED)
Debug.assertion(!_visitingNewObject);
_mapIndexCount = value;
}
private final int mapIndex(int index) {
if (_visitingNewObject) {
if (Debug.ENABLED)
Debug.assertion(index < mapIndex2() - mapIndex1());
return mapIndex1() + index;
}
return _mapIndexes[index];
}
/*
* Visit.
*/
void onVisitingWorkspace() {
if (!interrupted())
OverrideAssert.set(this);
}
void onVisitedWorkspace() {
if (!interrupted())
OverrideAssert.set(this);
if (_workspace.granularity() != Granularity.ALL)
visitResources();
}
//
private enum VisitWorkspaceStep {
VISITING, VISIT, VISITED
}
@SuppressWarnings("fallthrough")
private final void visitWorkspace() {
VisitWorkspaceStep step = VisitWorkspaceStep.VISITING;
int mapIndex;
if (interrupted()) {
step = (VisitWorkspaceStep) resume();
mapIndex = resumeInt();
} else {
if (Debug.ENABLED)
assertNothingToFlush();
mapIndex = mapIndex1();
}
switch (step) {
case VISITING: {
if (!interrupted())
OverrideAssert.add(this);
onVisitingWorkspace();
if (!interrupted())
OverrideAssert.end(this);
if (interrupted()) {
interruptInt(mapIndex);
interrupt(VisitWorkspaceStep.VISITING);
return;
}
}
case VISIT: {
for (; mapIndex < mapIndex2(); mapIndex++) {
visitMap(mapIndex);
if (interrupted()) {
interruptInt(mapIndex);
interrupt(VisitWorkspaceStep.VISIT);
return;
}
if (_workspace.granularity() == Granularity.ALL) {
// Done with map, let it merge
releaseSnapshot(mapIndex, mapIndex + 1);
}
}
if (Debug.ENABLED)
Debug.assertion(!interrupted());
}
case VISITED: {
if (!interrupted())
OverrideAssert.add(this);
onVisitedWorkspace();
if (!interrupted())
OverrideAssert.end(this);
if (interrupted()) {
interruptInt(mapIndex);
interrupt(VisitWorkspaceStep.VISITED);
return;
}
}
}
}
//
Action onVisitingMap(int mapIndex) {
if (!interrupted())
OverrideAssert.set(this);
if (Debug.ENABLED) {
if (_workspace.granularity() == Granularity.ALL) {
VersionMap map = snapshot().getVersionMaps()[mapIndex];
Debug.assertion(map.isNotMerging());
Debug.assertion(map.getTransaction() != null);
Debug.assertion(map.getTransaction().parent() == null);
Debug.assertion(map.getTransaction().workspace() == _workspace);
if (Debug.THREADS)
ThreadAssert.assertShared(map);
}
}
return Action.VISIT;
}
void onVisitedMap(int mapIndex) {
if (!interrupted())
OverrideAssert.set(this);
if (_workspace.granularity() == Granularity.ALL)
visitResources();
}
//
private enum VisitMapStep {
VISITING, READS, WRITES, VISITED
}
@SuppressWarnings("fallthrough")
final void visitMap(int mapIndex) {
VisitMapStep step = VisitMapStep.VISITING;
boolean atLeastOneRead = false;
boolean atLeastOneWrite = false;
if (interrupted()) {
step = (VisitMapStep) resume();
atLeastOneRead = resumeBoolean();
atLeastOneWrite = resumeBoolean();
}
Action action = Action.VISIT;
switch (step) {
case VISITING: {
if (!interrupted())
OverrideAssert.add(this);
action = onVisitingMap(mapIndex);
if (!interrupted())
OverrideAssert.end(this);
if (interrupted()) {
interruptBoolean(atLeastOneWrite);
interruptBoolean(atLeastOneRead);
interrupt(VisitMapStep.VISITING);
return;
}
if (Debug.ENABLED)
Debug.assertion(action != null);
}
case READS: {
if (action == Action.VISIT) {
if (visitsReads()) {
Version[] reads = snapshot().getReads() != null ? snapshot().getReads()[mapIndex] : null;
if (reads != null) {
visitingRead(true);
atLeastOneRead = gatherReads(reads);
if (interrupted()) {
interruptBoolean(atLeastOneWrite);
interruptBoolean(atLeastOneRead);
interrupt(VisitMapStep.READS);
return;
}
visitingRead(false);
}
}
}
}
case WRITES: {
if (action == Action.VISIT) {
Version[] versions = snapshot().writes()[mapIndex];
atLeastOneWrite = gatherWrites(versions);
if (interrupted()) {
interruptBoolean(atLeastOneWrite);
interruptBoolean(atLeastOneRead);
interrupt(VisitMapStep.WRITES);
return;
}
if (atLeastOneRead || atLeastOneWrite)
addMapIndex(mapIndex);
}
}
case VISITED: {
if (!interrupted())
OverrideAssert.add(this);
onVisitedMap(mapIndex);
if (!interrupted())
OverrideAssert.end(this);
if (interrupted()) {
interruptBoolean(atLeastOneWrite);
interruptBoolean(atLeastOneRead);
interrupt(VisitMapStep.VISITED);
return;
}
}
}
}
//
private final boolean gatherReads(Version[] reads) {
if (Debug.ENABLED)
Debug.assertion(reads.length > 0);
int index;
boolean atLeastOne = false;
if (interrupted()) {
index = resumeInt();
atLeastOne = resumeBoolean();
} else
index = reads.length - 1;
for (; index >= 0; index--) {
Version version = reads[index];
if (version != null) {
Action action = visitTObject(version.object());
if (interrupted()) {
interruptBoolean(atLeastOne);
interruptInt(index);
return false;
}
if (action == Action.VISIT) {
atLeastOne = true;
int result;
while ((result = TObjectSet.tryToAdd(_reads, version.object())) == OpenMap.REHASH) {
TObject[] previous = _reads;
for (;;) {
_reads = new TObject[_reads.length << OpenMap.TIMES_TWO_SHIFT];
if (TObjectSet.rehash(previous, _reads))
break;
}
}
if (result >= 0) {
int resource = _resources.add(version.object().resource());
if (resource >= 0) {
if (resource >= _readCount.length) {
_readCount = Helper.extend(_readCount);
_readList = Helper.extend(_readList);
}
if (_readList[resource] == null)
_readList[resource] = new TObject[16];
} else
resource = -resource - 1;
if (_readCount[resource] == _readList[resource].length)
_readList[resource] = Helper.extend(_readList[resource]);
_readList[resource][_readCount[resource]++] = version.object();
}
}
}
}
return atLeastOne;
}
private final boolean gatherWrites(Version[] versions) {
if (Debug.ENABLED)
Debug.assertion(versions.length > 0);
int index;
boolean atLeastOne = false;
if (interrupted()) {
index = resumeInt();
atLeastOne = resumeBoolean();
} else
index = versions.length - 1;
for (; index >= 0; index--) {
Version version = versions[index];
if (version != null) {
Action action = visitTObject(version.object());
if (interrupted()) {
interruptBoolean(atLeastOne);
interruptInt(index);
return false;
}
if (action == Action.VISIT) {
atLeastOne = true;
int result;
while ((result = TObjectSet.tryToAdd(_writes, version.object())) == OpenMap.REHASH) {
TObject[] previous = _writes;
for (;;) {
_writes = new TObject[_writes.length << OpenMap.TIMES_TWO_SHIFT];
if (TObjectSet.rehash(previous, _writes))
break;
}
}
if (result >= 0) {
int resource = _resources.add(version.object().resource());
if (resource >= 0) {
if (resource >= _writeCount.length) {
_writeCount = Helper.extend(_writeCount);
_writeList = Helper.extend(_writeList);
}
if (_writeList[resource] == null)
_writeList[resource] = new TObject[16];
} else
resource = -resource - 1;
if (_writeCount[resource] == _writeList[resource].length)
_writeList[resource] = Helper.extend(_writeList[resource]);
_writeList[resource][_writeCount[resource]++] = version.object();
}
}
}
}
return atLeastOne;
}
Action onVisitingTObject(TObject object) {
if (!interrupted())
OverrideAssert.set(this);
return Action.VISIT;
}
private final Action visitTObject(TObject object) {
if (!interrupted())
OverrideAssert.add(this);
Action action = onVisitingTObject(object);
if (!interrupted())
OverrideAssert.end(this);
if (interrupted())
return null;
if (Debug.ENABLED)
Debug.assertion(action != null);
return action;
}
//
void onVisitingResource(Resource resource) {
if (!interrupted())
OverrideAssert.set(this);
}
void onVisitedResource(Resource resource) {
if (!interrupted())
OverrideAssert.set(this);
}
//
private enum VisitURIStep {
VISITING, VISIT, VISITED
}
@SuppressWarnings("fallthrough")
private final void visitResources() {
int index;
VisitURIStep step = VisitURIStep.VISITING;
if (interrupted()) {
index = resumeInt();
step = (VisitURIStep) resume();
} else
index = _resources.size() - 1;
for (; index >= 0; index--) {
Resource resource = _resources.get(index);
switch (step) {
case VISITING: {
if (!interrupted())
OverrideAssert.add(this);
onVisitingResource(resource);
if (!interrupted())
OverrideAssert.end(this);
if (interrupted()) {
interrupt(VisitURIStep.VISITING);
interruptInt(index);
return;
}
}
case VISIT: {
visitVersions(index);
if (interrupted()) {
interrupt(VisitURIStep.VISIT);
interruptInt(index);
return;
}
_resources.pollPartOfClear();
if (Debug.ENABLED)
Debug.assertion(!interrupted());
}
case VISITED: {
if (!interrupted())
OverrideAssert.add(this);
onVisitedResource(resource);
if (!interrupted())
OverrideAssert.end(this);
if (interrupted()) {
interrupt(VisitURIStep.VISITED);
interruptInt(index);
return;
}
}
}
}
mapIndexCount(0);
}
//
private enum FlushStep {
READS, WRITES
}
@SuppressWarnings("fallthrough")
final void visitVersions(int uri) {
FlushStep step = FlushStep.READS;
int index;
if (interrupted()) {
step = (FlushStep) resume();
index = resumeInt();
} else {
visitingRead(true);
index = _readCount[uri] - 1;
}
switch (step) {
case READS: {
for (; index >= 0; index--) {
if (Debug.ENABLED)
Debug.assertion(visitsReads());
TObject object = _readList[uri][index];
visit(object);
if (interrupted()) {
interruptInt(index);
interrupt(FlushStep.READS);
return;
}
}
visitingRead(false);
for (int i = _readCount[uri] - 1; i >= 0; i--) {
TObjectSet.removePartOfClear(_reads, _readList[uri][i]);
_readList[uri][i] = null;
}
_readCount[uri] = 0;
if (Debug.ENABLED)
if (uri == 0)
for (int i = _reads.length - 1; i >= 0; i--)
Debug.assertion(_reads[i] == null);
index = _writeCount[uri] - 1;
}
case WRITES: {
for (; index >= 0; index--) {
TObject object = _writeList[uri][index];
visit(object);
if (interrupted()) {
interruptInt(index);
interrupt(FlushStep.WRITES);
return;
}
}
for (int i = _writeCount[uri] - 1; i >= 0; i--) {
TObjectSet.removePartOfClear(_writes, _writeList[uri][i]);
_writeList[uri][i] = null;
}
_writeCount[uri] = 0;
if (Debug.ENABLED)
if (uri == 0)
for (int i = _writes.length - 1; i >= 0; i--)
Debug.assertion(_writes[i] == null);
}
}
}
/**
* ! Not interruptible !
*/
void onVisitingVersion(Version version) {
OverrideAssert.set(this);
}
final void visit(TObject object) {
Version version;
if (interrupted())
version = (Version) resume();
else {
version = object.createVersion_();
int length = mapIndexCount();
for (int i = 0; i < length; i++) {
Version current = getGatheredVersion(version.object(), i);
if (current != null)
version.deepCopy(current);
}
OverrideAssert.add(this);
onVisitingVersion(version);
OverrideAssert.end(this);
}
version.visit(this);
if (interrupted())
interrupt(version);
}
// Debug
void assertNothingToFlush() {
if (!Debug.ENABLED)
throw new IllegalStateException();
Debug.assertion(_resources.size() == 0);
for (int i = 0; i < _readCount.length; i++)
Debug.assertion(_readCount[i] == 0);
for (int i = 0; i < _writeCount.length; i++)
Debug.assertion(_writeCount[i] == 0);
Debug.assertion(mapIndexCount() == 0);
}
}