21
21
import java .util .ArrayList ;
22
22
import java .util .List ;
23
23
import java .util .Locale ;
24
+ import java .util .concurrent .TimeUnit ;
24
25
import java .util .concurrent .locks .ReentrantLock ;
25
26
import java .util .function .Consumer ;
26
27
@@ -212,6 +213,38 @@ public void onComplete(AsyncEvent event) throws IOException {
212
213
}
213
214
214
215
216
+ /**
217
+ * Return 0 when there is no need to obtain a lock (no async handling in
218
+ * progress), 1 if lock was acquired, and -1 if lock is not acquired because
219
+ * request is no longer usable.
220
+ */
221
+ private int tryObtainLock () {
222
+
223
+ if (this .state == State .NEW ) {
224
+ return 0 ;
225
+ }
226
+
227
+ // Do not wait indefinitely, stop if we moved on from ASYNC state (e.g. to ERROR),
228
+ // helps to avoid ABBA deadlock with onError callback
229
+
230
+ while (this .state == State .ASYNC ) {
231
+ try {
232
+ if (this .stateLock .tryLock (500 , TimeUnit .MILLISECONDS )) {
233
+ if (this .state == State .ASYNC ) {
234
+ return 1 ;
235
+ }
236
+ this .stateLock .unlock ();
237
+ break ;
238
+ }
239
+ }
240
+ catch (InterruptedException ex ) {
241
+ // ignore
242
+ }
243
+ }
244
+
245
+ return -1 ;
246
+ }
247
+
215
248
/**
216
249
* Package private access for testing only.
217
250
*/
@@ -246,7 +279,7 @@ public void setAsyncWebRequest(StandardServletAsyncWebRequest asyncWebRequest) {
246
279
@ Override
247
280
@ SuppressWarnings ("NullAway" )
248
281
public ServletOutputStream getOutputStream () throws IOException {
249
- int level = obtainLockAndCheckState ();
282
+ int level = obtainLockOrRaiseException ();
250
283
try {
251
284
if (this .outputStream == null ) {
252
285
Assert .notNull (this .asyncWebRequest , "Not initialized" );
@@ -266,7 +299,7 @@ public ServletOutputStream getOutputStream() throws IOException {
266
299
@ Override
267
300
@ SuppressWarnings ("NullAway" )
268
301
public PrintWriter getWriter () throws IOException {
269
- int level = obtainLockAndCheckState ();
302
+ int level = obtainLockOrRaiseException ();
270
303
try {
271
304
if (this .writer == null ) {
272
305
Assert .notNull (this .asyncWebRequest , "Not initialized" );
@@ -284,7 +317,7 @@ public PrintWriter getWriter() throws IOException {
284
317
285
318
@ Override
286
319
public void flushBuffer () throws IOException {
287
- int level = obtainLockAndCheckState ();
320
+ int level = obtainLockOrRaiseException ();
288
321
try {
289
322
getResponse ().flushBuffer ();
290
323
}
@@ -296,25 +329,15 @@ public void flushBuffer() throws IOException {
296
329
}
297
330
}
298
331
299
- /**
300
- * Return 0 if checks passed and lock is not needed, 1 if checks passed
301
- * and lock is held, or raise AsyncRequestNotUsableException.
302
- */
303
- private int obtainLockAndCheckState () throws AsyncRequestNotUsableException {
332
+ private int obtainLockOrRaiseException () throws AsyncRequestNotUsableException {
304
333
Assert .notNull (this .asyncWebRequest , "Not initialized" );
305
- if (this .asyncWebRequest .state == State .NEW ) {
306
- return 0 ;
307
- }
308
-
309
- this .asyncWebRequest .stateLock .lock ();
310
- if (this .asyncWebRequest .state == State .ASYNC ) {
311
- return 1 ;
334
+ int result = this .asyncWebRequest .tryObtainLock ();
335
+ if (result == -1 ) {
336
+ throw new AsyncRequestNotUsableException ("Response not usable after " +
337
+ (this .asyncWebRequest .state == State .COMPLETED ?
338
+ "async request completion" : "response errors" ) + "." );
312
339
}
313
-
314
- this .asyncWebRequest .stateLock .unlock ();
315
- throw new AsyncRequestNotUsableException ("Response not usable after " +
316
- (this .asyncWebRequest .state == State .COMPLETED ?
317
- "async request completion" : "response errors" ) + "." );
340
+ return result ;
318
341
}
319
342
320
343
void handleIOException (IOException ex , String msg ) throws AsyncRequestNotUsableException {
@@ -360,7 +383,7 @@ public void setWriteListener(WriteListener writeListener) {
360
383
361
384
@ Override
362
385
public void write (int b ) throws IOException {
363
- int level = this .response .obtainLockAndCheckState ();
386
+ int level = this .response .obtainLockOrRaiseException ();
364
387
try {
365
388
this .delegate .write (b );
366
389
}
@@ -373,7 +396,7 @@ public void write(int b) throws IOException {
373
396
}
374
397
375
398
public void write (byte [] buf , int offset , int len ) throws IOException {
376
- int level = this .response .obtainLockAndCheckState ();
399
+ int level = this .response .obtainLockOrRaiseException ();
377
400
try {
378
401
this .delegate .write (buf , offset , len );
379
402
}
@@ -387,7 +410,7 @@ public void write(byte[] buf, int offset, int len) throws IOException {
387
410
388
411
@ Override
389
412
public void flush () throws IOException {
390
- int level = this .response .obtainLockAndCheckState ();
413
+ int level = this .response .obtainLockOrRaiseException ();
391
414
try {
392
415
this .delegate .flush ();
393
416
}
@@ -401,7 +424,7 @@ public void flush() throws IOException {
401
424
402
425
@ Override
403
426
public void close () throws IOException {
404
- int level = this .response .obtainLockAndCheckState ();
427
+ int level = this .response .obtainLockOrRaiseException ();
405
428
try {
406
429
this .delegate .close ();
407
430
}
@@ -435,7 +458,7 @@ private LifecyclePrintWriter(PrintWriter delegate, StandardServletAsyncWebReques
435
458
436
459
@ Override
437
460
public void flush () {
438
- int level = tryObtainLockAndCheckState ();
461
+ int level = this . asyncWebRequest . tryObtainLock ();
439
462
if (level > -1 ) {
440
463
try {
441
464
this .delegate .flush ();
@@ -448,7 +471,7 @@ public void flush() {
448
471
449
472
@ Override
450
473
public void close () {
451
- int level = tryObtainLockAndCheckState ();
474
+ int level = this . asyncWebRequest . tryObtainLock ();
452
475
if (level > -1 ) {
453
476
try {
454
477
this .delegate .close ();
@@ -466,7 +489,7 @@ public boolean checkError() {
466
489
467
490
@ Override
468
491
public void write (int c ) {
469
- int level = tryObtainLockAndCheckState ();
492
+ int level = this . asyncWebRequest . tryObtainLock ();
470
493
if (level > -1 ) {
471
494
try {
472
495
this .delegate .write (c );
@@ -479,7 +502,7 @@ public void write(int c) {
479
502
480
503
@ Override
481
504
public void write (char [] buf , int off , int len ) {
482
- int level = tryObtainLockAndCheckState ();
505
+ int level = this . asyncWebRequest . tryObtainLock ();
483
506
if (level > -1 ) {
484
507
try {
485
508
this .delegate .write (buf , off , len );
@@ -497,7 +520,7 @@ public void write(char[] buf) {
497
520
498
521
@ Override
499
522
public void write (String s , int off , int len ) {
500
- int level = tryObtainLockAndCheckState ();
523
+ int level = this . asyncWebRequest . tryObtainLock ();
501
524
if (level > -1 ) {
502
525
try {
503
526
this .delegate .write (s , off , len );
@@ -513,22 +536,6 @@ public void write(String s) {
513
536
this .delegate .write (s );
514
537
}
515
538
516
- /**
517
- * Return 0 if checks passed and lock is not needed, 1 if checks passed
518
- * and lock is held, and -1 if checks did not pass.
519
- */
520
- private int tryObtainLockAndCheckState () {
521
- if (this .asyncWebRequest .state == State .NEW ) {
522
- return 0 ;
523
- }
524
- this .asyncWebRequest .stateLock .lock ();
525
- if (this .asyncWebRequest .state == State .ASYNC ) {
526
- return 1 ;
527
- }
528
- this .asyncWebRequest .stateLock .unlock ();
529
- return -1 ;
530
- }
531
-
532
539
private void releaseLock (int level ) {
533
540
if (level > 0 ) {
534
541
this .asyncWebRequest .stateLock .unlock ();
0 commit comments