* [PATCH] dir_idle: support IN_DELETE_SELF|IN_MOVE_SELF, too
@ 2021-05-14 20:14 Eric Wong
0 siblings, 0 replies; only message in thread
From: Eric Wong @ 2021-05-14 20:14 UTC (permalink / raw)
To: meta
We'll treat IN_MOVE_SELF as IN_DELETE_SELF since there
doesn't seem to be a reliable way to distinguish them
with FakeInotify, nor know the new name with kevent.
---
lib/PublicInbox/DirIdle.pm | 12 +++++++++---
lib/PublicInbox/FakeInotify.pm | 23 ++++++++++++++++++++---
lib/PublicInbox/KQNotify.pm | 32 +++++++++++++++++++++++++-------
t/dir_idle.t | 27 ++++++++++++++++++++++++---
4 files changed, 78 insertions(+), 16 deletions(-)
diff --git a/lib/PublicInbox/DirIdle.pm b/lib/PublicInbox/DirIdle.pm
index e53fd9d1..5142d005 100644
--- a/lib/PublicInbox/DirIdle.pm
+++ b/lib/PublicInbox/DirIdle.pm
@@ -12,17 +12,23 @@ my ($MAIL_IN, $MAIL_GONE, $ino_cls);
if ($^O eq 'linux' && eval { require Linux::Inotify2; 1 }) {
$MAIL_IN = Linux::Inotify2::IN_MOVED_TO() |
Linux::Inotify2::IN_CREATE();
- $MAIL_GONE = Linux::Inotify2::IN_DELETE();
+ $MAIL_GONE = Linux::Inotify2::IN_DELETE() |
+ Linux::Inotify2::IN_DELETE_SELF() |
+ Linux::Inotify2::IN_MOVE_SELF();
$ino_cls = 'Linux::Inotify2';
# Perl 5.22+ is needed for fileno(DIRHANDLE) support:
} elsif ($^V ge v5.22 && eval { require PublicInbox::KQNotify }) {
$MAIL_IN = PublicInbox::KQNotify::MOVED_TO_OR_CREATE();
- $MAIL_GONE = PublicInbox::KQNotify::NOTE_DELETE();
+ $MAIL_GONE = PublicInbox::KQNotify::NOTE_DELETE() |
+ PublicInbox::KQNotify::NOTE_REVOKE() |
+ PublicInbox::KQNotify::NOTE_RENAME();
$ino_cls = 'PublicInbox::KQNotify';
} else {
require PublicInbox::FakeInotify;
$MAIL_IN = PublicInbox::FakeInotify::MOVED_TO_OR_CREATE();
- $MAIL_GONE = PublicInbox::FakeInotify::IN_DELETE();
+ $MAIL_GONE = PublicInbox::FakeInotify::IN_DELETE() |
+ PublicInbox::FakeInotify::IN_DELETE_SELF() |
+ PublicInbox::FakeInotify::IN_MOVE_SELF();
}
sub new {
diff --git a/lib/PublicInbox/FakeInotify.pm b/lib/PublicInbox/FakeInotify.pm
index 644f5b5b..641bc5bd 100644
--- a/lib/PublicInbox/FakeInotify.pm
+++ b/lib/PublicInbox/FakeInotify.pm
@@ -13,7 +13,9 @@ sub IN_MODIFY () { 0x02 } # match Linux inotify
# my $IN_MOVED_TO = 0x80;
# my $IN_CREATE = 0x100;
sub MOVED_TO_OR_CREATE () { 0x80 | 0x100 }
-sub IN_DELETE () { 0x00000200 }
+sub IN_DELETE () { 0x200 }
+sub IN_DELETE_SELF () { 0x400 }
+sub IN_MOVE_SELF () { 0x800 }
our @EXPORT_OK = qw(fill_dirlist on_dir_change);
@@ -44,7 +46,7 @@ sub watch {
# also used by KQNotify since it kevent requires readdir on st_nlink
# count changes.
-sub on_dir_change ($$$$;$) {
+sub on_dir_change ($$$$$) {
my ($events, $dh, $path, $old_ctime, $dirlist) = @_;
my $oldlist = $dirlist->{$path};
my $newlist = $oldlist ? {} : undef;
@@ -79,7 +81,14 @@ sub read {
my @watch_gone;
for my $x (keys %$watch) {
my ($path, $mask) = split(/\0/, $x, 2);
- my @now = stat($path) or next;
+ my @now = stat($path);
+ if (!@now && $!{ENOENT} && ($mask & IN_DELETE_SELF)) {
+ push @$events, bless(\$path,
+ 'PublicInbox::FakeInotify::SelfGoneEvent');
+ push @watch_gone, $x;
+ delete $self->{dirlist}->{$path};
+ }
+ next if !@now;
my $old_ctime = $watch->{$x};
$watch->{$x} = $now[10];
next if $old_ctime == $now[10];
@@ -92,6 +101,7 @@ sub read {
$self->{dirlist});
} elsif ($!{ENOENT}) {
push @watch_gone, $x;
+ delete $self->{dirlist}->{$path};
} else {
warn "W: opendir $path: $!\n";
}
@@ -126,6 +136,7 @@ use strict;
sub fullname { ${$_[0]} }
sub IN_DELETE { 0 }
+sub IN_DELETE_SELF { 0 }
package PublicInbox::FakeInotify::GoneEvent;
use strict;
@@ -133,4 +144,10 @@ our @ISA = qw(PublicInbox::FakeInotify::Event);
sub IN_DELETE { 1 }
+package PublicInbox::FakeInotify::SelfGoneEvent;
+use strict;
+our @ISA = qw(PublicInbox::FakeInotify::GoneEvent);
+
+sub IN_DELETE_SELF { 1 }
+
1;
diff --git a/lib/PublicInbox/KQNotify.pm b/lib/PublicInbox/KQNotify.pm
index fc321a16..7efb8b60 100644
--- a/lib/PublicInbox/KQNotify.pm
+++ b/lib/PublicInbox/KQNotify.pm
@@ -33,14 +33,16 @@ sub watch {
'PublicInbox::KQNotify::Watch';
}
my $ident = fileno($fh);
- $self->{dskq}->{kq}->EV_SET($ident, # ident
+ $self->{dskq}->{kq}->EV_SET($ident, # ident (fd)
EVFILT_VNODE, # filter
EV_ADD | EV_CLEAR, # flags
$mask, # fflags
0, 0); # data, udata
- if ($mask & (MOVED_TO_OR_CREATE | NOTE_DELETE)) {
+ if ($mask & (MOVED_TO_OR_CREATE|NOTE_DELETE|NOTE_LINK|NOTE_REVOKE)) {
$self->{watch}->{$ident} = $watch;
- fill_dirlist($self, $path, $fh) if $mask & NOTE_DELETE;
+ if ($mask & (NOTE_DELETE|NOTE_LINK|NOTE_REVOKE)) {
+ fill_dirlist($self, $path, $fh)
+ }
} else {
die "TODO Not implemented: $mask";
}
@@ -63,21 +65,37 @@ sub read {
my ($self) = @_;
my @kevents = $self->{dskq}->{kq}->kevent(0);
my $events = [];
+ my @gone;
+ my $watch = $self->{watch};
for my $kev (@kevents) {
my $ident = $kev->[KQ_IDENT];
my $mask = $kev->[KQ_FFLAGS];
- my ($dh, $path, $old_ctime) = @{$self->{watch}->{$ident}};
+ my ($dh, $path, $old_ctime) = @{$watch->{$ident}};
if (!defined($old_ctime)) {
push @$events,
bless(\$path, 'PublicInbox::FakeInotify::Event')
- } elsif ($mask & (MOVED_TO_OR_CREATE | NOTE_DELETE)) {
- my @new_st = stat($path) or next;
- $self->{watch}->{$ident}->[3] = $new_st[10]; # ctime
+ } elsif ($mask & (MOVED_TO_OR_CREATE|NOTE_DELETE|NOTE_LINK|
+ NOTE_REVOKE|NOTE_RENAME)) {
+ my @new_st = stat($path);
+ if (!@new_st && $!{ENOENT}) {
+ push @$events, bless(\$path,
+ 'PublicInbox::FakeInotify::'.
+ 'SelfGoneEvent');
+ push @gone, $ident;
+ delete $self->{dirlist}->{$path};
+ next;
+ }
+ if (!@new_st) {
+ warn "unhandled stat($path) error: $!\n";
+ next;
+ }
+ $watch->{$ident}->[3] = $new_st[10]; # ctime
rewinddir($dh);
on_dir_change($events, $dh, $path, $old_ctime,
$self->{dirlist});
}
}
+ delete @$watch{@gone};
@$events;
}
diff --git a/t/dir_idle.t b/t/dir_idle.t
index 969c16e9..0bb3b758 100644
--- a/t/dir_idle.t
+++ b/t/dir_idle.t
@@ -6,10 +6,10 @@ use PublicInbox::DS qw(now);
use File::Path qw(make_path);
use_ok 'PublicInbox::DirIdle';
my ($tmpdir, $for_destroy) = tmpdir();
-make_path("$tmpdir/a/b");
+make_path("$tmpdir/a/b", "$tmpdir/c");
my @x;
my $cb = sub { push @x, \@_ };
-my $di = PublicInbox::DirIdle->new(["$tmpdir/a"], $cb, 1);
+my $di = PublicInbox::DirIdle->new(["$tmpdir/a", "$tmpdir/c"], $cb, 1);
PublicInbox::DS->SetLoopTimeout(1000);
my $end = 3 + now;
PublicInbox::DS->SetPostLoopCallback(sub { scalar(@x) == 0 && now < $end });
@@ -17,6 +17,27 @@ tick(0.011);
rmdir("$tmpdir/a/b") or xbail "rmdir $!";
PublicInbox::DS->EventLoop;
is(scalar(@x), 1, 'got an event') and
- is($x[0]->[0]->fullname, "$tmpdir/a/b", 'got expected fullname');
+ is($x[0]->[0]->fullname, "$tmpdir/a/b", 'got expected fullname') and
+ ok($x[0]->[0]->IN_DELETE, 'IN_DELETE set');
+
+tick(0.011);
+rmdir("$tmpdir/a") or xbail "rmdir $!";
+@x = ();
+$end = 3 + now;
+PublicInbox::DS->EventLoop;
+is(scalar(@x), 1, 'got an event') and
+ is($x[0]->[0]->fullname, "$tmpdir/a", 'got expected fullname') and
+ ok($x[0]->[0]->IN_DELETE_SELF, 'IN_DELETE_SELF set');
+
+tick(0.011);
+rename("$tmpdir/c", "$tmpdir/j") or xbail "rmdir $!";
+@x = ();
+$end = 3 + now;
+PublicInbox::DS->EventLoop;
+is(scalar(@x), 1, 'got an event') and
+ is($x[0]->[0]->fullname, "$tmpdir/c", 'got expected fullname') and
+ ok($x[0]->[0]->IN_DELETE_SELF || $x[0]->[0]->IN_MOVE_SELF,
+ 'IN_DELETE_SELF set on move');
+
PublicInbox::DS->Reset;
done_testing;
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2021-05-14 20:14 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-14 20:14 [PATCH] dir_idle: support IN_DELETE_SELF|IN_MOVE_SELF, too Eric Wong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).