You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@subversion.apache.org by Stefan Sperling <st...@elego.de> on 2011/06/24 14:44:45 UTC
serf infinite loop in svn_ra_serf__handle_xml_parser()
Trying to update my working copy I observed svn spinning on the CPU
forever. Attaching with gdb showed that it was stuck in an endless
loop inside svn_ra_serf__handle_xml_parser().
Below is the code. The problem was that serf_bucket_read() returned
zero bytes but also indicated success. If that happens the loop never
terminates.
I suppose this code was written with the assumption that reading
from the socket would always block until data is available?
This does not seem to happen in my case. Maybe serf is doing something
wrong when it opens the socket?
I'm using serf-0.7.x from the branch as of June 21.
while (1)
{
const char *data;
apr_size_t len;
status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
if (SERF_BUCKET_READ_ERROR(status))
{
return svn_error_wrap_apr(status, NULL);
}
/* Note: once the callbacks invoked by inject_to_parser() sets the
PAUSED flag, then it will not be cleared. write_to_pending() will
only save the content. Logic outside of serf_context_run() will
clear that flag, as appropriate, along with processing the
content that we have placed into the PENDING buffer.
We want to save arriving content into the PENDING structures if
the parser has been paused, or we already have data in there (so
the arriving data is appended, rather than injected out of order) */
#ifdef DISABLE_THIS_FOR_NOW
if (ctx->paused || HAS_PENDING_DATA(ctx->pending))
{
err = write_to_pending(ctx, data, len, pool);
}
else
#endif
{
err = inject_to_parser(ctx, data, len, &sl);
if (err)
{
/* Should have no errors if IGNORE_ERRORS is set. */
SVN_ERR_ASSERT(!ctx->ignore_errors);
}
}
if (err)
{
XML_ParserFree(ctx->xmlp);
add_done_item(ctx);
return svn_error_return(err);
}
if (APR_STATUS_IS_EAGAIN(status))
{
return svn_error_wrap_apr(status, NULL);
}
if (APR_STATUS_IS_EOF(status))
{
if (ctx->pending != NULL)
ctx->pending->network_eof = TRUE;
/* We just hit the end of the network content. If we have nothing
in the PENDING structures, then we're completely done. */
if (!HAS_PENDING_DATA(ctx->pending))
{
/* Ignore the return status. We just don't care. */
(void) XML_Parse(ctx->xmlp, NULL, 0, 1);
XML_ParserFree(ctx->xmlp);
add_done_item(ctx);
}
return svn_error_wrap_apr(status, NULL);
}
/* feed me! */
}
Re: serf infinite loop in svn_ra_serf__handle_xml_parser()
Posted by Lieven Govaerts <lg...@mobsol.be>.
On Fri, Jun 24, 2011 at 2:44 PM, Stefan Sperling <st...@elego.de> wrote:
> Trying to update my working copy I observed svn spinning on the CPU
> forever. Attaching with gdb showed that it was stuck in an endless
> loop inside svn_ra_serf__handle_xml_parser().
>
> Below is the code. The problem was that serf_bucket_read() returned
> zero bytes but also indicated success. If that happens the loop never
> terminates.
>
> I suppose this code was written with the assumption that reading
> from the socket would always block until data is available?
No, serf is non-blocking! It's ok that serf returns 0 bytes read but
still APR_SUCCESS, but that should happen only once. The second time
svn reads something it should either return more data, or 0 with
status APR_EOF.
> This does not seem to happen in my case. Maybe serf is doing something
> wrong when it opens the socket?
>
> I'm using serf-0.7.x from the branch as of June 21.
>
Assuming you have a working copy checkout out from https://s.a.o, en
you haven't disabled gzip compression, my guess is that you're
encountering the same issue I have documented here:
http://groups.google.com/group/serf-dev/browse_thread/thread/56fcd4f25b290e29
In my scenario it loops in handle_fetch instead of the xml parser, but
it's basically the same case.
Serf trunk r1481 ensures the loop ends with an error in this situation.
Lieven
> while (1)
> {
> const char *data;
> apr_size_t len;
>
> status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
>
> if (SERF_BUCKET_READ_ERROR(status))
> {
> return svn_error_wrap_apr(status, NULL);
> }
>
> /* Note: once the callbacks invoked by inject_to_parser() sets the
> PAUSED flag, then it will not be cleared. write_to_pending() will
> only save the content. Logic outside of serf_context_run() will
> clear that flag, as appropriate, along with processing the
> content that we have placed into the PENDING buffer.
>
> We want to save arriving content into the PENDING structures if
> the parser has been paused, or we already have data in there (so
> the arriving data is appended, rather than injected out of order) */
> #ifdef DISABLE_THIS_FOR_NOW
> if (ctx->paused || HAS_PENDING_DATA(ctx->pending))
> {
> err = write_to_pending(ctx, data, len, pool);
> }
> else
> #endif
> {
> err = inject_to_parser(ctx, data, len, &sl);
> if (err)
> {
> /* Should have no errors if IGNORE_ERRORS is set. */
> SVN_ERR_ASSERT(!ctx->ignore_errors);
> }
> }
> if (err)
> {
> XML_ParserFree(ctx->xmlp);
> add_done_item(ctx);
> return svn_error_return(err);
> }
>
> if (APR_STATUS_IS_EAGAIN(status))
> {
> return svn_error_wrap_apr(status, NULL);
> }
>
> if (APR_STATUS_IS_EOF(status))
> {
> if (ctx->pending != NULL)
> ctx->pending->network_eof = TRUE;
>
> /* We just hit the end of the network content. If we have nothing
> in the PENDING structures, then we're completely done. */
> if (!HAS_PENDING_DATA(ctx->pending))
> {
> /* Ignore the return status. We just don't care. */
> (void) XML_Parse(ctx->xmlp, NULL, 0, 1);
>
> XML_ParserFree(ctx->xmlp);
> add_done_item(ctx);
> }
>
> return svn_error_wrap_apr(status, NULL);
> }
>
> /* feed me! */
> }
>
>
Re: serf infinite loop in svn_ra_serf__handle_xml_parser()
Posted by Stefan Sperling <st...@elego.de>.
On Fri, Jun 24, 2011 at 02:44:45PM +0200, Stefan Sperling wrote:
> Trying to update my working copy I observed svn spinning on the CPU
> forever. Attaching with gdb showed that it was stuck in an endless
> loop inside svn_ra_serf__handle_xml_parser().
Forgot to mention that I have on reliable way to reproduce this problem,
unfortunately :(
Re: serf infinite loop in svn_ra_serf__handle_xml_parser()
Posted by Stefan Sperling <st...@elego.de>.
On Fri, Jun 24, 2011 at 01:46:42PM -0400, Greg Stein wrote:
> When you say "success", you mean status==0?
Yup.
> No, it would never block. The bucket should return EAGAIN when it has no
> more data.
I see. Lieven explained this too and also pointed to a related
(or the same) issue documented elsewhere.
Re: serf infinite loop in svn_ra_serf__handle_xml_parser()
Posted by Greg Stein <gs...@gmail.com>.
On Jun 24, 2011 8:45 AM, "Stefan Sperling" <st...@elego.de> wrote:
>
> Trying to update my working copy I observed svn spinning on the CPU
> forever. Attaching with gdb showed that it was stuck in an endless
> loop inside svn_ra_serf__handle_xml_parser().
>
> Below is the code. The problem was that serf_bucket_read() returned
> zero bytes but also indicated success. If that happens the loop never
> terminates.
When you say "success", you mean status==0?
>
> I suppose this code was written with the assumption that reading
> from the socket would always block until data is available?
> This does not seem to happen in my case. Maybe serf is doing something
> wrong when it opens the socket?
No, it would never block. The bucket should return EAGAIN when it has no
more data.
>...
I'll investigate...
Cheers,
-g