Since the removal of pseudo-hash support in Perl 5.10, the
"fields" module no longer provides the space or speed benefits
it did in 5.8. It also does not allow for compile-time checks,
only run-time checks.
To me, the extra developer overhead in maintaining "use fields"
args has become a hassle. None of our non-DS-related code uses
fields.pm, nor do any of our current dependencies. In fact,
Danga::Socket (which DS was originally forked from) and its
subclasses are the only fields.pm users I've ever encountered in
the wild. Removing fields may make our code more approachable
to other Perl hackers.
So stop using fields.pm and locked hashes, but continue to
document what fields do for non-trivial classes.
13 files changed:
# Bugs encountered were reported to bug-Danga-Socket@rt.cpan.org,
# fixed in Danga::Socket 1.62 and visible at:
# https://rt.cpan.org/Public/Dist/Display.html?Name=Danga-Socket
# Bugs encountered were reported to bug-Danga-Socket@rt.cpan.org,
# fixed in Danga::Socket 1.62 and visible at:
# https://rt.cpan.org/Public/Dist/Display.html?Name=Danga-Socket
+#
+# fields:
+# sock: underlying socket
+# rbuf: scalarref, usually undef
+# wbuf: arrayref of coderefs or tmpio (autovivified))
+# (tmpio = [ GLOB, offset, [ length ] ])
package PublicInbox::DS;
use strict;
use bytes;
package PublicInbox::DS;
use strict;
use bytes;
use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
use parent qw(Exporter);
our @EXPORT_OK = qw(now msg_more);
use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
use parent qw(Exporter);
our @EXPORT_OK = qw(now msg_more);
use 5.010_001;
use Scalar::Util qw(blessed);
use 5.010_001;
use Scalar::Util qw(blessed);
use PublicInbox::Syscall qw(:epoll);
use PublicInbox::Tmpfile;
use PublicInbox::Syscall qw(:epoll);
use PublicInbox::Tmpfile;
-
-use fields ('sock', # underlying socket
- 'rbuf', # scalarref, usually undef
- 'wbuf', # arrayref of coderefs or tmpio (autovivified))
- # (tmpio = [ GLOB, offset, [ length ] ])
- );
-
use Errno qw(EAGAIN EINVAL);
use Carp qw(confess carp);
use Errno qw(EAGAIN EINVAL);
use Carp qw(confess carp);
=cut
sub new {
my ($self, $sock, $ev) = @_;
=cut
sub new {
my ($self, $sock, $ev) = @_;
- $self = fields::new($self) unless ref $self;
-
$self->{sock} = $sock;
my $fd = fileno($sock);
$self->{sock} = $sock;
my $fd = fileno($sock);
# Used by public-inbox-watch for Maildir (and possibly MH in the future)
package PublicInbox::DirIdle;
use strict;
# Used by public-inbox-watch for Maildir (and possibly MH in the future)
package PublicInbox::DirIdle;
use strict;
-use base 'PublicInbox::DS';
-use fields qw(inot);
+use parent 'PublicInbox::DS';
use PublicInbox::Syscall qw(EPOLLIN EPOLLET);
use PublicInbox::In2Tie;
use PublicInbox::Syscall qw(EPOLLIN EPOLLET);
use PublicInbox::In2Tie;
sub new {
my ($class, $dirs, $cb) = @_;
sub new {
my ($class, $dirs, $cb) = @_;
- my $self = fields::new($class);
+ my $self = bless {}, $class;
my $inot;
if ($ino_cls) {
$inot = $ino_cls->new or die "E: $ino_cls->new: $!";
my $inot;
if ($ino_cls) {
$inot = $ino_cls->new or die "E: $ino_cls->new: $!";
package PublicInbox::GitAsyncCat;
use strict;
use parent qw(PublicInbox::DS Exporter);
package PublicInbox::GitAsyncCat;
use strict;
use parent qw(PublicInbox::DS Exporter);
use PublicInbox::Syscall qw(EPOLLIN EPOLLET);
our @EXPORT = qw(git_async_cat);
sub _add {
my ($class, $git) = @_;
use PublicInbox::Syscall qw(EPOLLIN EPOLLET);
our @EXPORT = qw(git_async_cat);
sub _add {
my ($class, $git) = @_;
- my $self = fields::new($class);
+ my $self = bless { git => $git }, $class;
$self->SUPER::new($git->{in}, EPOLLIN|EPOLLET);
$self->SUPER::new($git->{in}, EPOLLIN|EPOLLET);
\undef; # this is a true ref()
}
\undef; # this is a true ref()
}
# to learn different ways to admin both NNTP and HTTP components.
# There's nothing which depends on public-inbox, here.
# Each instance of this class represents a HTTP client socket
# to learn different ways to admin both NNTP and HTTP components.
# There's nothing which depends on public-inbox, here.
# Each instance of this class represents a HTTP client socket
+#
+# fields:
+# httpd: PublicInbox::HTTPD ref
+# env: PSGI env hashref
+# input_left: bytes left to read in request body (e.g. POST/PUT)
+# remote_addr: remote IP address as a string (e.g. "127.0.0.1")
+# remote_port: peer port
+# forward: response body object, response to ->getline + ->close
+# alive: HTTP keepalive state:
+# 0: drop connection when done
+# 1: keep connection when done
+# 2: keep connection, chunk responses
package PublicInbox::HTTP;
use strict;
package PublicInbox::HTTP;
use strict;
-use warnings;
-use base qw(PublicInbox::DS);
-use fields qw(httpd env input_left remote_addr remote_port forward alive);
+use parent qw(PublicInbox::DS);
use bytes (); # only for bytes::length
use Fcntl qw(:seek);
use Plack::HTTPParser qw(parse_http_request); # XS or pure Perl
use bytes (); # only for bytes::length
use Fcntl qw(:seek);
use Plack::HTTPParser qw(parse_http_request); # XS or pure Perl
sub new ($$$) {
my ($class, $sock, $addr, $httpd) = @_;
sub new ($$$) {
my ($class, $sock, $addr, $httpd) = @_;
- my $self = fields::new($class);
+ my $self = bless { httpd => $httpd }, $class;
my $ev = EPOLLIN;
my $wbuf;
if ($sock->can('accept_SSL') && !$sock->accept_SSL) {
my $ev = EPOLLIN;
my $wbuf;
if ($sock->can('accept_SSL') && !$sock->accept_SSL) {
$ev = PublicInbox::TLS::epollbit();
$wbuf = [ \&PublicInbox::DS::accept_tls_step ];
}
$ev = PublicInbox::TLS::epollbit();
$wbuf = [ \&PublicInbox::DS::accept_tls_step ];
}
- $self->SUPER::new($sock, $ev | EPOLLONESHOT);
- $self->{httpd} = $httpd;
$self->{wbuf} = $wbuf if $wbuf;
($self->{remote_addr}, $self->{remote_port}) =
PublicInbox::Daemon::host_with_port($addr);
$self->{wbuf} = $wbuf if $wbuf;
($self->{remote_addr}, $self->{remote_port}) =
PublicInbox::Daemon::host_with_port($addr);
+ $self->SUPER::new($sock, $ev | EPOLLONESHOT);
}
sub event_step { # called by PublicInbox::DS
}
sub event_step { # called by PublicInbox::DS
# The name of this key is not even stable!
# Currently intended for use with read-only pipes with expensive
# processes such as git-http-backend(1), cgit(1)
# The name of this key is not even stable!
# Currently intended for use with read-only pipes with expensive
# processes such as git-http-backend(1), cgit(1)
+#
+# fields:
+# http: PublicInbox::HTTP ref
+# fh: PublicInbox::HTTP::{Identity,Chunked} ref (can ->write + ->close)
+# cb: initial read callback
+# arg: arg for {cb}
+# end_obj: CODE or object which responds to ->event_step when ->close is called
package PublicInbox::HTTPD::Async;
use strict;
package PublicInbox::HTTPD::Async;
use strict;
-use warnings;
-use base qw(PublicInbox::DS);
-use fields qw(http fh cb arg end_obj);
+use parent qw(PublicInbox::DS);
use Errno qw(EAGAIN);
use PublicInbox::Syscall qw(EPOLLIN EPOLLET);
use Errno qw(EAGAIN);
use PublicInbox::Syscall qw(EPOLLIN EPOLLET);
die '$end_obj unsupported w/o $io' if $end_obj;
return;
}
die '$end_obj unsupported w/o $io' if $end_obj;
return;
}
-
- my $self = fields::new($class);
+ my $self = bless {
+ cb => $cb, # initial read callback
+ arg => $arg, # arg for $cb
+ end_obj => $end_obj, # like END{}, can ->event_step
+ }, $class;
IO::Handle::blocking($io, 0);
$self->SUPER::new($io, EPOLLIN | EPOLLET);
IO::Handle::blocking($io, 0);
$self->SUPER::new($io, EPOLLIN | EPOLLET);
- $self->{cb} = $cb; # initial read callback
- $self->{arg} = $arg; # arg for $cb
- $self->{end_obj} = $end_obj; # like END{}, can ->event_step
- $self;
# as a 50K uint16_t array (via pack("S*", ...)). "UID offset"
# is the offset from {uid_base} which determines the start of
# the mailbox slice.
# as a 50K uint16_t array (via pack("S*", ...)). "UID offset"
# is the offset from {uid_base} which determines the start of
# the mailbox slice.
+#
+# fields:
+# imapd: PublicInbox::IMAPD ref
+# ibx: PublicInbox::Inbox ref
+# long_cb: long_response private data
+# uid_base: base UID for mailbox slice (0-based)
+# -login_tag: IMAP TAG for LOGIN
+# -idle_tag: IMAP response tag for IDLE
+# uo2m: UID-to-MSN mapping
package PublicInbox::IMAP;
use strict;
package PublicInbox::IMAP;
use strict;
-use base qw(PublicInbox::DS);
-use fields qw(imapd ibx long_cb -login_tag
- uid_base -idle_tag uo2m);
+use parent qw(PublicInbox::DS);
use PublicInbox::Eml;
use PublicInbox::EmlContentFoo qw(parse_content_disposition);
use PublicInbox::DS qw(now);
use PublicInbox::Eml;
use PublicInbox::EmlContentFoo qw(parse_content_disposition);
use PublicInbox::DS qw(now);
use PublicInbox::GitAsyncCat;
use Text::ParseWords qw(parse_line);
use Errno qw(EAGAIN);
use PublicInbox::GitAsyncCat;
use Text::ParseWords qw(parse_line);
use Errno qw(EAGAIN);
-use Hash::Util qw(unlock_hash); # dependency of fields for perl 5.10+, anyways
use PublicInbox::Search;
use PublicInbox::IMAPsearchqp;
*mdocid = \&PublicInbox::Search::mdocid;
use PublicInbox::Search;
use PublicInbox::IMAPsearchqp;
*mdocid = \&PublicInbox::Search::mdocid;
sub new ($$$) {
my ($class, $sock, $imapd) = @_;
sub new ($$$) {
my ($class, $sock, $imapd) = @_;
- my $self = fields::new('PublicInbox::IMAP_preauth');
- unlock_hash(%$self);
+ my $self = bless { imapd => $imapd }, 'PublicInbox::IMAP_preauth';
my $ev = EPOLLIN;
my $wbuf;
if ($sock->can('accept_SSL') && !$sock->accept_SSL) {
my $ev = EPOLLIN;
my $wbuf;
if ($sock->can('accept_SSL') && !$sock->accept_SSL) {
$wbuf = [ \&PublicInbox::DS::accept_tls_step, \&greet ];
}
$self->SUPER::new($sock, $ev | EPOLLONESHOT);
$wbuf = [ \&PublicInbox::DS::accept_tls_step, \&greet ];
}
$self->SUPER::new($sock, $ev | EPOLLONESHOT);
- $self->{imapd} = $imapd;
if ($wbuf) {
$self->{wbuf} = $wbuf;
} else {
if ($wbuf) {
$self->{wbuf} = $wbuf;
} else {
# Copyright (C) 2020 all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
# Copyright (C) 2020 all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+# fields:
+# pi_config: PublicInbox::Config ref
+# inot: Linux::Inotify2-like object
+# pathmap => { inboxdir => [ ibx, watch1, watch2, watch3... ] } mapping
package PublicInbox::InboxIdle;
use strict;
package PublicInbox::InboxIdle;
use strict;
-use base qw(PublicInbox::DS);
-use fields qw(pi_config inot pathmap);
+use parent qw(PublicInbox::DS);
use Cwd qw(abs_path);
use PublicInbox::Syscall qw(EPOLLIN EPOLLET);
my $IN_MODIFY = 0x02; # match Linux inotify
use Cwd qw(abs_path);
use PublicInbox::Syscall qw(EPOLLIN EPOLLET);
my $IN_MODIFY = 0x02; # match Linux inotify
sub new {
my ($class, $pi_config) = @_;
sub new {
my ($class, $pi_config) = @_;
- my $self = fields::new($class);
+ my $self = bless {}, $class;
my $inot;
if ($ino_cls) {
$inot = $ino_cls->new or die "E: $ino_cls->new: $!";
my $inot;
if ($ino_cls) {
$inot = $ino_cls->new or die "E: $ino_cls->new: $!";
# Used by -nntpd for listen sockets
package PublicInbox::Listener;
use strict;
# Used by -nntpd for listen sockets
package PublicInbox::Listener;
use strict;
-use warnings;
-use base 'PublicInbox::DS';
+use parent 'PublicInbox::DS';
use Socket qw(SOL_SOCKET SO_KEEPALIVE IPPROTO_TCP TCP_NODELAY);
use Socket qw(SOL_SOCKET SO_KEEPALIVE IPPROTO_TCP TCP_NODELAY);
-use fields qw(post_accept);
use IO::Handle;
use PublicInbox::Syscall qw(EPOLLIN EPOLLEXCLUSIVE EPOLLET);
use Errno qw(EAGAIN ECONNABORTED EPERM);
use IO::Handle;
use PublicInbox::Syscall qw(EPOLLIN EPOLLEXCLUSIVE EPOLLET);
use Errno qw(EAGAIN ECONNABORTED EPERM);
setsockopt($s, SOL_SOCKET, SO_KEEPALIVE, 1);
setsockopt($s, IPPROTO_TCP, TCP_NODELAY, 1); # ignore errors on non-TCP
listen($s, 1024);
setsockopt($s, SOL_SOCKET, SO_KEEPALIVE, 1);
setsockopt($s, IPPROTO_TCP, TCP_NODELAY, 1); # ignore errors on non-TCP
listen($s, 1024);
- my $self = fields::new($class);
+ my $self = bless { post_accept => $cb }, $class;
$self->SUPER::new($s, EPOLLIN|EPOLLET|EPOLLEXCLUSIVE);
$self->SUPER::new($s, EPOLLIN|EPOLLET|EPOLLEXCLUSIVE);
- $self->{post_accept} = $cb;
- $self
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
#
# Each instance of this represents a NNTP client socket
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
#
# Each instance of this represents a NNTP client socket
+# fields:
+# nntpd: PublicInbox::NNTPD ref
+# article: per-session current article number
+# ng: PublicInbox::Inbox ref
+# long_cb: long_response private data
package PublicInbox::NNTP;
use strict;
package PublicInbox::NNTP;
use strict;
-use warnings;
-use base qw(PublicInbox::DS);
-use fields qw(nntpd article ng long_cb);
+use parent qw(PublicInbox::DS);
use PublicInbox::MID qw(mid_escape $MID_EXTRACT);
use PublicInbox::Eml;
use POSIX qw(strftime);
use PublicInbox::MID qw(mid_escape $MID_EXTRACT);
use PublicInbox::Eml;
use POSIX qw(strftime);
sub new ($$$) {
my ($class, $sock, $nntpd) = @_;
sub new ($$$) {
my ($class, $sock, $nntpd) = @_;
- my $self = fields::new($class);
+ my $self = bless { nntpd => $nntpd }, $class;
my $ev = EPOLLIN;
my $wbuf;
if ($sock->can('accept_SSL') && !$sock->accept_SSL) {
my $ev = EPOLLIN;
my $wbuf;
if ($sock->can('accept_SSL') && !$sock->accept_SSL) {
$wbuf = [ \&PublicInbox::DS::accept_tls_step, \&greet ];
}
$self->SUPER::new($sock, $ev | EPOLLONESHOT);
$wbuf = [ \&PublicInbox::DS::accept_tls_step, \&greet ];
}
$self->SUPER::new($sock, $ev | EPOLLONESHOT);
- $self->{nntpd} = $nntpd;
if ($wbuf) {
$self->{wbuf} = $wbuf;
} else {
if ($wbuf) {
$self->{wbuf} = $wbuf;
} else {
# efficient in terms of server memory usage.
package PublicInbox::NNTPdeflate;
use strict;
# efficient in terms of server memory usage.
package PublicInbox::NNTPdeflate;
use strict;
-use base qw(PublicInbox::NNTP);
+use parent qw(PublicInbox::NNTP);
-use Hash::Util qw(unlock_hash); # dependency of fields for perl 5.10+, anyways
my %IN_OPT = (
-Bufsize => PublicInbox::NNTP::LINE_MAX,
my %IN_OPT = (
-Bufsize => PublicInbox::NNTP::LINE_MAX,
$self->res('403 Unable to activate compression');
return;
}
$self->res('403 Unable to activate compression');
return;
}
$self->res('206 Compression active');
bless $self, $class;
$self->{zin} = $in;
$self->res('206 Compression active');
bless $self, $class;
$self->{zin} = $in;
# notified if the master process dies.
package PublicInbox::ParentPipe;
use strict;
# notified if the master process dies.
package PublicInbox::ParentPipe;
use strict;
-use warnings;
-use base qw(PublicInbox::DS);
-use fields qw(cb);
+use parent qw(PublicInbox::DS);
use PublicInbox::Syscall qw(EPOLLIN EPOLLONESHOT);
sub new ($$$) {
my ($class, $pipe, $worker_quit) = @_;
use PublicInbox::Syscall qw(EPOLLIN EPOLLONESHOT);
sub new ($$$) {
my ($class, $pipe, $worker_quit) = @_;
- my $self = fields::new($class);
+ my $self = bless { cb => $worker_quit }, $class;
$self->SUPER::new($pipe, EPOLLIN|EPOLLONESHOT);
$self->SUPER::new($pipe, EPOLLIN|EPOLLONESHOT);
- $self->{cb} = $worker_quit;
- $self;
}
# master process died, time to call worker_quit ourselves
}
# master process died, time to call worker_quit ourselves
# Copyright (C) 2019-2020 all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
# Copyright (C) 2019-2020 all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+
+# Wraps a signalfd (or similar) for PublicInbox::DS
+# fields: (sig: hashref similar to %SIG, but signal numbers as keys)
package PublicInbox::Sigfd;
use strict;
use parent qw(PublicInbox::DS);
package PublicInbox::Sigfd;
use strict;
use parent qw(PublicInbox::DS);
-use fields qw(sig); # hashref similar to %SIG, but signal numbers as keys
use PublicInbox::Syscall qw(signalfd EPOLLIN EPOLLET SFD_NONBLOCK);
use POSIX qw(:signal_h);
use IO::Handle ();
use PublicInbox::Syscall qw(signalfd EPOLLIN EPOLLET SFD_NONBLOCK);
use POSIX qw(:signal_h);
use IO::Handle ();
# are available.
sub new {
my ($class, $sig, $flags) = @_;
# are available.
sub new {
my ($class, $sig, $flags) = @_;
- my $self = fields::new($class);
my %signo = map {;
my $cb = $sig->{$_};
# SIGWINCH is 28 on FreeBSD, NetBSD, OpenBSD
my %signo = map {;
my $cb = $sig->{$_};
# SIGWINCH is 28 on FreeBSD, NetBSD, OpenBSD
};
$num => $cb;
} keys %$sig;
};
$num => $cb;
} keys %$sig;
+ my $self = bless { sig => \%signo }, $class;
my $io;
my $fd = signalfd(-1, [keys %signo], $flags);
if (defined $fd && $fd >= 0) {
my $io;
my $fd = signalfd(-1, [keys %signo], $flags);
if (defined $fd && $fd >= 0) {
$self->SUPER::new($io, EPOLLIN | EPOLLET);
} else { # master main loop
$self->{sock} = $io;
$self->SUPER::new($io, EPOLLIN | EPOLLET);
} else { # master main loop
$self->{sock} = $io;
- $self->{sig} = \%signo;
- $self;
}
# PublicInbox::Daemon in master main loop (blocking)
}
# PublicInbox::Daemon in master main loop (blocking)
package IMAPC;
use strict;
package IMAPC;
use strict;
-use base qw(PublicInbox::DS);
-use fields qw(step zin);
+use parent qw(PublicInbox::DS);
+# fields: step: state machine, zin: Zlib inflate context
use PublicInbox::Syscall qw(EPOLLIN EPOLLOUT EPOLLONESHOT);
use Errno qw(EAGAIN);
# determines where we start event_step
use PublicInbox::Syscall qw(EPOLLIN EPOLLOUT EPOLLONESHOT);
use Errno qw(EAGAIN);
# determines where we start event_step
sub new {
my ($class, $io) = @_;
sub new {
my ($class, $io) = @_;
- my $self = fields::new($class);
-
- # wait for connect(), and maybe SSL_connect()
- $self->SUPER::new($io, EPOLLOUT|EPOLLONESHOT);
+ my $self = bless { step => FIRST_STEP }, $class;
if ($io->can('connect_SSL')) {
$self->{wbuf} = [ \&connect_tls_step ];
}
if ($io->can('connect_SSL')) {
$self->{wbuf} = [ \&connect_tls_step ];
}
- $self->{step} = FIRST_STEP;
- $self;
+ # wait for connect(), and maybe SSL_connect()
+ $self->SUPER::new($io, EPOLLOUT|EPOLLONESHOT);
}
1;
package IMAPCdeflate;
use strict;
}
1;
package IMAPCdeflate;
use strict;
-use base qw(IMAPC); # parent doesn't work for fields
-use Hash::Util qw(unlock_hash); # dependency of fields for perl 5.10+, anyways
use Compress::Raw::Zlib;
use PublicInbox::IMAPdeflate;
my %ZIN_OPT;
BEGIN {
use Compress::Raw::Zlib;
use PublicInbox::IMAPdeflate;
my %ZIN_OPT;
BEGIN {
%ZIN_OPT = ( -WindowBits => -15, -AppendOutput => 1 );
*write = \&PublicInbox::IMAPdeflate::write;
*do_read = \&PublicInbox::IMAPdeflate::do_read;
%ZIN_OPT = ( -WindowBits => -15, -AppendOutput => 1 );
*write = \&PublicInbox::IMAPdeflate::write;
*do_read = \&PublicInbox::IMAPdeflate::do_read;
my ($class, $self) = @_;
my ($in, $err) = Compress::Raw::Zlib::Inflate->new(%ZIN_OPT);
die "Inflate->new failed: $err" if $err != Z_OK;
my ($class, $self) = @_;
my ($in, $err) = Compress::Raw::Zlib::Inflate->new(%ZIN_OPT);
die "Inflate->new failed: $err" if $err != Z_OK;
bless $self, $class;
$self->{zin} = $in;
}
bless $self, $class;
$self->{zin} = $in;
}