Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [jetty-users] Concurrency issues with the WebSocket client

single-invocation-per-endpoint-instance contract WSC-5.1-2 does not apply to a
@OnMessage annotated method that works with Streams.

The very nature of @OnMessage with streams is that the first frame of a message causes an invocation that is dispatched to a new thread, and all subsequent frames for that message arrive via that dispatched thread (really via the buffers that the thread has an is using)

If another Message arrives, then that is also dispatched to a new thread, etc.



--
Joakim Erdfelt <joakim@xxxxxxxxxxx>
Expert advice, services and support from from the Jetty & CometD experts

On Thu, Jul 2, 2015 at 3:39 AM, Jörg Henne <hennejg@xxxxxxxxx> wrote:
Hi,

with a WebSocket client implemented using Jetty 9.2.11 I am experiencing
strange concurrency behaviour with which I have difficulties telling "works
as designed" from "that's a bug".

The client in Question is implemented using the @ClientEndpoint annotation.
It has an onMessage method annotated with @OnMessage. The client is
initialized using a pattern along the lines of
ContainerProvider.getWebSocketContainer().connectToServer(theClient,
serverURI);.

In situations where the server sends a few messages in rapid succession, I
experienced unexpected application behaviour which I tracked down to
concurrent calls to the onMessage method being made.

I verified that observation using something like this:

  AtomicInteger _onMessageConcurrency_ = new AtomicInteger();

  @OnMessage
  public void onMessage(Serializable message) throws InterruptedException {
    final int c = onMessageConcurrency.incrementAndGet();
    try {
      if (c > 1)
        LOGGER.warn("Concurrent calls of onMessage >1: " + c);

      ... do something
    } finally {
      onMessageConcurrency.decrementAndGet();
    }
  }

To me this seems to be a violation of the
single-invocation-per-endpoint-instance contract WSC-5.1-2 in the
specification. To make things even worse, even if the onMessage method is
made synchronized, the calls may happen in a different order from the one in
which the messages were sent, thus destroying the message ordering.

A few things I already looked at are
- there are indeed different threads that call into onMethod. Their stacks
look something like this:

  WebSocketJobController.onMessage(Serializable) line: 332
  NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not
available [native method]
  NativeMethodAccessorImpl.invoke(Object, Object[]) line: 57
  DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43
  Method.invoke(Object, Object...) line: 601
  OnMessageTextStreamCallable(CallableMethod).call(Object, Object...) line: 70
  OnMessageTextStreamCallable.call(Object, Reader) line: 60
  JsrEvents<T,C>.callTextStream(RemoteEndpoint$Async, Object, Reader) line: 206
  JsrAnnotatedEventDriver$2.run() line: 340
  QueuedThreadPool.runJob(Runnable) line: 635
  QueuedThreadPool$3.run() line: 555
  Thread.run() line: 722

- it is indeed the same client instance the calls are being made on
- the server side sends the messages in the correct order using a single
thread using synchronous delivery via session.getBasicRemote().sendObject(...)

So, I'd love to hear your opinion on this. Is this a bug or correct
behaviour? The specification doesn't explicitly mention the client with
respect to the single-thread-rule, but the client is certainly just an
endpoint so I think it should apply.

Thanks
Jörg Henne

_______________________________________________
jetty-users mailing list
jetty-users@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://dev.eclipse.org/mailman/listinfo/jetty-users


Back to the top