38
38
import org .bson .codecs .Decoder ;
39
39
40
40
import java .util .List ;
41
- import java .util .concurrent .atomic .AtomicBoolean ;
42
41
import java .util .concurrent .atomic .AtomicInteger ;
43
42
import java .util .concurrent .atomic .AtomicReference ;
44
43
@@ -65,7 +64,6 @@ class AsyncQueryBatchCursor<T> implements AsyncAggregateResponseBatchCursor<T> {
65
64
private final Decoder <T > decoder ;
66
65
private final long maxTimeMS ;
67
66
private final AsyncConnectionSource connectionSource ;
68
- private final AtomicBoolean isClosed = new AtomicBoolean ();
69
67
private final AtomicReference <ServerCursor > cursor ;
70
68
private volatile QueryResult <T > firstBatch ;
71
69
private volatile int batchSize ;
@@ -74,6 +72,12 @@ class AsyncQueryBatchCursor<T> implements AsyncAggregateResponseBatchCursor<T> {
74
72
private volatile BsonTimestamp operationTime ;
75
73
private volatile boolean firstBatchEmpty ;
76
74
75
+ /* protected by `this` */
76
+ private boolean isOperationInProgress = false ;
77
+ private boolean isClosed = false ;
78
+ private boolean isClosePending = false ;
79
+ /* protected by `this` */
80
+
77
81
AsyncQueryBatchCursor (final QueryResult <T > firstBatch , final int limit , final int batchSize , final long maxTimeMS ,
78
82
final Decoder <T > decoder , final AsyncConnectionSource connectionSource , final AsyncConnection connection ) {
79
83
this (firstBatch , limit , batchSize , maxTimeMS , decoder , connectionSource , connection , null );
@@ -108,7 +112,19 @@ class AsyncQueryBatchCursor<T> implements AsyncAggregateResponseBatchCursor<T> {
108
112
109
113
@ Override
110
114
public void close () {
111
- if (!isClosed .getAndSet (true )) {
115
+ boolean killCursor = false ;
116
+
117
+ synchronized (this ) {
118
+ if (isOperationInProgress ) {
119
+ isClosePending = true ;
120
+ } else {
121
+ killCursor = !isClosed ;
122
+ isClosed = true ;
123
+ isClosePending = false ;
124
+ }
125
+ }
126
+
127
+ if (killCursor ) {
112
128
killCursorOnClose ();
113
129
}
114
130
}
@@ -125,19 +141,25 @@ public void tryNext(final SingleResultCallback<List<T>> callback) {
125
141
126
142
@ Override
127
143
public void setBatchSize (final int batchSize ) {
128
- isTrue ("open" , !isClosed .get ());
144
+ synchronized (this ) {
145
+ isTrue ("open" , !isClosed );
146
+ }
129
147
this .batchSize = batchSize ;
130
148
}
131
149
132
150
@ Override
133
151
public int getBatchSize () {
134
- isTrue ("open" , !isClosed .get ());
152
+ synchronized (this ) {
153
+ isTrue ("open" , !isClosed );
154
+ }
135
155
return batchSize ;
136
156
}
137
157
138
158
@ Override
139
159
public boolean isClosed () {
140
- return isClosed .get ();
160
+ synchronized (this ) {
161
+ return isClosed ;
162
+ }
141
163
}
142
164
143
165
@ Override
@@ -170,9 +192,19 @@ private void next(final SingleResultCallback<List<T>> callback, final boolean tr
170
192
} else {
171
193
ServerCursor localCursor = getServerCursor ();
172
194
if (localCursor == null ) {
173
- isClosed .set (true );
195
+ synchronized (this ) {
196
+ isClosed = true ;
197
+ }
174
198
callback .onResult (null , null );
175
199
} else {
200
+ synchronized (this ) {
201
+ if (isClosed ) {
202
+ callback .onResult (null , new MongoException (format ("%s called after the cursor was closed." ,
203
+ tryNext ? "tryNext()" : "next()" )));
204
+ return ;
205
+ }
206
+ isOperationInProgress = true ;
207
+ }
176
208
getMore (localCursor , callback , tryNext );
177
209
}
178
210
}
@@ -187,6 +219,7 @@ private void getMore(final ServerCursor cursor, final SingleResultCallback<List<
187
219
@ Override
188
220
public void onResult (final AsyncConnection connection , final Throwable t ) {
189
221
if (t != null ) {
222
+ endOperationInProgress ();
190
223
callback .onResult (null , t );
191
224
} else {
192
225
getMore (connection , cursor , callback , tryNext );
@@ -274,17 +307,20 @@ private BsonDocument asKillCursorsCommandDocument(final ServerCursor localCursor
274
307
.append ("cursors" , new BsonArray (singletonList (new BsonInt64 (localCursor .getId ()))));
275
308
}
276
309
310
+ private void endOperationInProgress () {
311
+ boolean closePending = false ;
312
+ synchronized (this ) {
313
+ isOperationInProgress = false ;
314
+ closePending = this .isClosePending ;
315
+ }
316
+ if (closePending ) {
317
+ close ();
318
+ }
319
+ }
277
320
278
321
private void handleGetMoreQueryResult (final AsyncConnection connection , final SingleResultCallback <List <T >> callback ,
279
322
final QueryResult <T > result , final boolean tryNext ) {
280
- if (isClosed ()) {
281
- connection .release ();
282
- callback .onResult (null , new MongoException (format ("The cursor was closed before %s completed." ,
283
- tryNext ? "tryNext()" : "next()" )));
284
- return ;
285
- }
286
-
287
- cursor .getAndSet (result .getCursor ());
323
+ cursor .set (result .getCursor ());
288
324
if (!tryNext && result .getResults ().isEmpty () && result .getCursor () != null ) {
289
325
getMore (connection , result .getCursor (), callback , false );
290
326
} else {
@@ -298,6 +334,7 @@ private void handleGetMoreQueryResult(final AsyncConnection connection, final Si
298
334
connectionSource .release ();
299
335
}
300
336
}
337
+ endOperationInProgress ();
301
338
302
339
if (result .getResults ().isEmpty ()) {
303
340
callback .onResult (null , null );
@@ -328,6 +365,7 @@ public void onResult(final BsonDocument result, final Throwable t) {
328
365
? translateCommandException ((MongoCommandException ) t , cursor )
329
366
: t ;
330
367
connection .release ();
368
+ endOperationInProgress ();
331
369
callback .onResult (null , translatedException );
332
370
} else {
333
371
QueryResult <T > queryResult = getMoreCursorDocumentToQueryResult (result .getDocument (CURSOR ),
@@ -354,6 +392,7 @@ private class QueryResultSingleResultCallback implements SingleResultCallback<Qu
354
392
public void onResult (final QueryResult <T > result , final Throwable t ) {
355
393
if (t != null ) {
356
394
connection .release ();
395
+ endOperationInProgress ();
357
396
callback .onResult (null , t );
358
397
} else {
359
398
handleGetMoreQueryResult (connection , callback , result , tryNext );
0 commit comments