@@ -72,9 +72,16 @@ public StateForLevel(@Nullable DataLoaderInvocation dataLoaderInvocation,
7272 StateForLevel currentState = currentStateRef .get ();
7373
7474
75- boolean dispatchingStarted = currentState != null && currentState .dispatchingStarted ;
76- boolean dispatchingFinished = currentState != null && currentState .dispatchingFinished ;
77- boolean currentlyDelayedDispatching = currentState != null && currentState .currentlyDelayedDispatching ;
75+ boolean dispatchingStarted = false ;
76+ boolean dispatchingFinished = false ;
77+ boolean currentlyDelayedDispatching = false ;
78+
79+ if (currentState != null ) {
80+ dispatchingStarted = currentState .dispatchingStarted ;
81+ dispatchingFinished = currentState .dispatchingFinished ;
82+ currentlyDelayedDispatching = currentState .currentlyDelayedDispatching ;
83+
84+ }
7885
7986 if (!chained ) {
8087 if (normalDispatchOrDelayed ) {
@@ -107,10 +114,16 @@ public boolean newDataLoaderInvocation(DataLoaderInvocation dataLoaderInvocation
107114 while (true ) {
108115 StateForLevel currentState = currentStateRef .get ();
109116
117+ boolean dispatchingStarted = false ;
118+ boolean dispatchingFinished = false ;
119+ boolean currentlyDelayedDispatching = false ;
110120
111- boolean dispatchingStarted = currentState != null && currentState .dispatchingStarted ;
112- boolean dispatchingFinished = currentState != null && currentState .dispatchingFinished ;
113- boolean currentlyDelayedDispatching = currentState != null && currentState .currentlyDelayedDispatching ;
121+ if (currentState != null ) {
122+ dispatchingStarted = currentState .dispatchingStarted ;
123+ dispatchingFinished = currentState .dispatchingFinished ;
124+ currentlyDelayedDispatching = currentState .currentlyDelayedDispatching ;
125+
126+ }
114127
115128 // we need to start a new delayed dispatching if
116129 // the normal dispatching is finished and there is no currently delayed dispatching for this level
@@ -135,6 +148,35 @@ public void clear() {
135148
136149 private static class CallStack {
137150
151+ /**
152+ * We track three things per level:
153+ * - the number of execute object calls
154+ * - the number of object completion calls
155+ * - if the level is already dispatched
156+ * <p/>
157+ * The number of execute object calls is the number of times the execution
158+ * of a field with sub selection (meaning it is an object) started.
159+ * <p/>
160+ * For each execute object call there will be one matching object completion call,
161+ * indicating that the all fields in the sub selection have been fetched AND completed.
162+ * Completion implies the fetched value is "resolved" (CompletableFuture is completed if it was a CF)
163+ * and it the engine has processed it and called any needed subsequent execute object calls (if the result
164+ * was none null and of Object of [Object] (or [[Object]] etc).
165+ * <p/>
166+ * Together we know a that a level is ready for dispatch if:
167+ * - the parent was dispatched
168+ * - the #executeObject == #completionFinished in the grandparent level.
169+ * <p/>
170+ * The second condition implies that all execute object calls in the parent level happened
171+ * which again implies that all fetch fields in the current level have happened.
172+ * <p/>
173+ * For the first level we track only if all expected fetched field calls have happened.
174+ */
175+
176+ /**
177+ * The whole algo is impleted lock free and relies purely on CAS methods to handle concurrency.
178+ */
179+
138180 static class StateForLevel {
139181 private final int happenedCompletionFinishedCount ;
140182 private final int happenedExecuteObjectCalls ;
0 commit comments