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
@@ -211,6 +212,38 @@ public void onComplete(AsyncEvent event) throws IOException {
211
212
}
212
213
213
214
215
+ /**
216
+ * Return 0 when there is no need to obtain a lock (no async handling in
217
+ * progress), 1 if lock was acquired, and -1 if lock is not acquired because
218
+ * request is no longer usable.
219
+ */
220
+ private int tryObtainLock () {
221
+
222
+ if (this .state == State .NEW ) {
223
+ return 0 ;
224
+ }
225
+
226
+ // Do not wait indefinitely, stop if we moved on from ASYNC state (e.g. to ERROR),
227
+ // helps to avoid ABBA deadlock with onError callback
228
+
229
+ while (this .state == State .ASYNC ) {
230
+ try {
231
+ if (this .stateLock .tryLock (500 , TimeUnit .MILLISECONDS )) {
232
+ if (this .state == State .ASYNC ) {
233
+ return 1 ;
234
+ }
235
+ this .stateLock .unlock ();
236
+ break ;
237
+ }
238
+ }
239
+ catch (InterruptedException ex ) {
240
+ // ignore
241
+ }
242
+ }
243
+
244
+ return -1 ;
245
+ }
246
+
214
247
/**
215
248
* Package private access for testing only.
216
249
*/
@@ -244,7 +277,7 @@ public void setAsyncWebRequest(StandardServletAsyncWebRequest asyncWebRequest) {
244
277
245
278
@ Override
246
279
public ServletOutputStream getOutputStream () throws IOException {
247
- int level = obtainLockAndCheckState ();
280
+ int level = obtainLockOrRaiseException ();
248
281
try {
249
282
if (this .outputStream == null ) {
250
283
Assert .notNull (this .asyncWebRequest , "Not initialized" );
@@ -263,7 +296,7 @@ public ServletOutputStream getOutputStream() throws IOException {
263
296
264
297
@ Override
265
298
public PrintWriter getWriter () throws IOException {
266
- int level = obtainLockAndCheckState ();
299
+ int level = obtainLockOrRaiseException ();
267
300
try {
268
301
if (this .writer == null ) {
269
302
Assert .notNull (this .asyncWebRequest , "Not initialized" );
@@ -281,7 +314,7 @@ public PrintWriter getWriter() throws IOException {
281
314
282
315
@ Override
283
316
public void flushBuffer () throws IOException {
284
- int level = obtainLockAndCheckState ();
317
+ int level = obtainLockOrRaiseException ();
285
318
try {
286
319
getResponse ().flushBuffer ();
287
320
}
@@ -293,25 +326,15 @@ public void flushBuffer() throws IOException {
293
326
}
294
327
}
295
328
296
- /**
297
- * Return 0 if checks passed and lock is not needed, 1 if checks passed
298
- * and lock is held, or raise AsyncRequestNotUsableException.
299
- */
300
- private int obtainLockAndCheckState () throws AsyncRequestNotUsableException {
329
+ private int obtainLockOrRaiseException () throws AsyncRequestNotUsableException {
301
330
Assert .notNull (this .asyncWebRequest , "Not initialized" );
302
- if (this .asyncWebRequest .state == State .NEW ) {
303
- return 0 ;
304
- }
305
-
306
- this .asyncWebRequest .stateLock .lock ();
307
- if (this .asyncWebRequest .state == State .ASYNC ) {
308
- return 1 ;
331
+ int result = this .asyncWebRequest .tryObtainLock ();
332
+ if (result == -1 ) {
333
+ throw new AsyncRequestNotUsableException ("Response not usable after " +
334
+ (this .asyncWebRequest .state == State .COMPLETED ?
335
+ "async request completion" : "response errors" ) + "." );
309
336
}
310
-
311
- this .asyncWebRequest .stateLock .unlock ();
312
- throw new AsyncRequestNotUsableException ("Response not usable after " +
313
- (this .asyncWebRequest .state == State .COMPLETED ?
314
- "async request completion" : "response errors" ) + "." );
337
+ return result ;
315
338
}
316
339
317
340
void handleIOException (IOException ex , String msg ) throws AsyncRequestNotUsableException {
@@ -357,7 +380,7 @@ public void setWriteListener(WriteListener writeListener) {
357
380
358
381
@ Override
359
382
public void write (int b ) throws IOException {
360
- int level = this .response .obtainLockAndCheckState ();
383
+ int level = this .response .obtainLockOrRaiseException ();
361
384
try {
362
385
this .delegate .write (b );
363
386
}
@@ -370,7 +393,7 @@ public void write(int b) throws IOException {
370
393
}
371
394
372
395
public void write (byte [] buf , int offset , int len ) throws IOException {
373
- int level = this .response .obtainLockAndCheckState ();
396
+ int level = this .response .obtainLockOrRaiseException ();
374
397
try {
375
398
this .delegate .write (buf , offset , len );
376
399
}
@@ -384,7 +407,7 @@ public void write(byte[] buf, int offset, int len) throws IOException {
384
407
385
408
@ Override
386
409
public void flush () throws IOException {
387
- int level = this .response .obtainLockAndCheckState ();
410
+ int level = this .response .obtainLockOrRaiseException ();
388
411
try {
389
412
this .delegate .flush ();
390
413
}
@@ -398,7 +421,7 @@ public void flush() throws IOException {
398
421
399
422
@ Override
400
423
public void close () throws IOException {
401
- int level = this .response .obtainLockAndCheckState ();
424
+ int level = this .response .obtainLockOrRaiseException ();
402
425
try {
403
426
this .delegate .close ();
404
427
}
@@ -432,7 +455,7 @@ private LifecyclePrintWriter(PrintWriter delegate, StandardServletAsyncWebReques
432
455
433
456
@ Override
434
457
public void flush () {
435
- int level = tryObtainLockAndCheckState ();
458
+ int level = this . asyncWebRequest . tryObtainLock ();
436
459
if (level > -1 ) {
437
460
try {
438
461
this .delegate .flush ();
@@ -445,7 +468,7 @@ public void flush() {
445
468
446
469
@ Override
447
470
public void close () {
448
- int level = tryObtainLockAndCheckState ();
471
+ int level = this . asyncWebRequest . tryObtainLock ();
449
472
if (level > -1 ) {
450
473
try {
451
474
this .delegate .close ();
@@ -463,7 +486,7 @@ public boolean checkError() {
463
486
464
487
@ Override
465
488
public void write (int c ) {
466
- int level = tryObtainLockAndCheckState ();
489
+ int level = this . asyncWebRequest . tryObtainLock ();
467
490
if (level > -1 ) {
468
491
try {
469
492
this .delegate .write (c );
@@ -476,7 +499,7 @@ public void write(int c) {
476
499
477
500
@ Override
478
501
public void write (char [] buf , int off , int len ) {
479
- int level = tryObtainLockAndCheckState ();
502
+ int level = this . asyncWebRequest . tryObtainLock ();
480
503
if (level > -1 ) {
481
504
try {
482
505
this .delegate .write (buf , off , len );
@@ -494,7 +517,7 @@ public void write(char[] buf) {
494
517
495
518
@ Override
496
519
public void write (String s , int off , int len ) {
497
- int level = tryObtainLockAndCheckState ();
520
+ int level = this . asyncWebRequest . tryObtainLock ();
498
521
if (level > -1 ) {
499
522
try {
500
523
this .delegate .write (s , off , len );
@@ -510,22 +533,6 @@ public void write(String s) {
510
533
this .delegate .write (s );
511
534
}
512
535
513
- /**
514
- * Return 0 if checks passed and lock is not needed, 1 if checks passed
515
- * and lock is held, and -1 if checks did not pass.
516
- */
517
- private int tryObtainLockAndCheckState () {
518
- if (this .asyncWebRequest .state == State .NEW ) {
519
- return 0 ;
520
- }
521
- this .asyncWebRequest .stateLock .lock ();
522
- if (this .asyncWebRequest .state == State .ASYNC ) {
523
- return 1 ;
524
- }
525
- this .asyncWebRequest .stateLock .unlock ();
526
- return -1 ;
527
- }
528
-
529
536
private void releaseLock (int level ) {
530
537
if (level > 0 ) {
531
538
this .asyncWebRequest .stateLock .unlock ();
0 commit comments