[Libwebsockets] lws_cancel_service() and libev

Andy Green andy at warmcat.com
Sun Feb 16 05:40:35 CET 2020



On February 16, 2020 3:00:48 AM GMT, Olivier Langlois <olivier at olivierlanglois.net> wrote:
>> > - Why calling lws_cancel_service() from lws_create_context() to
>> > initialize the protocols?
>> 
>> If there is no network event, depending on the event loop backend
>> once we return to the event loop from create context, it may never
>> come back - ie, remain asleep -  to init the protocols otherwise.
>> 
>> > - Why not just calling lws_protocol_init() directly instead from
>> > lws_create_context()?
>> 
>> IIRC the main reason is it guarantees context creation completed and
>> any user code, typically in main(), that needed the context pointer
>> to init things (eg, to create explicit vhosts) before starting the
>> event loop has been able to get the context pointer and has
>> definitely run, before protocols get their PROTOCOL_INIT to set
>> themselves up on their vhosts.
>> 
>> > - If protocols were initialized before exiting
>> > lws_create_context(),
>> > the code wouldn't have to verify the protocol initialisation from
>> > various points (such as in lws_client_connect_via_info())...
>> 
>> That checking a flag (in lws internal code) is not very burdensome
>> and lets user code follow the convenient pattern of linearly creating
>> the context and then the vhosts.
>> 
>> Also in lws vhost creation and destruction is something that can
>> happen dynamically too, not just at init.
>> 
>Andy,
>
>thx for the explanations. I appreciate.
>
>Note that I realize nothing is very burdensome. If I did ask is because
>on the surface, making system calls and an IPC to postpone the call of
>a function did not seem like the most direct way to achieve the goal.
>
>If there exist a simpler and straighter way to achieve the same result,
>my guess is that noone would object even if it improve the code just by
>a little bit.
>
>what makes me cringe is calling lws_cancel_service() even before it has
>started. Maybe it is just me, it feels wrong in my stomach... but don't
>worry if you don't feel the same. It is not a big deal.

... it's just putting a byte in a pipe that it has a POLLIN wait on.  When we join the service loop or enter the poll wait, he'll see something to do one time without potentially sleeping forever since no network events.  See your next question below for why that's the correct way here.

This is directly analogous to breaking up large sends by going back to the event loop with a POLLOUT wait... gratuitously 'going back to the event loop' is a very common pattern in event loop programming.

>It is just a semantic issue. For the non-initiaed, it makes you wonder
>why the wait cancel handling code is inialising the protocol but the
>end result is perfect.

Wait cancel handling code is not 'initializing the protocol'.

>So, what I understand from your explanations is this:
>
>1. lws_protocol_init() has to be called from the service thread.
>2. User code could have things to do between the context creation and
>servicing it that are required by protocol initialisation.
>
>Then why not calling lws_protocol_init() from inside lws_service()?
>
>ie:
>
>        if (!pt->service_tid_detected) {
>                struct lws _lws;
>
>                memset(&_lws, 0, sizeof(_lws));
>                _lws.context = context;
>
>                pt->service_tid = context->vhost_list-
>>protocols[0].callback(
>                                        &_lws,
>LWS_CALLBACK_GET_THREAD_ID,
>                                        NULL, NULL, 0);
>                pt->service_tid_detected = 1;
>        	if (!context->protocol_init_done)
>                	lws_protocol_init(context);
>        }

Lws supports 'foreign event loops'... in that situation lws is an addendum to some existing app and its existing event loop that it joins as a guest... in fact the whole lws_context lifecycle itself is happening on a loop that existed before and continues after.  Your lws-related code then doesn't run the service at all nor is allowed to break it, nor has any idea what would happen to the outer app if it did.  As it is, lws works equally fine in that situation and your proposed flow doesn't.

Lws is very flexible, mostly that flexibility is managed by build-time options or ops structs and is just out of the picture wholesale if disabled, but in cases like this choices get made in the core code to do things consistent with ways of using lws you may not be thinking about for your own use-case.

-Andy



More information about the Libwebsockets mailing list