[Libwebsockets] Truncated send handled by user not working as expected

Andy Green andy at warmcat.com
Mon Aug 18 13:00:54 CEST 2014

On 18 August 2014 17:59:52 GMT+08:00, Roger Light <roger at atchoo.org> wrote:
>On Mon, Aug 18, 2014 at 10:19 AM, Andy Green <andy at warmcat.com> wrote:
>> On 18 August 2014 17:00:17 GMT+08:00, Roger Light <roger at atchoo.org>
>>>I believe I could trigger the assert easily on Linux by calling
>>>libwebsocket_write() without checking lws_send_pipe_choked() first. I
>>>was checking that the socket was available to write, then attempting
>>>to write as much as possible until it blocked. So crudely, and iirc:
>>>    /* Gives assert */
>>>    while(have_data){
>>>        count = libwebsockets_write(len);
>>>        if(count < 0) return 0;
>>>        if(wlen != len) break;
>>>    }
>> Yeah you can't do that.  Say you send a lot each time like 1MByte.
>> The kernel won't accept that much for buffering, so a partial send is
>> Once lws took on dealing with your partial send, flushing the
>buffered remainder
>> has to take priority over everything else, since it has to get sent
>first before anything else.
>If I'm reading what Nilson said correctly, he has configured lws so
>that he is dealing with partial writes, not the library. The same is
>true in my case. In that situation I would expect to be able to call

What did you actually do to "configure lws so [you] are dealing with partial writes"?

>libwebsockets_write() in a loop as above, and monitor the return code
>and possible errno so that I knew when the kernel buffers were full.
>If I replace libwebsockets_write() with just write() then the loop
>above is valid, I just need to handle the return code differently to
>detect EAGAIN and actual errors.
>> When there was a partial send, a writable callback is scheduled
>automatically, and
>> before the user callback is notified, lws checks to see if there is a
>pending partial send.
>> If so, it will send as much of the pending buffer as the kernel will
>take.  If it was
>> everything, then the partial send is marked as emptied out and the
>next writeable
>> callback will get passed through to the user code again to start
>sending something new.
>> What your asserting code does is ignore all that and just sit
>looping, blocking and
>> spamming more new stuff into libwebsockets_write() every time without
>going around
>> the service loop and let it dump what it buffered.
>What the loop does is expect libwebsockets_write() to tell me if a
>write wasn't possible, just the way that write() and send() do.
>> On the other hand -->
>>>    /* No assert */
>>>    while(have_data && !lws_send_pipe_choked()){
>>>        count = libwebsockets_write(len);
>>>        if(count < 0) return 0;
>>>        if(wlen != len) break;
>>>    }
>> This makes sense because the test for if we can write means that the
>> libwebsockets_write() eventually does never returns -EWOULDBLOCK or
>> because we already checked he can actually send.
>I'd suggest this is the wrong approach. You're adding the requirement
>for an extra poll() call for each additional time

I justed talked about why the two chunks of code acted differently, from looking at that code, not created a "requirement".

>libwebsockets_write() is called, versus having one extra call to
>libwebsockets_write() that returns to say the socket wasn't writeable.
>Put another way, if poll tells me that I can write, I can write until
>I'm told not to. I don't need to check poll again after each time I
>call write().

Yes ignoring all that, the code is not reliable anyway, since the choked state of the pipe may change aynchronously due to whatever else in going on in the system.  But it's still useful to loop and pack the pipe while it's relatively empty and you have many small writes.

I'm happy if you think you identified a problem, and know how to fix it, but I'm still not sure what it is yet.  (Maybe it's simpler to send a patch?)



More information about the Libwebsockets mailing list