[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [libmicrohttpd] Some issues with MHD
From: |
Olivier Delhomme |
Subject: |
Re: [libmicrohttpd] Some issues with MHD |
Date: |
Sat, 7 Nov 2015 13:37:48 +0100 |
User-agent: |
Mutt/1.5.21 (2010-09-15) |
Hi Christian,
On Fri, Nov 06, 2015 at 11:00:05PM +0100, Christian Grothoff wrote:
> Reproduced, located, fixed: SVN 36643 (diff attached).
> Basically, contrary to what I was thinking, MHD wasn't somehow having an
> issue with the pool having allocations in it that would limit the
> available memory. The issue was simply that MHD would at the end of
> reading a request shrink the read buffer to the size that was needed for
> the request (to have more space for the write buffer). Then, for the
> next request, it would *only* grow the read buffer if it was strictly
> necessary (and not use the usual more generous allocation of half of the
> memory anymore). That's also why it never failed: if the read buffer
> was actually to small, MHD would eventually grow it. But that would only
> happen after the IO buffer had gotten MUCH too small for performance.
>
> Combined with the fact that it would take a while for the shrinkage to
> kick in (long-lasting multi-request POST session), this was not commonly
> observed.
>
> Anyway, should now really be killed. Thanks for the report & the testcase!
Yes :-)
Thanks a lot: tested against SVN 36643 I can not reproduce this
now. Perfect.
Happy Hacking ;-) !
> On 11/06/2015 10:09 PM, Olivier Delhomme wrote:
> > On Wed, Nov 04, 2015 at 11:00:42PM +0100, Christian Grothoff wrote:
> >> Hmm. That's not even clearly indicative of an MHD-related issue.
> >
> > I'm even not sure that I'm doing things the right way...
> >
> >> What you should look at is the buffer size MHD offers when calling
> >> recv() on the TCP stream. The size of the buffer MHD offers to the
> >> application can be smaller simply because the OS didn't receive 13k data
> >> chunks between calls anymore -- be it because the sender is slower, TCP
> >> had a congestion event or your receiver happens to be draining the
> >> buffer slightly faster. So there are many _other_ reasons that could
> >> explain a decrease in upload_buffer_size.
> >>
> >> So to diagnose this better, you first should check if the cause is
> >> really the size of the IO buffer offered by MHD to the kernel. Given
> >> that this is all for the same connection, I doubt this is the cause ---
> >> unless your application again doesn't always handle 100% of the data in
> >> each call, then you could of course also see such patterns emerge.
> >
> > I'm sorry but I'm not sure to understand what your are saying as I'm
> > not an expert in TCP nor network related kernel things.
> >
> > To diagnose it I managed to code a minimalistic server (160 lines with
> > comments) and a minimalistic client (200 lines with comments) that
> > simulates the behavior of my programs. It is published here:
> > https://github.com/dupgit/shrinkage
> >
> > I found the same behavior regarding the *upload_size value as you can
> > see in the *.png images and the file shrinkserver.output. The whole
> > run to observe this is about 6-7 minutes. I use the lo interface and
> > the throughput is around 80 Mbits/s. It has been tested on my laptop
> > that was running gnome desktop with very few opened applications.
> > The distribution is a Debian Jessie. shrinkserver is linked with
> > libmicrohttpd r36641 and shrinkclient is linked with system's libcurl
> > 7.38.0-4+deb8u2.
> >
> > Can you tell me if this code leads to something similar with your
> > environment ?
> Index: ChangeLog
> ===================================================================
> --- ChangeLog (revision 36640)
> +++ ChangeLog (working copy)
> @@ -1,3 +1,6 @@
> +Fri Nov 6 22:54:38 CET 2015
> + Fixing the buffer shrinkage issue, this time with test. -CG
> +
> Tue Nov 3 23:24:52 CET 2015
> Undoing change from Sun Oct 25 15:29:23 CET 2015
> as the original code was counter-intuitive but
> Index: src/include/microhttpd.h
> ===================================================================
> --- src/include/microhttpd.h (revision 36640)
> +++ src/include/microhttpd.h (working copy)
> @@ -130,7 +130,7 @@
> * Current version of the library.
> * 0x01093001 = 1.9.30-1.
> */
> -#define MHD_VERSION 0x00094600
> +#define MHD_VERSION 0x00094601
>
> /**
> * MHD-internal return code for "YES".
> Index: src/microhttpd/connection.c
> ===================================================================
> --- src/microhttpd/connection.c (revision 36640)
> +++ src/microhttpd/connection.c (working copy)
> @@ -2559,14 +2559,15 @@
> /* can try to keep-alive */
> connection->version = NULL;
> connection->state = MHD_CONNECTION_INIT;
> - /* read_buffer_size is correct here, as we want to
> - preserve the entire read buffer allocation, even
> - if in terms of the data we only care to preserve
> - up to "read_buffer_offset" */
> + /* Reset the read buffer to the starting size,
> + preserving the bytes we have already read. */
> connection->read_buffer
> = MHD_pool_reset (connection->pool,
> connection->read_buffer,
> - connection->read_buffer_size);
> + connection->read_buffer_offset,
> + connection->daemon->pool_size / 2);
> + connection->read_buffer_size
> + = connection->daemon->pool_size / 2;
> }
> connection->client_aware = MHD_NO;
> connection->client_context = NULL;
> Index: src/microhttpd/memorypool.c
> ===================================================================
> --- src/microhttpd/memorypool.c (revision 36640)
> +++ src/microhttpd/memorypool.c (working copy)
> @@ -219,7 +219,8 @@
> if ((pool->end < old_size) || (pool->end < asize))
> return NULL; /* unsatisfiable or bogus request */
>
> - if ((pool->pos >= old_size) && (&pool->memory[pool->pos - old_size] ==
> old))
> + if ( (pool->pos >= old_size) &&
> + (&pool->memory[pool->pos - old_size] == old) )
> {
> /* was the previous allocation - optimize! */
> if (pool->pos + asize - old_size <= pool->end)
> @@ -251,32 +252,40 @@
>
> /**
> * Clear all entries from the memory pool except
> - * for @a keep of the given @a size.
> + * for @a keep of the given @a size. The pointer
> + * returned should be a buffer of @a new_size where
> + * the first @a copy_bytes are from @a keep.
> *
> * @param pool memory pool to use for the operation
> * @param keep pointer to the entry to keep (maybe NULL)
> - * @param size how many bytes need to be kept at this address
> + * @param copy_bytes how many bytes need to be kept at this address
> + * @param new_size how many bytes should the allocation we return have?
> + * (should be larger or equal to @a copy_bytes)
> * @return addr new address of @a keep (if it had to change)
> */
> void *
> MHD_pool_reset (struct MemoryPool *pool,
> void *keep,
> - size_t size)
> + size_t copy_bytes,
> + size_t new_size)
> {
> if (NULL != keep)
> {
> if (keep != pool->memory)
> {
> - memmove (pool->memory, keep, size);
> + memmove (pool->memory,
> + keep,
> + copy_bytes);
> keep = pool->memory;
> }
> }
> pool->end = pool->size;
> - memset (&pool->memory[size],
> + /* technically not needed, but safer to zero out */
> + memset (&pool->memory[copy_bytes],
> 0,
> - pool->size - size);
> + pool->size - copy_bytes);
> if (NULL != keep)
> - pool->pos = ROUND_TO_ALIGN(size);
> + pool->pos = ROUND_TO_ALIGN (new_size);
> return keep;
> }
>
> Index: src/microhttpd/memorypool.h
> ===================================================================
> --- src/microhttpd/memorypool.h (revision 36640)
> +++ src/microhttpd/memorypool.h (working copy)
> @@ -99,16 +99,21 @@
>
> /**
> * Clear all entries from the memory pool except
> - * for "keep" of the given "size".
> + * for @a keep of the given @a copy_bytes. The pointer
> + * returned should be a buffer of @a new_size where
> + * the first @a copy_bytes are from @a keep.
> *
> * @param pool memory pool to use for the operation
> * @param keep pointer to the entry to keep (maybe NULL)
> - * @param size how many bytes need to be kept at this address
> - * @return addr new address of "keep" (if it had to change)
> + * @param copy_bytes how many bytes need to be kept at this address
> + * @param new_size how many bytes should the allocation we return have?
> + * (should be larger or equal to @a copy_bytes)
> + * @return addr new address of @a keep (if it had to change)
> */
> void *
> MHD_pool_reset (struct MemoryPool *pool,
> void *keep,
> - size_t size);
> + size_t copy_bytes,
> + size_t new_size);
>
> #endif
--
"Quand la vérité n'est pas libre, la liberté n'est pas vraie."
[Jacques Prévert]