Skip to content

Commit 55bb4a8

Browse files
committed
fifth attempt
1 parent 62ca456 commit 55bb4a8

2 files changed

Lines changed: 63 additions & 54 deletions

File tree

driver-core/src/main/com/mongodb/internal/async/function/AsyncCallbackLoop.java

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
import java.util.function.Supplier;
2323

24+
import static com.mongodb.assertions.Assertions.assertTrue;
25+
2426
/**
2527
* A decorator that implements automatic repeating of an {@link AsyncCallbackRunnable}.
2628
* {@link AsyncCallbackLoop} may execute the original asynchronous function multiple times sequentially,
@@ -39,7 +41,6 @@
3941
*/
4042
@NotThreadSafe
4143
public final class AsyncCallbackLoop implements AsyncCallbackRunnable {
42-
private final LoopState state;
4344
private final Body body;
4445

4546
/**
@@ -51,88 +52,96 @@ public AsyncCallbackLoop(final LoopState state, final AsyncCallbackRunnable body
5152
}
5253

5354
public AsyncCallbackLoop(final boolean optimized, final LoopState state, final AsyncCallbackRunnable body) {
54-
this.state = state;
55-
this.body = new Body(optimized, body);
55+
this.body = new Body(optimized, state, body);
5656
}
5757

5858
@Override
5959
public void run(final SingleResultCallback<Void> callback) {
60-
body.run(new LoopingCallback(callback));
60+
body.loop(new ReusableLoopCallback(callback));
6161
}
6262

6363
private static final class Body {
6464
private final AsyncCallbackRunnable wrapped;
6565
private final boolean optimized;
66-
private boolean executingChain;
66+
private final LoopState state;
67+
private boolean reenteredLoopMethod;
6768

68-
private Body(final boolean optimized, final AsyncCallbackRunnable body) {
69+
private Body(final boolean optimized, final LoopState state, final AsyncCallbackRunnable body) {
6970
this.wrapped = body;
7071
this.optimized = optimized;
71-
executingChain = false;
72+
this.state = state;
73+
reenteredLoopMethod = false;
7274
}
7375

74-
@Nullable
75-
Element run(final LoopingCallback loopingCallback) {
76-
Element[] mutableElement = new Element[1];
76+
/**
77+
* @return {@code true} to indicate that the looping completed;
78+
* {@code false} to indicate that the looping is still executing, potentially asynchronously.
79+
*/
80+
boolean loop(final ReusableLoopCallback callback) {
81+
boolean[] done = new boolean[] {true};
7782
wrapped.run((r, t) -> {
78-
Element element = loopingCallback.onResult(r, t);
79-
if (!optimized && element != null) {
80-
element.execute();
83+
boolean localDone = callback.onResult(state, r, t);
84+
if (localDone) {
85+
done[0] = localDone;
8186
return;
8287
}
83-
if (element == null || executingChain) {
84-
mutableElement[0] = element;
85-
} else {
86-
executingChain = true;
88+
if (!optimized) {
89+
localDone = loop(callback);
90+
done[0] = localDone;
91+
return;
92+
}
93+
if (!reenteredLoopMethod) {
94+
reenteredLoopMethod = true;
8795
try {
8896
do {
89-
element = element.execute();
90-
} while (element != null);
97+
localDone = loop(callback);
98+
} while (!localDone);
99+
done[0] = assertTrue(localDone);
91100
} finally {
92-
executingChain = false;
101+
reenteredLoopMethod = false;
93102
}
103+
} else {
104+
done[0] = localDone;
94105
}
95106
});
96-
return mutableElement[0];
107+
return done[0];
97108
}
98109
}
99110

100111
/**
101-
* This callback is allowed to be completed more than once.
112+
* This callback is allowed to be {@linkplain #onResult(LoopState, Void, Throwable) completed} more than once.
102113
*/
103114
@NotThreadSafe
104-
private class LoopingCallback {
115+
private static final class ReusableLoopCallback {
105116
private final SingleResultCallback<Void> wrapped;
106117

107-
LoopingCallback(final SingleResultCallback<Void> callback) {
118+
ReusableLoopCallback(final SingleResultCallback<Void> callback) {
108119
wrapped = callback;
109120
}
110121

111-
@Nullable
112-
public Element onResult(@Nullable final Void result, @Nullable final Throwable t) {
122+
/**
123+
* @return {@code true} iff the {@linkplain ReusableLoopCallback#ReusableLoopCallback(SingleResultCallback) wrapped}
124+
* {@link SingleResultCallback} is {@linkplain SingleResultCallback#onResult(Object, Throwable) completed}.
125+
*/
126+
public boolean onResult(final LoopState state, @Nullable final Void result, @Nullable final Throwable t) {
113127
if (t != null) {
114128
wrapped.onResult(null, t);
115-
return null;
129+
return true;
116130
} else {
117131
boolean continueLooping;
118132
try {
119133
continueLooping = state.advance();
120134
} catch (Throwable e) {
121135
wrapped.onResult(null, e);
122-
return null;
136+
return true;
123137
}
124138
if (continueLooping) {
125-
return () -> body.run(this);
139+
return false;
126140
} else {
127141
wrapped.onResult(result, null);
128-
return null;
142+
return true;
129143
}
130144
}
131145
}
132146
}
133-
134-
interface Element {
135-
@Nullable
136-
Element execute();
137-
}
138147
}

driver-core/src/test/unit/com/mongodb/internal/async/AsyncFunctionsAbstractTest.java

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,25 @@ void testDoWhileLoop() {
768768
});
769769
}
770770

771+
@Test
772+
void testDoWhileLoop2() {
773+
assertBehavesSameVariations(8,
774+
() -> {
775+
int i = 0;
776+
do {
777+
i++;
778+
sync(i);
779+
} while (i < 3 && plainTest(i));
780+
},
781+
(callback) -> {
782+
final int[] i = new int[1];
783+
beginAsync().thenRunDoWhileLoop((c) -> {
784+
i[0]++;
785+
async(i[0], c);
786+
}, () -> i[0] < 3 && plainTest(i[0])).finish(callback);
787+
});
788+
}
789+
771790
@Test
772791
void testFinallyWithPlainInsideTry() {
773792
// (in try: normal flow + exception + exception) * (in finally: normal + exception) = 6
@@ -990,23 +1009,4 @@ void testDerivation() {
9901009
}).finish(callback);
9911010
});
9921011
}
993-
994-
@Test
995-
void testThenRunDoWhileLoop() {
996-
assertBehavesSameVariations(8,
997-
() -> {
998-
int i = 0;
999-
do {
1000-
i++;
1001-
sync(i);
1002-
} while (i < 3 && plainTest(i));
1003-
},
1004-
(callback) -> {
1005-
final int[] i = new int[1];
1006-
beginAsync().thenRunDoWhileLoop((c) -> {
1007-
i[0]++;
1008-
async(i[0], c);
1009-
}, () -> i[0] < 3 && plainTest(i[0])).finish(callback);
1010-
});
1011-
}
10121012
}

0 commit comments

Comments
 (0)