[Libwebsockets] multithreaded client and ring buffer question
andy at warmcat.com
Sat Jan 12 04:19:02 CET 2019
On 12/01/2019 09:50, Dave Horton wrote:
> Hi - I’m a bit of a newbie, looking forward to using this library to build a high-performance multithreaded websocket client.
It seems you understood the main point, which is lws runs in a single
thread with an event loop.
My program will need to establish multiple connections to many
different far-end web servers, and send a large amount of near real-time
data over those connections.
Multiple clients is no problem (in one event loop / thread).
> From reading the README docs and examples (thanks!) I think I have a basic idea of what I need to do (but interested in key things I am missing):
> - While my program will have many threads that want to send, I will have one wsi service thread running my callback, and just call ‘lws_callback_on_writable’ from the other threads
> - I will use ringbuffers to stash the data waiting to go out (e.g waiting to get a LWS_CALLBACK_CLIENT_WRITEABLE event).
> My first question is whether there is any documentation on the ringbuffer API that I could study? It looks like there are a bunch of useful functions, but if there is a good overview doc on them it would be helpful.
Not really... there's some docs in the header, and "documentation" in
the form of the related examples, eg
> Most of the ws client examples illustrate a client that initializes a
context and then creates a client connection right away.
> My case is slightly different — at startup I need to create the
context and then poll in the service thread, and then some time later a
foreign thread needs to connect to a remote endpoint.
> I know how a foreign thread can call call lws_callback_on_writable
when it has data to write on an existing connection, but how can it
signal the service thread so as to cause a new client connection
entirely to be made?
The best tool for thread synchronization is lws_cancel_service(). He's
very robust (the foreign thread simply adds a byte into a pipe) and lws
has automatically both created the pipe and set it up that any incoming
data on the lws end of it "causes an event" in the event loop. If the
"event loop" is poll(), it means the poll wait is immediately stopped
and lws will broadcast LWS_CALLBACK_EVENT_WAIT_CANCELLED to every
protocol on every vhost. In the case multiple threads called it before
we can respond, lws reads and discards all the pipe content, so it only
creates one cancel event instead of spamming n uselessly.
This is the recommended method to do any thread sync, including the
"there's something to write in a ringbuffer". In your code handling
LWS_CALLBACK_EVENT_WAIT_CANCELLED, you can take your mutex protecting
your shared structs / ringbuffers and find out what needs attention,
calling on_writable() from the lws context on affected wsi.
> Any other pointers or guidance welcome..
You don't have to use lws_ring, you can use some other ringbuffer
abstraction. But I actually use lws_ring in difficult cases like the
mirror protocol where it handles important but nonbvious situations like
rx flow control management.
... so it may be worth the effort to get it working based on the examples.
> Libwebsockets mailing list
> Libwebsockets at ml.libwebsockets.org
More information about the Libwebsockets