Uploaded image for project: 'RESTEasy'
  1. RESTEasy
  2. RESTEASY-1764

Asynchronous execution resource leak

    XMLWordPrintable

Details

    Description

      1st case

      Let's considerer following SSE resource:

      @Path("/server-sent-events-leak")
      public class ResourceLeakSseResource {
      
      	private final Object outputLock = new Object();
      
      	private volatile SseEventSink eventSink;
      
      	@GET
      	@Produces(MediaType.SERVER_SENT_EVENTS)
      	public void getMessageQueue(@Context SseEventSink eventSink, @Context Sse s) {
      		synchronized (this.outputLock) {
      			if (this.eventSink != null) {
      				throw new IllegalStateException("Server sink already served.");
      			}
      		}
      		this.eventSink = eventSink;
      		this.eventSink.send(s.newEvent("data"));
      		throw new RuntimeException();
      	}
      
      	@DELETE
      	public void close() {
      		synchronized (this.outputLock) {
      			if (this.eventSink != null) {
      				this.eventSink.close();
      				this.eventSink = null;
      			}
      		}
      	}
      
      }
      
      • Running on a server that does not support async

      When calling the getMessageQueue(...) resource the server will send back to the client a 200 response followed by the "data" SseEvent. Then on throwing the RuntimeException
      resteasy core methods org.jboss.resteasy.core.SynchronousExecutionContext.SynchronousAsynchronousResponse.resume(Throwable exc) will be invoked. This method is supposed to countdown the latch on complete but since the HttpResponse as already been commited (which is a good thing) when sending previous 200 response, the latch will never be counted down. See org.jboss.resteasy.core.SynchronousDispatcher.writeException(...):

            if (response.isCommitted())
            {
               LogMessages.LOGGER.debug(Messages.MESSAGES.responseIsCommitted());
               return;
            }
      

      So the state of the current org.jboss.resteasy.core.SynchronousExecutionContext.SynchronousAsynchronousResponse will be now "done" and future invocation by another thread of whichever method supposed to counted down the latch will be useless since the state is already set to "done".
      So as a result the calling thread will wait forever on method org.jboss.resteasy.core.SynchronousDispatcher.invoke(HttpRequest request, HttpResponse response, ResourceInvoker invoker):

         public void invoke(HttpRequest request, HttpResponse response, ResourceInvoker invoker)
         {
            Response jaxrsResponse = null;
            try
            {
               jaxrsResponse = invoker.invoke(request, response);
               if (request.getAsyncContext().isSuspended())
               {
                  /**
                   * Callback by the initial calling thread.  This callback will probably do nothing in an asynchronous environment
                   * but will be used to simulate AsynchronousResponse in vanilla Servlet containers that do not support
                   * asychronous HTTP.
                   *
                   */
                  request.getAsyncContext().getAsyncResponse().initialRequestThreadFinished();
                  jaxrsResponse = null; // we're handing response asynchronously
               }
            }
      ...
         }
      
      • Running on a server that support async

      The same will occur leading us to javax.servlet.AsyncContext.complete() never invoked and maybe the connection and resources associated to the async execution never released.

      For this case see org.jboss.resteasy.plugins.server.servlet.Servlet3ExecutionContext.Servle3AsychronousResponse.resume(Throwable) and org.jboss.resteasy.plugins.server.netty.NettyHttpRequest.NettyExecutionContext.NettyHttpAsyncResponse.resume(Throwable)

      2nd case

      Let's considerer following SSE resource:

      	@Path("async")
      	@GET
      	@Produces(MediaType.APPLICATION_JSON)
      	public void async(final @Suspended AsyncResponse async) {
      		async.cancel();
      	}
      
      • Running on a server that does not support async

      Just like the previous case a simple call to this resource will lead to a thread waiting forever on the same exact line of org.jboss.resteasy.core.SynchronousDispatcher.invoke(HttpRequest request, HttpResponse response, ResourceInvoker invoker) since org.jboss.resteasy.core.SynchronousExecutionContext.SynchronousAsynchronousResponse.cancel(..) does not counted down the latch.

      Attachments

        Issue Links

          Activity

            People

              rhn-support-asoldano Alessio Soldano
              nicones Nicolas NESMON
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: