From b2b1006759730507731fcd3fc3e0de68239e3b92 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sat, 27 Jun 2020 10:03:36 +0000 Subject: watch: remove Filesys::Notify::Simple dependency Since we already use inotify and EVFILT_VNODE (kqueue) in -imapd, we might as well use them directly in -watch, too. This will allow public-inbox-watch to use PublicInbox::DS for timers to watch newsgroups/mailboxes and have saner signal handling in future commits. --- lib/PublicInbox/DirIdle.pm | 50 +++++++++++++++++++++++++++++++++++++++++ lib/PublicInbox/In2Tie.pm | 13 +++++++++++ lib/PublicInbox/InboxIdle.pm | 11 ++------- lib/PublicInbox/TestCommon.pm | 6 ++++- lib/PublicInbox/WatchMaildir.pm | 39 ++++++++++++++++---------------- 5 files changed, 89 insertions(+), 30 deletions(-) create mode 100644 lib/PublicInbox/DirIdle.pm (limited to 'lib/PublicInbox') diff --git a/lib/PublicInbox/DirIdle.pm b/lib/PublicInbox/DirIdle.pm new file mode 100644 index 00000000..ffceda66 --- /dev/null +++ b/lib/PublicInbox/DirIdle.pm @@ -0,0 +1,50 @@ +# Copyright (C) 2020 all contributors +# License: AGPL-3.0+ + +# 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 PublicInbox::Syscall qw(EPOLLIN EPOLLET); +use PublicInbox::In2Tie; + +my ($MAIL_IN, $ino_cls); +if ($^O eq 'linux' && eval { require Linux::Inotify2; 1 }) { + $MAIL_IN = Linux::Inotify2::IN_MOVED_TO() | + Linux::Inotify2::IN_CREATE(); + $ino_cls = 'Linux::Inotify2'; +} elsif (eval { require PublicInbox::KQNotify }) { + $MAIL_IN = PublicInbox::KQNotify::MOVED_TO_OR_CREATE(); + $ino_cls = 'PublicInbox::KQNotify'; +} else { + require PublicInbox::FakeInotify; + $MAIL_IN = PublicInbox::FakeInotify::MOVED_TO_OR_CREATE(); +} + +sub new { + my ($class, $dirs, $cb) = @_; + my $self = fields::new($class); + my $inot; + if ($ino_cls) { + $inot = $ino_cls->new or die "E: $ino_cls->new: $!"; + my $io = PublicInbox::In2Tie::io($inot); + $self->SUPER::new($io, EPOLLIN | EPOLLET); + } else { + require PublicInbox::FakeInotify; + $inot = PublicInbox::FakeInotify->new; # starts timer + } + + # Linux::Inotify2->watch or similar + $inot->watch($_, $MAIL_IN, $cb) for @$dirs; + $self->{inot} = $inot; + $self; +} + +sub event_step { + my ($self) = @_; + eval { $self->{inot}->poll }; # Linux::Inotify2::poll + warn "$self->{inot}->poll err: $@\n" if $@; +} + +1; diff --git a/lib/PublicInbox/In2Tie.pm b/lib/PublicInbox/In2Tie.pm index db1dc104..7dee3627 100644 --- a/lib/PublicInbox/In2Tie.pm +++ b/lib/PublicInbox/In2Tie.pm @@ -5,6 +5,19 @@ # on Linux::Inotify2 objects package PublicInbox::In2Tie; use strict; +use Symbol qw(gensym); + +sub io { + my $in2 = $_[0]; + $in2->blocking(0); + if ($in2->can('on_overflow')) { + # broadcasts everything on overflow + $in2->on_overflow(undef); + } + my $io = gensym; + tie *$io, __PACKAGE__, $in2; + $io; +} sub TIEHANDLE { my ($class, $in2) = @_; diff --git a/lib/PublicInbox/InboxIdle.pm b/lib/PublicInbox/InboxIdle.pm index 97e9d532..ba8200ae 100644 --- a/lib/PublicInbox/InboxIdle.pm +++ b/lib/PublicInbox/InboxIdle.pm @@ -6,7 +6,6 @@ use strict; use base qw(PublicInbox::DS); use fields qw(pi_config inot pathmap); use Cwd qw(abs_path); -use Symbol qw(gensym); use PublicInbox::Syscall qw(EPOLLIN EPOLLET); my $IN_MODIFY = 0x02; # match Linux inotify my $ino_cls; @@ -55,14 +54,8 @@ sub new { my $inot; if ($ino_cls) { $inot = $ino_cls->new or die "E: $ino_cls->new: $!"; - my $sock = gensym; - tie *$sock, 'PublicInbox::In2Tie', $inot; - $inot->blocking(0); - if ($inot->can('on_overflow')) { - # broadcasts everything on overflow - $inot->on_overflow(undef); - } - $self->SUPER::new($sock, EPOLLIN | EPOLLET); + my $io = PublicInbox::In2Tie::io($inot); + $self->SUPER::new($io, EPOLLIN | EPOLLET); } else { require PublicInbox::FakeInotify; $inot = PublicInbox::FakeInotify->new; diff --git a/lib/PublicInbox/TestCommon.pm b/lib/PublicInbox/TestCommon.pm index dc360135..b252810f 100644 --- a/lib/PublicInbox/TestCommon.pm +++ b/lib/PublicInbox/TestCommon.pm @@ -10,7 +10,7 @@ use Fcntl qw(FD_CLOEXEC F_SETFD F_GETFD :seek); use POSIX qw(dup2); use IO::Socket::INET; our @EXPORT = qw(tmpdir tcp_server tcp_connect require_git require_mods - run_script start_script key2sub xsys xqx eml_load); + run_script start_script key2sub xsys xqx eml_load tick); sub eml_load ($) { my ($path, $cb) = @_; @@ -418,4 +418,8 @@ sub DESTROY { $self->join('TERM'); } +package PublicInbox::TestCommon::InboxWakeup; +use strict; +sub on_inbox_unlock { ${$_[0]}->($_[1]) } + 1; diff --git a/lib/PublicInbox/WatchMaildir.pm b/lib/PublicInbox/WatchMaildir.pm index fea7d5ef..22f19036 100644 --- a/lib/PublicInbox/WatchMaildir.pm +++ b/lib/PublicInbox/WatchMaildir.pm @@ -119,19 +119,6 @@ sub _done_for_now { } } -sub _try_fsn_paths { - my ($self, $scan_re, $paths) = @_; - foreach (@$paths) { - my $path = $_->{path}; - if ($path =~ $scan_re) { - scan($self, $path); - } else { - _try_path($self, $path); - } - } - _done_for_now($self); -} - sub remove_eml_i { # each_inbox callback my ($ibx, $arg) = @_; my ($self, $eml, $loc) = @$arg; @@ -225,16 +212,28 @@ sub quit { sub watch_fs { my ($self) = @_; + require PublicInbox::DirIdle; my $scan = File::Temp->newdir("public-inbox-watch.$$.scan.XXXXXX", TMPDIR => 1); my $scandir = $self->{scandir} = $scan->dirname; - my $re = qr!\A$scandir/!; - my $cb = sub { _try_fsn_paths($self, $re, \@_) }; - - eval { require Filesys::Notify::Simple } or - die "Filesys::Notify::Simple is currently required for $0\n"; - my $fsn = Filesys::Notify::Simple->new([@{$self->{mdir}}, $scandir]); - $fsn->wait($cb) until $self->{quit}; + my $scan_re = qr!\A$scandir/!; + my $done = sub { + delete $self->{done_timer}; + _done_for_now($self); + }; + my $cb = sub { + my $path = $_[0]->fullname; + if ($path =~ $scan_re) { + scan($self, $path); + } else { + _try_path($self, $path); + } + $self->{done_timer} //= PublicInbox::DS::requeue($done); + }; + my $di = PublicInbox::DirIdle->new([@{$self->{mdir}}, $scandir], $cb); + PublicInbox::DS->SetPostLoopCallback(sub { !$self->{quit} }); + PublicInbox::DS->EventLoop; + _done_for_now($self); } # returns the git config section name, e.g [imap "imaps://user@example.com"] -- cgit v1.2.3-24-ge0c7