[Libwebsockets] lws_service and lws_cancel_service questions

Andy Green andy at warmcat.com
Thu May 19 23:29:33 CEST 2016

On May 19, 2016 10:14:15 PM GMT+08:00, Cooper Faust <cooperrf at yahoo.com> wrote:
>I built a broker that transfers messages back and forth between ZMQ
>sockets and web sockets.  Everything is working as expected, but I'm
>not sure I completely understand how lws_service works.
>Here is my experience when I set the lws_service timeout relatively
>high (an hour):- I start a thread that calls lws_service from within a
>- lws_service blocks while waiting for something to happen.  
>- Actions that originate "within" the context (client connects, client
>sends a message, client disconnects) are processed immediately.  
>- Actions that originate "outside" the context (a message from ZMQ is
>sent to the clients) are not processed immediately, they wait for
>lws_service to stop blocking.  
>- When lws_service returns, it then processes an action, returns,
>processes the next action, etc. ...until all the queued actions are
>Because of this experience, I set the lws_service timeout relatively
>low (100ms), so not to introduce too much lag to the user.
>By the way, I am calling lws_callback_on_writable for each client I
>want to send a message to.
>I just discovered the lws_cancel_service method (I just switched to
>libwebsockets 2.0 from 1.6.3...) and it got me thinking:- Can I set the

That's not a new api... the underlying thing causing this deferred response is that when you enter poll(), the kernel takes a copy of the .events it's waiting on.

When you come from another thread and do lws_callback_on_writable(), which is legal, it sets POLLOUT in the userland copy of .events, but that doesn't reach the copy the kernel took when it entered the poll().  It's only when we come back and start a new poll(), it takes a new copy of the .events and can respond.

>timeout to a high value, but call lws_cancel_service to stop blocking
>and return from lws_service after calling lws_callback_on_writable?
>Actually, I've already answered my question above.  Yes, it does work. 

The deferred update thing doesn't occur in the usual case there is only the one thread involved... by definition if we are calling lws_callback_on_writable() on that thread, we're not in the poll() wait.  After we finished doing what we're doing, we will return to the poll() wait with the new .events, which then apply immediately.

Lws can detect you called lws_callback_on_writable() from another thread context and apply the cancel service call automatically.  You just need to handle LWS_CALLBACK_GET_THREAD_ID (on protocol 0) to return an opaque, integer current thread id, so lws can detect the tid it entered poll() with and the tid changing the .events are different.

>But *should* I do it that way?  
>Does lws_cancel_service cause unprocessed actions to be dropped or does

It uses a pipe that was set up in lws for this purpose to create an event that will wake the otherwise idle poll().  The pipe event is swallowed, any real events that happened along at the same moment get serviced normally.

The api should better be called "lws_end_service_wait()" or "lws_interrupt_poll()".

>it prevent complete processing of a current action?Is this a reasonable
>implementation?  Or would it be better to go back to a very short
>timeout?Is there some performance penalty one way or the other?

Making poll() restart at short intervals is effective, but it is making a low level background noise of cpu wakeups that are alnost entirely not needed, and introducing unpredictable service jitter between 0 and the timeout period each time.

"Canceling service" after lws_callback_on_writable() from the other thread is safe, or you can get lws to detect the situation via the thread id callback, either should be fine.  This will get the changed events applied "immediately".


>Libwebsockets mailing list
>Libwebsockets at ml.libwebsockets.org

More information about the Libwebsockets mailing list