Skip to content

Commit

Permalink
Add Bosk.supersedingReadContext
Browse files Browse the repository at this point in the history
  • Loading branch information
prdoyle committed Jun 15, 2024
1 parent 6b1bfa4 commit 912d6f7
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 4 deletions.
35 changes: 32 additions & 3 deletions bosk-core/src/main/java/io/vena/bosk/Bosk.java
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ public <T> void submitInitialization(Reference<T> target, T newValue) {
assertCorrectBosk(target);
synchronized (this) {
boolean preconditionsSatisfied;
try (@SuppressWarnings("unused") ReadContext executionContext = new ReadContext(currentRoot)) {
try (@SuppressWarnings("unused") ReadContext executionContext = supersedingReadContext()) {
preconditionsSatisfied = !target.exists();
}
if (preconditionsSatisfied) {
Expand Down Expand Up @@ -264,7 +264,7 @@ public <T> void submitConditionalReplacement(Reference<T> target, T newValue, Re
assertCorrectBosk(precondition);
synchronized (this) {
boolean preconditionsSatisfied;
try (@SuppressWarnings("unused") ReadContext executionContext = new ReadContext(currentRoot)) {
try (@SuppressWarnings("unused") ReadContext executionContext = supersedingReadContext()) {
preconditionsSatisfied = Objects.equals(precondition.valueIfExists(), requiredValue);
}
if (preconditionsSatisfied) {
Expand All @@ -284,7 +284,7 @@ public <T> void submitConditionalDeletion(Reference<T> target, Reference<Identif
assertCorrectBosk(precondition);
synchronized (this) {
boolean preconditionsSatisfied;
try (@SuppressWarnings("unused") ReadContext executionContext = new ReadContext(currentRoot)) {
try (@SuppressWarnings("unused") ReadContext executionContext = supersedingReadContext()) {
preconditionsSatisfied = Objects.equals(precondition.value(), requiredValue);
}
if (preconditionsSatisfied) {
Expand Down Expand Up @@ -799,6 +799,35 @@ public final ReadContext readContext() {
return new ReadContext();
}

/**
* Establishes a new {@link ReadContext} for the calling thread, similar to {@link #readContext()}, except that
* if the calling thread already has a context, it will be ignored,
* and the newly created context will have a fresh snapshot of the bosk's state tree;
* then, when the returned context is {@link ReadContext#close closed},
* the previous context will be restored.
* <p>
* This is intended to support coordination of distributed logic among multiple threads (or servers) using the same bosk.
* Threads can submit an update, call {@link BoskDriver#flush}, and then use this method
* to inspect the bosk state and determine what effect the update had.
* <p>
* Use this method when it's important to observe the bosk state after a {@link BoskDriver#flush flush}
* performed by the same thread.
* When in doubt, you probably want {@link #readContext()} instead of this.
* This method opens the possibility that the same thread can see two different revisions of the bosk state,
* which can lead to confusing bugs in application code.
* In addition, when the returned context is {@link ReadContext#close closed},
* the bosk state can appear to revert to a prior state, which can be confusing.
*
* @see #readContext()
*/
public final ReadContext supersedingReadContext() {
R snapshot = currentRoot;
if (snapshot == null) {
throw new IllegalStateException("Bosk constructor has not yet finished; cannot create a ReadContext");
}
return new ReadContext(snapshot);
}

/**
* A path is "vetted" if we've already called {@link #pathCompiler}.{@link PathCompiler#targetTypeOf} on it.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,16 @@ private <T> void checkUpdates(Reference<T> ref, UnaryOperator<T> updater) throws
assertSame(firstValue, ref.value(), "New ReadContext sees same value as before");
bosk.driver().submitReplacement(ref, secondValue);
assertSame(firstValue, ref.value(), "Bosk updates not visible during the same ReadContext");

try (val ___ = bosk.supersedingReadContext()) {
assertSame(secondValue, ref.value(), "Superseding context sees the latest state");
try (val ____ = bosk.readContext()) {
assertSame(secondValue, ref.value(), "Nested context matches outer context");
}
}

try (val ___ = bosk.readContext()) {
assertSame(firstValue, ref.value(), "Nested context matches outer context");
assertSame(firstValue, ref.value(), "Nested context matches original outer context");
}
}

Expand Down

0 comments on commit 912d6f7

Please sign in to comment.