Refactor SelectHandler to use PBot::IRC's select loop

This commit is contained in:
Pragmatic Software 2021-06-20 20:31:47 -07:00
parent 8b093d313c
commit 06d986e11e
2 changed files with 56 additions and 64 deletions

View File

@ -100,7 +100,7 @@ sub initialize {
my ($item, $value) = split /=/, $arg, 2; my ($item, $value) = split /=/, $arg, 2;
if (not defined $item or not defined $value) { if (not defined $item or not defined $value) {
print STDERR "Fatal error: unknown argument `$arg`; arguments must be in the form of `section.key=value` (e.g.: irc.botnick=newnick)\n"; print STDERR "Fatal error: unknown argument `$arg`; arguments must be in the form of `section.key=value` or `path_dir=value` (e.g.: irc.botnick=newnick or data_dir=path)\n";
exit; exit;
} }
@ -174,6 +174,9 @@ sub initialize {
exit; exit;
} }
# prepare the IRC engine
$self->{irc} = PBot::IRC->new;
# prepare remaining core PBot modules -- do not change this order # prepare remaining core PBot modules -- do not change this order
$self->{timer} = PBot::Timer->new(pbot => $self, timeout => 10, name => 'PBot Timer', %conf); $self->{timer} = PBot::Timer->new(pbot => $self, timeout => 10, name => 'PBot Timer', %conf);
$self->{event_dispatcher} = PBot::EventDispatcher->new(pbot => $self, %conf); $self->{event_dispatcher} = PBot::EventDispatcher->new(pbot => $self, %conf);
@ -188,7 +191,6 @@ sub initialize {
$self->{functions} = PBot::Functions->new(pbot => $self, %conf); $self->{functions} = PBot::Functions->new(pbot => $self, %conf);
$self->{refresher} = PBot::Refresher->new(pbot => $self); $self->{refresher} = PBot::Refresher->new(pbot => $self);
$self->{ignorelist} = PBot::IgnoreList->new(pbot => $self, filename => "$conf{data_dir}/ignorelist", %conf); $self->{ignorelist} = PBot::IgnoreList->new(pbot => $self, filename => "$conf{data_dir}/ignorelist", %conf);
$self->{irc} = PBot::IRC->new();
$self->{irchandlers} = PBot::IRCHandlers->new(pbot => $self, %conf); $self->{irchandlers} = PBot::IRCHandlers->new(pbot => $self, %conf);
$self->{interpreter} = PBot::Interpreter->new(pbot => $self, %conf); $self->{interpreter} = PBot::Interpreter->new(pbot => $self, %conf);
$self->{lagchecker} = PBot::LagChecker->new(pbot => $self, %conf); $self->{lagchecker} = PBot::LagChecker->new(pbot => $self, %conf);
@ -332,16 +334,17 @@ sub exit {
# main loop # main loop
sub do_one_loop { sub do_one_loop {
my $self = shift; my ($self) = @_;
$self->{irc}->do_one_loop() if $self->{connected}; $self->{irc}->do_one_loop();
$self->{select_handler}->do_select;
} }
# main entry point # main entry point
sub start { sub start {
my $self = shift; my $self = shift;
$self->connect;
while (1) { while (1) {
$self->connect if not $self->{connected};
$self->do_one_loop; $self->do_one_loop;
} }
} }

View File

@ -1,6 +1,7 @@
# File: SelectHandler.pm # File: SelectHandler.pm
# #
# Purpose: Invokes select() system call and handles its events. # Purpose: Adds/removes file handles to/from PBot::IRC's select loop
# and contains handlers for select events.
# This Source Code Form is subject to the terms of the Mozilla Public # This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this # License, v. 2.0. If a copy of the MPL was not distributed with this
@ -11,87 +12,75 @@ use parent 'PBot::Class';
use PBot::Imports; use PBot::Imports;
use IO::Select;
sub initialize { sub initialize {
my ($self, %conf) = @_; # nothing to initialize
$self->{select} = IO::Select->new();
$self->{readers} = {};
$self->{buffers} = {};
} }
sub add_reader { sub add_reader {
my ($self, $handle, $subref) = @_; my ($self, $handle, $subref) = @_;
$self->{select}->add($handle);
$self->{readers}->{$handle} = $subref; # add file handle to PBot::IRC's select loop
$self->{pbot}->{irc}->addfh($handle, sub { $self->on_select_read($handle, $subref) }, 'r');
# create read buffer for this handle
$self->{buffers}->{$handle} = ''; $self->{buffers}->{$handle} = '';
} }
sub remove_reader { sub remove_reader {
my ($self, $handle) = @_; my ($self, $handle) = @_;
$self->{select}->remove($handle);
delete $self->{readers}->{$handle}; # remove file handle from PBot::IRC's select loop
$self->{pbot}->{irc}->removefh($handle);
# delete this handle's read buffer
delete $self->{buffers}->{$handle}; delete $self->{buffers}->{$handle};
} }
sub do_select { sub on_select_read {
my ($self) = @_; my ($self, $handle, $subref) = @_;
# maximum read length # maximum read length
my $length = 8192; my $length = 8192;
# check if any readers can read
my @ready = $self->{select}->can_read(.1);
foreach my $fh (@ready) {
# read from handle # read from handle
my $ret = sysread($fh, my $buf, $length); my $ret = sysread($handle, my $buf, $length);
# error reading # error reading
if (not defined $ret) { if (not defined $ret) {
$self->{pbot}->{logger}->log("SelectHandler: Error reading $fh: $!\n"); $self->{pbot}->{logger}->log("SelectHandler: Error reading $handle: $!\n");
$self->remove_reader($fh); $self->remove_reader($handle);
next; return;
} }
# reader closed # reader closed
if ($ret == 0) { if ($ret == 0) {
# is there anything in reader's buffer? # is there anything in reader's buffer?
if (length $self->{buffers}->{$fh}) { if (length $self->{buffers}->{$handle}) {
# send buffer to reader subref # send buffer to reader subref
$self->{readers}->{$fh}->($self->{buffers}->{$fh}); $self->{readers}->{$handle}->($self->{buffers}->{$handle});
} }
# remove reader # remove reader
$self->remove_reader($fh); $self->remove_reader($handle);
return;
# skip to next reader
next;
}
# sanity check for missing reader
if (not exists $self->{readers}->{$fh}) {
$self->{pbot}->{logger}->log("Error: no reader for $fh\n");
# skip to next reader
next;
} }
# accumulate input into reader's buffer # accumulate input into reader's buffer
$self->{buffers}->{$fh} .= $buf; $self->{buffers}->{$handle} .= $buf;
# if we read less than max length bytes then this is probably # if we read less than max length bytes then this is probably
# a complete message so send it to reader now, otherwise we'll # a complete message so send it to reader now, otherwise we'll
# continue to accumulate input into reader's buffer and then send # continue to accumulate input into reader's buffer and then send
# the buffer when reader closes. # the buffer when reader closes.
#
# FIXME: this should be line-based or some protocol.
if ($ret < $length) { if ($ret < $length) {
# send reader's buffer to reader subref # send reader's buffer to reader's consumer subref
$self->{readers}->{$fh}->($self->{buffers}->{$fh}); $subref->($self->{buffers}->{$handle});
# clear out reader's buffer # clear out reader's buffer
$self->{buffers}->{$fh} = ''; $self->{buffers}->{$handle} = '';
}
} }
} }