1 # Copyright (C) 2020 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
4 # connects public-inbox processes to PublicInbox::Gcf2::loop()
5 package PublicInbox::Gcf2Client;
7 use parent qw(PublicInbox::DS);
9 use PublicInbox::Spawn qw(popen_rd);
11 use PublicInbox::Syscall qw(EPOLLONESHOT);
13 # async_cat => GitAsyncCat ref (read-only pipe)
14 # sock => writable pipe to Gcf2::loop
15 # in => pipe we read from
16 # pid => PID of Gcf2::loop process
19 my $self = bless {}, __PACKAGE__;
20 # ensure the child process has the same @INC we do:
21 my $env = { PERL5LIB => join(':', @INC) };
23 pipe($out_r, $out_w) or die "pipe failed: $!";
26 my $cmd = [$^X, qw[-MPublicInbox::Gcf2 -e PublicInbox::Gcf2::loop()]];
27 @$self{qw(in pid)} = popen_rd($cmd, $env, $rdr);
28 fcntl($out_w, 1031, 4096) if $^O eq 'linux'; # 1031: F_SETPIPE_SZ
31 $self->{inflight} = [];
32 $self->SUPER::new($out_w, EPOLLONESHOT); # detect errors once
37 $self->close; # PublicInbox::DS::close
38 PublicInbox::Git::fail($self, @_);
41 sub cat_async ($$$;$) {
42 my ($self, $req, $cb, $arg) = @_;
43 my $inflight = $self->{inflight};
45 # {wbuf} is rare, I hope:
46 cat_async_step($self, $inflight) if $self->{wbuf};
48 if (!$self->write(\"$req\n")) {
49 $self->fail("gcf2c write: $!") if !$self->{sock};
51 push @$inflight, $req, $cb, $arg;
54 # ensure PublicInbox::Git::cat_async_step never calls cat_async_retry
55 sub alternates_changed {}
57 # this is the write-only end of a pipe, DS->EventLoop will call this
61 $self->close if !$self->{in}; # process died
68 my $pid = delete $self->{pid};
72 PublicInbox::DS::dwaitpid($pid, undef, undef);
73 $self->close; # we're still in the event loop
75 if ($@) { # wait synchronously if not in event loop
76 my $sock = delete $self->{sock};
83 *cat_async_step = \&PublicInbox::Git::cat_async_step;