DeferredResult accessors based on volatile fields for proper visibility

Issue: SPR-13451
master
Juergen Hoeller 9 years ago
parent 4dee9cbf62
commit ae0d945a05
  1. 35
      spring-web/src/main/java/org/springframework/web/context/request/async/DeferredResult.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2013 the original author or authors. * Copyright 2002-2015 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.springframework.web.context.request.async; package org.springframework.web.context.request.async;
import java.util.PriorityQueue; import java.util.PriorityQueue;
@ -64,9 +65,9 @@ public class DeferredResult<T> {
private DeferredResultHandler resultHandler; private DeferredResultHandler resultHandler;
private Object result = RESULT_NONE; private volatile Object result = RESULT_NONE;
private boolean expired; private volatile boolean expired;
/** /**
@ -98,33 +99,34 @@ public class DeferredResult<T> {
this.timeout = timeout; this.timeout = timeout;
} }
/** /**
* Return {@code true} if this DeferredResult is no longer usable either * Return {@code true} if this DeferredResult is no longer usable either
* because it was previously set or because the underlying request expired. * because it was previously set or because the underlying request expired.
* <p> * <p>The result may have been set with a call to {@link #setResult(Object)},
* The result may have been set with a call to {@link #setResult(Object)},
* or {@link #setErrorResult(Object)}, or as a result of a timeout, if a * or {@link #setErrorResult(Object)}, or as a result of a timeout, if a
* timeout result was provided to the constructor. The request may also * timeout result was provided to the constructor. The request may also
* expire due to a timeout or network error. * expire due to a timeout or network error.
*/ */
public final boolean isSetOrExpired() { public final boolean isSetOrExpired() {
return ((this.result != RESULT_NONE) || this.expired); return (this.result != RESULT_NONE || this.expired);
} }
/** /**
* @return {@code true} if the DeferredResult has been set. * Return {@code true} if the DeferredResult has been set.
*/ */
public boolean hasResult() { public boolean hasResult() {
return this.result != RESULT_NONE; return (this.result != RESULT_NONE);
} }
/** /**
* @return the result or {@code null} if the result wasn't set; since the result can * Return the result, or {@code null} if the result wasn't set. Since the result
* also be {@code null}, it is recommended to use {@link #hasResult()} first * can also be {@code null}, it is recommended to use {@link #hasResult()} first
* to check if there is a result prior to calling this method. * to check if there is a result prior to calling this method.
*/ */
public Object getResult() { public Object getResult() {
return hasResult() ? this.result : null; Object resultToCheck = this.result;
return (resultToCheck != RESULT_NONE ? resultToCheck : null);
} }
/** /**
@ -165,12 +167,12 @@ public class DeferredResult<T> {
Assert.notNull(resultHandler, "DeferredResultHandler is required"); Assert.notNull(resultHandler, "DeferredResultHandler is required");
synchronized (this) { synchronized (this) {
this.resultHandler = resultHandler; this.resultHandler = resultHandler;
if ((this.result != RESULT_NONE) && (!this.expired)) { if (this.result != RESULT_NONE && !this.expired) {
try { try {
this.resultHandler.handleResult(this.result); this.resultHandler.handleResult(this.result);
} }
catch (Throwable t) { catch (Throwable ex) {
logger.trace("DeferredResult not handled", t); logger.trace("DeferredResult not handled", ex);
} }
} }
} }
@ -214,9 +216,9 @@ public class DeferredResult<T> {
return setResultInternal(result); return setResultInternal(result);
} }
final DeferredResultProcessingInterceptor getInterceptor() { final DeferredResultProcessingInterceptor getInterceptor() {
return new DeferredResultProcessingInterceptorAdapter() { return new DeferredResultProcessingInterceptorAdapter() {
@Override @Override
public <S> boolean handleTimeout(NativeWebRequest request, DeferredResult<S> deferredResult) { public <S> boolean handleTimeout(NativeWebRequest request, DeferredResult<S> deferredResult) {
if (timeoutCallback != null) { if (timeoutCallback != null) {
@ -227,7 +229,6 @@ public class DeferredResult<T> {
} }
return true; return true;
} }
@Override @Override
public <S> void afterCompletion(NativeWebRequest request, DeferredResult<S> deferredResult) { public <S> void afterCompletion(NativeWebRequest request, DeferredResult<S> deferredResult) {
synchronized (DeferredResult.this) { synchronized (DeferredResult.this) {

Loading…
Cancel
Save