Skip to content

Commit 9e88eda

Browse files
authored
Merge pull request #119 from joaomlneto/fix-115
Add DeterministicCompletableFuture
2 parents 5fe12d2 + 50254cb commit 9e88eda

File tree

3 files changed

+31
-3
lines changed

3 files changed

+31
-3
lines changed

docs/implementing-protocols.md

+7-2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ check whether safety invariants of distributed consensus are broken.
5050

5151
- **Non-determinism**: For reproducibility we require the removal of any non-determinism! This can manifest itself in
5252
many ways:
53-
- Avoid interfaces and collections that do not guarantee order, such as `Set` or `Map`: use the `OrderedSet` or
54-
`OrderedMap` interfaces instead, and `TreeSet` or `LinkedHashMap` implementations to ensure order.
53+
- Avoid interfaces and collections that do not guarantee order, such as `Set` or `Map`: use the `OrderedSet` or
54+
`OrderedMap` interfaces instead, and `TreeSet` or `LinkedHashMap` implementations to ensure order.
55+
- Avoid the use of Java's `CompletableFuture`: the implementation in Java is non-deterministic, as it uses a
56+
`ForkJoinPool`
57+
that can execute tasks in parallel - tasks that are submitted to the `CompletableFuture` can be executed in any
58+
order, and this can lead to non-deterministic behavior. Use our own `DeterministicCompletableFuture`, which will
59+
execute the tasks in the order they were submitted to it.
5560

simulator/src/main/java/byzzbench/simulator/protocols/pbft_java/Ticket.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import byzzbench.simulator.protocols.pbft_java.message.PrePrepareMessage;
55
import byzzbench.simulator.protocols.pbft_java.message.PrepareMessage;
66
import byzzbench.simulator.protocols.pbft_java.message.RequestMessage;
7+
import byzzbench.simulator.utils.FIFOCompletableFuture;
78
import lombok.Getter;
89
import lombok.RequiredArgsConstructor;
910

@@ -26,7 +27,7 @@ public class Ticket<O extends Serializable, R extends Serializable> implements S
2627
private final Collection<Serializable> messages = new ConcurrentLinkedQueue<>();
2728
private final transient AtomicReference<ReplicaTicketPhase> phase = new AtomicReference<>(ReplicaTicketPhase.PRE_PREPARE);
2829
@Getter
29-
private final transient CompletableFuture<R> result = new CompletableFuture<>();
30+
private final transient CompletableFuture<R> result = new FIFOCompletableFuture<>();
3031
@Getter
3132
private volatile RequestMessage request;
3233

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package byzzbench.simulator.utils;
2+
3+
import java.util.concurrent.CompletableFuture;
4+
import java.util.concurrent.Executor;
5+
import java.util.concurrent.Executors;
6+
7+
/**
8+
* A {@link CompletableFuture} that uses a single thread {@link Executor} to guarantee ordering of dependent tasks.
9+
* Dependent tasks are executed in the order they are added (FIFO).
10+
*
11+
* @param <T> The result type returned by this future's {@code join} and {@code get} methods
12+
*/
13+
public class FIFOCompletableFuture<T> extends CompletableFuture<T> {
14+
private static final Executor executor = Executors.newSingleThreadExecutor(
15+
runnable -> new Thread(runnable, "Custom-Single-Thread")
16+
);
17+
18+
@Override
19+
public final Executor defaultExecutor() {
20+
return executor;
21+
}
22+
}

0 commit comments

Comments
 (0)