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

Roger Light roger at atchoo.org
Mon Aug 18 11:59:52 CEST 2014


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> wrote:
>
>>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:
>>
>>case LWS_CALLBACK_SERVER_WRITEABLE:
>>    /* 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 guaranteed.
>
> 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
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 -->
>
>>case LWS_CALLBACK_SERVER_WRITEABLE:
>>    /* 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 send()
> libwebsockets_write() eventually does never returns -EWOULDBLOCK or whatever,
> 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
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().

Cheers,

Roger



More information about the Libwebsockets mailing list