diff --git a/examples/src/main/java/io/grpc/examples/manualflowcontrol/ManualFlowControlServer.java b/examples/src/main/java/io/grpc/examples/manualflowcontrol/ManualFlowControlServer.java index abd04fa9ad5..694330dfdb6 100644 --- a/examples/src/main/java/io/grpc/examples/manualflowcontrol/ManualFlowControlServer.java +++ b/examples/src/main/java/io/grpc/examples/manualflowcontrol/ManualFlowControlServer.java @@ -24,7 +24,6 @@ import java.io.IOException; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; public class ManualFlowControlServer { @@ -42,12 +41,6 @@ public StreamObserver sayHelloStreaming(final StreamObserver) responseObserver; serverCallStreamObserver.disableAutoInboundFlowControl(); - // Guard against spurious onReady() calls caused by a race between onNext() and onReady(). If the transport - // toggles isReady() from false to true while onNext() is executing, but before onNext() checks isReady(), - // request(1) would be called twice - once by onNext() and once by the onReady() scheduled during onNext()'s - // execution. - final AtomicBoolean wasReady = new AtomicBoolean(false); - // Set up a back-pressure-aware consumer for the request stream. The onReadyHandler will be invoked // when the consuming side has enough buffer space to receive more messages. // @@ -55,10 +48,17 @@ public StreamObserver sayHelloStreaming(final StreamObserver() { @@ -90,16 +92,17 @@ public void onNext(HelloRequest request) { // Check the provided ServerCallStreamObserver to see if it is still ready to accept more messages. if (serverCallStreamObserver.isReady()) { // Signal the sender to send another request. As long as isReady() stays true, the server will keep - // cycling through the loop of onNext() -> request()...onNext() -> request()... until either the client - // runs out of messages and ends the loop or the server runs out of receive buffer space. + // cycling through the loop of onNext() -> request(1)...onNext() -> request(1)... until the client runs + // out of messages and ends the loop (via onCompleted()). // - // If the server runs out of buffer space, isReady() will turn false. When the receive buffer has - // sufficiently drained, isReady() will turn true, and the serverCallStreamObserver's onReadyHandler - // will be called to restart the message pump. + // If request() was called here with the argument of more than 1, the server might runs out of receive + // buffer space, and isReady() will turn false. When the receive buffer has sufficiently drained, + // isReady() will turn true, and the serverCallStreamObserver's onReadyHandler will be called to restart + // the message pump. serverCallStreamObserver.request(1); } else { // If not, note that back-pressure has begun. - wasReady.set(false); + onReadyHandler.wasReady = false; } } catch (Throwable throwable) { throwable.printStackTrace(); diff --git a/stub/src/main/java/io/grpc/stub/CallStreamObserver.java b/stub/src/main/java/io/grpc/stub/CallStreamObserver.java index 8ed16bb96eb..98fa6fba57e 100644 --- a/stub/src/main/java/io/grpc/stub/CallStreamObserver.java +++ b/stub/src/main/java/io/grpc/stub/CallStreamObserver.java @@ -19,20 +19,30 @@ import io.grpc.ExperimentalApi; /** - * A refinement of StreamObserver provided by the GRPC runtime to the application that allows for - * more complex interactions with call behavior. + * A refinement of StreamObserver provided by the GRPC runtime to the application (the client or + * the server) that allows for more complex interactions with call behavior. * - *

In any call there are logically two {@link StreamObserver} implementations: + *

In any call there are logically four {@link StreamObserver} implementations: *

    - *
  • 'inbound' - which the GRPC runtime calls when it receives messages from the - * remote peer. This is implemented by the application. + *
  • 'inbound', client-side - which the GRPC runtime calls when it receives messages from + * the server. This is implemented by the client application and passed into a service method + * on a stub object. *
  • - *
  • 'outbound' - which the GRPC runtime provides to the application which it uses to - * send messages to the remote peer. + *
  • 'outbound', client-side - which the GRPC runtime provides to the client application and the + * client uses this {@code StreamObserver} to send messages to the server. + *
  • + *
  • 'inbound', server-side - which the GRPC runtime calls when it receives messages from + * the client. This is implemented by the server application and returned from service + * implementations of client-side streaming and bidirectional streaming methods. + *
  • + *
  • 'outbound', server-side - which the GRPC runtime provides to the server application and + * the server uses this {@code StreamObserver} to send messages (responses) to the client. *
  • *
* - *

Implementations of this class represent the 'outbound' message stream. + *

Implementations of this class represent the 'outbound' message streams. The client-side + * one is {@link ClientCallStreamObserver} and the service-side one is + * {@link ServerCallStreamObserver}. * *

Like {@code StreamObserver}, implementations are not required to be thread-safe; if multiple * threads will be writing to an instance concurrently, the application must synchronize its calls.