2121
2222import 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,
3941 */
4042@ NotThreadSafe
4143public 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}
0 commit comments