user/dev discussion of public-inbox itself
 help / color / mirror / code / Atom feed
From: Eric Wong <e@80x24.org>
To: meta@public-inbox.org
Subject: [PATCH 2/3] http: fix various race conditions
Date: Tue, 24 May 2016 04:22:22 +0000	[thread overview]
Message-ID: <20160524042223.27700-3-e@80x24.org> (raw)
In-Reply-To: <20160524042223.27700-1-e@80x24.org>

We no longer override Danga::Socket::event_write and instead
re-enable reads by queuing up another callback in the $close
response callback.  This is necessary because event_write may not be
completely done writing a response, only the existing buffered data.

Furthermore, the {closed} field can almost be set at any time when
writing, so we must check it before acting on pipelined requests as
well as during write callbacks in more().
---
 lib/PublicInbox/HTTP.pm | 60 ++++++++++++++++++++++++-------------------------
 1 file changed, 30 insertions(+), 30 deletions(-)

diff --git a/lib/PublicInbox/HTTP.pm b/lib/PublicInbox/HTTP.pm
index 00c9a04..6aae5c8 100644
--- a/lib/PublicInbox/HTTP.pm
+++ b/lib/PublicInbox/HTTP.pm
@@ -39,7 +39,10 @@ sub process_pipelineq () {
 	my $q = $pipelineq;
 	$pipet = undef;
 	$pipelineq = [];
-	rbuf_process($_) foreach @$q;
+	foreach (@$q) {
+		next if $_->{closed};
+		rbuf_process($_);
+	}
 }
 
 # Use the same configuration parameter as git since this is primarily
@@ -228,26 +231,36 @@ sub identity_wcb ($) {
 	sub { $self->write(\($_[0])) if $_[0] ne '' }
 }
 
+sub next_request ($) {
+	my ($self) = @_;
+	$self->watch_write(0);
+	if ($self->{rbuf} eq '') { # wait for next request
+		$self->watch_read(1);
+	} else { # avoid recursion for pipelined requests
+		push @$pipelineq, $self;
+		$pipet ||= PublicInbox::EvCleanup::asap(*process_pipelineq);
+	}
+}
+
+sub response_done ($$) {
+	my ($self, $alive) = @_;
+	my $env = $self->{env};
+	$self->{env} = undef;
+	$self->write("0\r\n\r\n") if $alive == 2;
+	$self->write(sub { $alive ? next_request($self) : $self->close });
+	if (my $obj = $env->{'pi-httpd.inbox'}) {
+		# grace period for reaping resources
+		$WEAKEN->{"$obj"} = $obj;
+		PublicInbox::EvCleanup::later(*weaken_task);
+	}
+}
+
 sub response_write {
 	my ($self, $env, $res) = @_;
 	my $alive = response_header_write($self, $env, $res);
 
 	my $write = $alive == 2 ? chunked_wcb($self) : identity_wcb($self);
-	my $close = sub {
-		$self->write("0\r\n\r\n") if $alive == 2;
-		if ($alive) {
-			$self->event_write; # watch for readability if done
-		} else {
-			Danga::Socket::write($self, sub { $self->close });
-		}
-		if (my $obj = $env->{'pi-httpd.inbox'}) {
-			# grace period for reaping resources
-			$WEAKEN->{"$obj"} = $obj;
-			$weakt ||= PublicInbox::EvCleanup::later(*weaken_task);
-		}
-		$self->{env} = undef;
-	};
-
+	my $close = sub { response_done($self, $alive) };
 	if (defined(my $body = $res->[2])) {
 		if (ref $body eq 'ARRAY') {
 			$write->($_) foreach @$body;
@@ -278,6 +291,7 @@ sub response_write {
 use constant MSG_MORE => ($^O eq 'linux') ? 0x8000 : 0;
 sub more ($$) {
 	my $self = $_[0];
+	return if $self->{closed};
 	if (MSG_MORE && !$self->{write_buf_size}) {
 		my $n = send($self->{sock}, $_[1], MSG_MORE);
 		if (defined $n) {
@@ -290,20 +304,6 @@ sub more ($$) {
 	$self->write($_[1]);
 }
 
-# overrides existing Danga::Socket method
-sub event_write {
-	my ($self) = @_;
-	# only continue watching for readability when we are done writing:
-	return if $self->write(undef) != 1;
-
-	if ($self->{rbuf} eq '') { # wait for next request
-		$self->watch_read(1);
-	} else { # avoid recursion for pipelined requests
-		push @$pipelineq, $self;
-		$pipet ||= PublicInbox::EvCleanup::asap(*process_pipelineq);
-	}
-}
-
 sub input_prepare {
 	my ($self, $env) = @_;
 	my $input = $null_io;

  parent reply	other threads:[~2016-05-24  4:22 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-05-24  4:22 [PATCH 0/3] more networking/daemon fixes Eric Wong
2016-05-24  4:22 ` [PATCH 1/3] standardize timer-related event-loop code Eric Wong
2016-05-24  4:22 ` Eric Wong [this message]
2016-05-24  4:22 ` [PATCH 3/3] git-http-backend: use qspawn to limit running processes Eric Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://public-inbox.org/README

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160524042223.27700-3-e@80x24.org \
    --to=e@80x24.org \
    --cc=meta@public-inbox.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/public-inbox.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).