Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [jetty-users] Asynchronous servlets - thread safety/synchronization & reusing objects

Hi Bartek,

Firstly, thanks for a nice, lucid explanation of what you're doing.

I went to the code to find you a good example of what to do with continuations
and servlet3 async handling, and discovered I couldn't find one! So we will
definitely have to rectify that!

Ideally I think your asynchronous worker would have a shorter timeout than
the continuation timeout, but that said, you are correct that you need to
do some work in your code to ensure atomicity.

For section 1, where your asynchronous worker returns after the continuation
has expired, you can use the ContinuationListener.onComplete and onTimeout
methods to help you here. You need some application object that you
can synchronize
on in them, and in your asynchronous worker. In the sync block in
onComplete and
onTimeout you then need to set up some application state to indicate that the
continuation is finished with. You then check that application state inside the
sync block of your asynchronous worker before changing the response.

I think the above also answers your second point. What you synchronize on
should be an object meaningful to the application, not one that the container
is also operating on at the same time.

BTW there are servlet3 equivalents to the ContinuationListener methods too,
so the same logic applies.

I've opened this enhancement request to track the creation of a good fully
worked example of using async and continuations:

https://bugs.eclipse.org/bugs/show_bug.cgi?id=363407



Jan

On 10 November 2011 08:57, Bartek K. <kowalewski.bartosz@xxxxxxxxx> wrote:
> Hi Jetty gurus,
>
> I've just started creating a simple asynchronous servlet running on
> top of a Jetty8 server. I first tried using the Jetty native API
> (continuations) as I used to employ a similar approach when creating
> Jetty6-based server-side components. Then I switched to the Servlet3
> API to make my code less coupled to Jetty classes. In both cases I had
> some issues with implementing proper synchronization mechanisms that
> would ensure that timeout tasks do not interfere with proper responses
> that cause continuations to be resumed around the time timeout
> happens. I also had problems with handling late responses - responses
> that might potentially cause the continuation to be resumed long after
> timeout happened.
>
> Some facts:
>
> I. Jetty reuses plenty of objects. This includes continuations,
> servlet request objects, servlet response objects, ... Let's imagine
> this scenario:
> 1. The servlet gets a new request (Req1), a continuation object
> (Cont1) is retrieved  from ContinuationSupport
> 2. Continuation Cont1 is suspended
> 3. A timeout is triggered for Cont1, it causes a response to be sent
> to the client
> 4. The client sends a new request (Req2) over the same TCP connection
> (reuses the connection), Jetty knows that Req1 was finished, so it
> resets Cont1 and reuses it
> 5. Cont1 for Req2 is suspended
> 6. Late response for request Req1 comes; it cannot use the
> continuation that was assigned to Req1 as this continuation is already
> used by Req2
> 7. When creating the servlet I need to implement my custom loigic for
> checking if I'm still allowed to set the response or if timeout has
> already expired. Just checking isExpired() is not enough as the
> continuation has been reset
>
> II. When expiring a continuation, Jetty first changes the state of the
> continuation in a safe 'synchronized' block of code and then calls
> listeners (continuation listeners & servlet3 async listeners) outside
> of this block. In order to ensure that my servlet doesn't reach an
> inconsistent state when handling a given request (i.e. proper response
> sets HTTP code on the response while the listener is in its onTimeout
> method), I need to put all o the actions responsible for setting the
> response into a synchronized block.
>
> Here's how I've done it when using Jetty native API:
>
> 1. Setting up the continuation:
> {code}
> UUID uuid = UUID.randomUUID();
> continuation.setAttribute(UUID_ATTRIBUTE_NAME, uuid);
> continuation.addContinuationListener(new ContinuationListener()
>            {
>                public void onTimeout(Continuation continuation) {
>                    try {
>                        synchronized (continuation) {
>                            HttpServletResponse response =
> (HttpServletResponse) continuation.getServletResponse();
>                            response.setContentType("text/html");
>                            response.setStatus(;
>                            response.getWriter().println("");
>                            continuation.complete();
>                            continuation.removeAttribute(UUID_ATTRIBUTE_NAME);
>                        }
>                    } catch (Exception e) {
>                      ...
>                    }
>                }
>
>                public void onComplete(Continuation continuation) {
>                     ...
>                }
>  });
> continuation.suspend(response);
> ...
> {code}
>
> Handling respone:
> {code}
> // my internal code keeps track of the continuation assigned to the
> given exchange and uuid of the exchange, so that I can resume/complete
> the continuation when processing is done
> synchronized (continuation) {
>                    if
> (uuid.equals(continuation.getAttribute(UUID_ATTRIBUTE_NAME)) &&
> !continuation.isExpired()) {
>                        HttpServletResponse response =
> (HttpServletResponse) continuation.getServletResponse();
>                        response.setContentType();
>                        response.setStatus();
>                        response.getWriter().println("");
>                        continuation.complete();
>                        continuation.removeAttribute(UUID_ATTRIBUTE_NAME);
>                    }
>                }
> {code}
>
> ... so I need some advice. Does this code look safe? It's based on
> initial static analysis o Jetty8 source code and some debugging, but I
> haven't carried out thorough tests yet, so I'm not 100% sure that is
> ok.
>
> I also took a look at examples shipped with Jetty. I haven't found any
> such mechanisms there. Shouldn't mechanism similar to the one I used
> be introduced in these examples? I know that my approach is far from
> being optimal :), but it seams that some synchronization is needed in
> these examples.
>
> As I mentioned at the very begining , I also tried the Servlet3 async
> API. I did some tests and it seems that similiar issues apply here. My
> simple AsyncContext-based servlet seemed to be called from the same
> impl of continuation as in the Jetty native API based example. It was
> called from AsyncContinuation. This suggests that similar
> synchronization/uuid stuff needs to be introduced there. While
> introducing uuids there is not a problem, synchronization seems to be
> an issue. Jetty code suggests that the only way to make my response
> handling code properly synchronized is to synchronize this block on
> the continuation object that is used by Jetty underneath (similary to
> what is done in the second snippet above). The issue is that
> introducing such synchronization block would make my servlet
> non-Jetty-agnostic. Any thoughts? Any good examples out there?
>
> Sorry for a longish e-mail and thanks for your help!
>  Bartek
> _______________________________________________
> jetty-users mailing list
> jetty-users@xxxxxxxxxxx
> https://dev.eclipse.org/mailman/listinfo/jetty-users
>


Back to the top