From d78f50649a5545d66a61b5465ca7f5ce4be398ea Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 19 Sep 2020 09:37:14 +0000 Subject: gcf2: wire up read-only daemons and rm -gcf2 script It seems easiest to have a singleton Gcf2Client client object per daemon worker for all inboxes to use. This reduces overall FD usage from pipes. The `public-inbox-gcf2' command + manpage are gone and a `$^X' one-liner is used, instead. This saves inodes for internal commands and hopefully makes it easier to avoid mismatched PERL5LIB include paths (as noticed during development :x). We'll also make the existing cat-file process management infrastructure more resilient to BOFHs on process killing sprees (or in case our libgit2-based code fails on us). (Rare) PublicInbox::WWW PSGI users NOT using public-inbox-httpd won't automatically benefit from this change, and extra configuration will be required (to be documented later). --- lib/PublicInbox/Gcf2Client.pm | 59 +++++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 13 deletions(-) (limited to 'lib/PublicInbox/Gcf2Client.pm') diff --git a/lib/PublicInbox/Gcf2Client.pm b/lib/PublicInbox/Gcf2Client.pm index 30f85c71..42ff1bf3 100644 --- a/lib/PublicInbox/Gcf2Client.pm +++ b/lib/PublicInbox/Gcf2Client.pm @@ -1,29 +1,62 @@ # Copyright (C) 2020 all contributors # License: AGPL-3.0+ -# connects public-inbox processes to public-inbox-gcf2(1) +# connects public-inbox processes to PublicInbox::Gcf2::loop() package PublicInbox::Gcf2Client; use strict; -use parent 'PublicInbox::Git'; +use parent qw(PublicInbox::DS); +use PublicInbox::Git; use PublicInbox::Spawn qw(popen_rd); use IO::Handle (); +use PublicInbox::Syscall qw(EPOLLONESHOT EPOLLOUT); +# fields: +# async_cat => GitAsyncCat ref (read-only pipe) +# sock => writable pipe to Gcf2::loop -sub new { - my ($rdr) = @_; - my $self = bless {}, __PACKAGE__; +sub new { bless($_[0] // {}, __PACKAGE__) } + +sub gcf2c_begin ($) { + my ($self) = @_; + # ensure the child process has the same @INC we do: + my $env = { PERL5LIB => join(':', @INC) }; my ($out_r, $out_w); - pipe($out_r, $out_w) or $self->fail("pipe failed: $!"); - $rdr //= {}; - $rdr->{0} = $out_r; - @$self{qw(in pid)} = popen_rd(['public-inbox-gcf2'], undef, $rdr); - $self->{inflight} = []; - $self->{out} = $out_w; + pipe($out_r, $out_w) or die "pipe failed: $!"; + my $rdr = { 0 => $out_r, 2 => $self->{2} }; + my $cmd = [$^X, qw[-MPublicInbox::Gcf2 -e PublicInbox::Gcf2::loop()]]; + @$self{qw(in pid)} = popen_rd($cmd, $env, $rdr); fcntl($out_w, 1031, 4096) if $^O eq 'linux'; # 1031: F_SETPIPE_SZ $out_w->autoflush(1); - $self; + $out_w->blocking(0); + $self->SUPER::new($out_w, 0); # EPOLL_CTL_ADD (a bit wasteful :x) + $self->{inflight} = []; +} + +sub fail { + my $self = shift; + $self->close; # PublicInbox::DS::close + PublicInbox::Git::fail($self, @_); +} + +sub cat_async ($$$;$) { + my ($self, $req, $cb, $arg) = @_; + my $inflight = $self->{inflight} // gcf2c_begin($self); + + # rare, I hope: + cat_async_step($self, $inflight) if $self->{wbuf}; + + $self->write(\"$req\n") or $self->fail("gcf2c write: $!"); + push @$inflight, $req, $cb, $arg; } -# always false, since -gcf2 retries internally +# ensure PublicInbox::Git::cat_async_step never calls cat_async_retry sub alternates_changed {} +no warnings 'once'; + +# this is the write-only end of a pipe, DS->EventLoop will call this +*event_step = \&PublicInbox::DS::flush_write; + +# used by GitAsyncCat +*cat_async_step = \&PublicInbox::Git::cat_async_step; + 1; -- cgit v1.2.3-24-ge0c7