D(SYS_sendmsg);
D(SYS_recvmsg);
+ D(SYS_writev);
STRUCT_BEGIN(struct flock);
PR_NUM(l_start);
use List::Util qw(sum);
our @EXPORT_OK = qw(now msg_more awaitpid add_timer add_uniq_timer);
my $sendmsg_more = PublicInbox::Syscall->can('sendmsg_more');
+my $writev = PublicInbox::Syscall->can('writev');
my $nextq; # queue for next_tick
my $reap_armed;
}
}
+sub _iov_write ($$@) {
+ my ($self, $cb) = (shift, shift);
+ my ($tip, $tmpio, $s, $exp);
+ $s = $cb->($self->{sock}, @_);
+ if (defined $s) {
+ $exp = sum(map length, @_);
+ return 1 if $s == $exp;
+ while (@_) {
+ $tip = shift;
+ if ($s >= length($tip)) { # fully written
+ $s -= length($tip);
+ } else { # first partial write
+ $tmpio = tmpio $self, \$tip, $s, @_ or return 0;
+ last;
+ }
+ }
+ $tmpio // return drop $self, "BUG: tmpio on $s != $exp";
+ } elsif ($! == EAGAIN) {
+ $tip = shift;
+ $tmpio = tmpio $self, \$tip, 0, @_ or return 0;
+ } else { # client disconnected
+ return $self->close;
+ }
+ push @{$self->{wbuf}}, $tmpio; # autovivifies
+ epwait $self->{sock}, EPOLLOUT|EPOLLONESHOT;
+ 0;
+}
+
sub msg_more ($@) {
my $self = shift;
- my ($sock, $wbuf);
- $sock = $self->{sock} or return 1;
- $wbuf = $self->{wbuf};
-
+ my $sock = $self->{sock} or return 1;
+ my $wbuf = $self->{wbuf};
if ($sendmsg_more && (!defined($wbuf) || !scalar(@$wbuf)) &&
- !$sock->can('stop_SSL')) {
- my ($s, $tip, $tmpio);
- $s = $sendmsg_more->($sock, @_);
- if (defined $s) {
- my $exp = sum(map length, @_);
- return 1 if $s == $exp;
- while (@_) {
- $tip = shift;
- if ($s >= length($tip)) { # fully written
- $s -= length($tip);
- } else { # first partial write
- $tmpio = tmpio $self, \$tip, $s, @_
- or return 0;
- last;
- }
- }
- $tmpio // return drop $self, "BUG: tmpio on $s != $exp";
- } elsif ($! == EAGAIN) {
- $tip = shift;
- $tmpio = tmpio $self, \$tip, 0, @_ or return 0;
- } else { # client disconnected
- return $self->close;
- }
- push @{$self->{wbuf}}, $tmpio; # autovivifies
- epwait $sock, EPOLLOUT|EPOLLONESHOT;
- 0;
- } else {
- # don't redispatch into NNTPdeflate::write
+ !$sock->can('stop_SSL')) {
+ _iov_write $self, $sendmsg_more, @_;
+ } else { # don't redispatch into NNTPdeflate::write
+ PublicInbox::DS::write($self, join('', @_));
+ }
+}
+
+sub writev ($@) {
+ my $self = shift;
+ my $sock = $self->{sock} or return 1;
+ my $wbuf = $self->{wbuf};
+ if ($writev && (!defined($wbuf) || !scalar(@$wbuf)) &&
+ !$sock->can('stop_SSL')) {
+ _iov_write $self, $writev, @_;
+ } else { # don't redispatch into NNTPdeflate::write
PublicInbox::DS::write($self, join('', @_));
}
}
my $conn = $env->{HTTP_CONNECTION} || '';
my $term = defined($len) || $chunked;
my $prot_persist = ($proto eq 'HTTP/1.1') && ($conn !~ /\bclose\b/i);
- my $alive;
+ my ($alive, $res_body);
if (!$term && ref($res->[2]) eq 'ARRAY') {
- $len = sum0(map length, @{$res->[2]});
+ ($res_body, $res->[2]) = ($res->[2], []);
+ $len = sum0(map length, @$res_body);
$h .= "Content-Length: $len\r\n";
$term = 1;
}
}
$h .= 'Date: ' . http_date() . "\r\n\r\n";
- if (($len || $chunked) && $env->{REQUEST_METHOD} ne 'HEAD') {
+ if ($res_body) {
+ $self->writev($h, @$res_body);
+ } elsif (($len || $chunked) && $env->{REQUEST_METHOD} ne 'HEAD') {
msg_more($self, $h);
} else {
$self->write(\$h);
$SYS_recvmsg);
my $SYS_fstatfs; # don't need fstatfs64, just statfs.f_type
-my ($FS_IOC_GETFLAGS, $FS_IOC_SETFLAGS);
+my ($FS_IOC_GETFLAGS, $FS_IOC_SETFLAGS, $SYS_writev);
my $SFD_CLOEXEC = 02000000; # Perl does not expose O_CLOEXEC
our $no_deprecated = 0;
$SYS_fstatfs = 100;
$SYS_sendmsg = 370;
$SYS_recvmsg = 372;
+ $SYS_writev = 146;
$INOTIFY = { # usage: `use constant $PublicInbox::Syscall::INOTIFY'
SYS_inotify_init1 => 332,
SYS_inotify_add_watch => 292,
$SYS_fstatfs = 138;
$SYS_sendmsg = 46;
$SYS_recvmsg = 47;
+ $SYS_writev = 20;
$INOTIFY = {
SYS_inotify_init1 => 294,
SYS_inotify_add_watch => 254,
$SYS_fstatfs = 138;
$SYS_sendmsg = 0x40000206;
$SYS_recvmsg = 0x40000207;
+ $SYS_writev = 0x40000204;
$FS_IOC_GETFLAGS = 0x80046601;
$FS_IOC_SETFLAGS = 0x40046602;
$INOTIFY = {
$SYS_fstatfs = 100;
$SYS_sendmsg = 341;
$SYS_recvmsg = 342;
+ $SYS_writev = 146;
$FS_IOC_GETFLAGS = 0x40086601;
$FS_IOC_SETFLAGS = 0x80086602;
$INOTIFY = {
$SYS_signalfd4 = 313;
$SYS_renameat2 //= 357;
$SYS_fstatfs = 100;
+ $SYS_writev = 146;
$FS_IOC_GETFLAGS = 0x40086601;
$FS_IOC_SETFLAGS = 0x80086602;
} elsif ($machine =~ m/^s390/) { # untested, no machine on cfarm
$SYS_fstatfs = 100;
$SYS_sendmsg = 370;
$SYS_recvmsg = 372;
+ $SYS_writev = 146;
} elsif ($machine eq 'ia64') { # untested, no machine on cfarm
$SYS_epoll_create = 1243;
$SYS_epoll_ctl = 1244;
$SYS_fstatfs = 44;
$SYS_sendmsg = 211;
$SYS_recvmsg = 212;
+ $SYS_writev = 66;
$INOTIFY = {
SYS_inotify_init1 => 26,
SYS_inotify_add_watch => 27,
$SYS_fstatfs = 100;
$SYS_sendmsg = 296;
$SYS_recvmsg = 297;
+ $SYS_writev = 146;
} elsif ($machine =~ m/^mips64/) { # cfarm only has 32-bit userspace
$SYS_epoll_create = 5207;
$SYS_epoll_ctl = 5208;
$SYS_fstatfs = 5135;
$SYS_sendmsg = 5045;
$SYS_recvmsg = 5046;
+ $SYS_writev = 5019;
$FS_IOC_GETFLAGS = 0x40046601;
$FS_IOC_SETFLAGS = 0x80046602;
} elsif ($machine =~ m/^mips/) { # 32-bit, tested on mips64 cfarm host
$SYS_fstatfs = 4100;
$SYS_sendmsg = 4179;
$SYS_recvmsg = 4177;
+ $SYS_writev = 4146;
$FS_IOC_GETFLAGS = 0x40046601;
$FS_IOC_SETFLAGS = 0x80046602;
$SIGNUM{WINCH} = 20;
# (I'm assuming Dragonfly copies FreeBSD, here, too)
$SYS_recvmsg = 27;
$SYS_sendmsg = 28;
+ $SYS_writev = 121;
}
BEGIN {
use constant msg_controllen_max =>
CMSG_SPACE(10 * SIZEOF_int) + SIZEOF_cmsghdr; # space for 10 FDs
-if (defined($SYS_sendmsg) && defined($SYS_recvmsg)) {
no warnings 'once';
+
+if (defined($SYS_sendmsg) && defined($SYS_recvmsg)) {
require PublicInbox::CmdIPC4;
*send_cmd4 = sub ($$$$;$) {
};
}
+if (defined($SYS_writev)) {
+*writev = sub {
+ my $fh = shift;
+ use bytes qw(length substr);
+ my $iov = join('', map { pack 'P'.TMPL_size_t, $_, length } @_);
+ my $w;
+ do {
+ $w = syscall($SYS_writev, fileno($fh), $iov, scalar(@_));
+ } while ($w < 0 && $!{EINTR});
+ $w < 0 ? undef : $w;
+};
+}
+
1;
=head1 WARRANTY
use PublicInbox::Syscall;
use Socket qw(AF_UNIX SOCK_STREAM);
my $sendmsg_more = PublicInbox::Syscall->can('sendmsg_more') or
- plan skip_all => "sendmsg syscalls not defined on $^O";
+ plan skip_all => "sendmsg syscall not defined on $^O";
+my $writev = PublicInbox::Syscall->can('writev') or
+ plan skip_all => "writev syscall not defined on $^O";
socketpair(my $s1, my $s2, AF_UNIX, SOCK_STREAM, 0);
is $sendmsg_more->($s1, 'hello', 'world'), 10, 'sendmsg_more expected size';
is sysread($s2, my $buf, 11), 10, 'reader got expected size from sendmsg_more';
is $buf, 'helloworld', 'sendmsg_more sent expected message';
+is $writev->($s1, 'hello', 'world'), 10, 'writev expected size';
+is sysread($s2, $buf, 11), 10, 'reader got expected size from writev';
+is $buf, 'helloworld', 'writev sent expected message';
+
done_testing;