X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=lib%2FPublicInbox%2FKQNotify.pm;h=c7740df2dc0ca1e8dc80d12bf5af5ed4c0d3bb85;hb=0d38f65c490466837ae091afa7a7b6f59d04ce7c;hp=3cf9c0f5a0c64c304ff43e5522afc9e20cbee46c;hpb=34c1a6c16733adee3acfe5861096692f3ea55378;p=public-inbox.git diff --git a/lib/PublicInbox/KQNotify.pm b/lib/PublicInbox/KQNotify.pm index 3cf9c0f5..c7740df2 100644 --- a/lib/PublicInbox/KQNotify.pm +++ b/lib/PublicInbox/KQNotify.pm @@ -7,10 +7,11 @@ package PublicInbox::KQNotify; use strict; use IO::KQueue; use PublicInbox::DSKQXS; # wraps IO::KQueue for fork-safe DESTROY +use PublicInbox::FakeInotify; +use Time::HiRes qw(stat); -# only true as far as public-inbox is concerned with .lock files: -sub IN_CLOSE () { NOTE_WRITE } -#sub IN_CLOSE () { 0x200 } # NOTE_CLOSE_WRITE (FreeBSD 11+ only) +# NOTE_EXTEND detects rename(2), NOTE_WRITE detects link(2) +sub MOVED_TO_OR_CREATE () { NOTE_EXTEND|NOTE_WRITE } sub new { my ($class) = @_; @@ -18,19 +19,30 @@ sub new { } sub watch { - my ($self, $path, $mask, $cb) = @_; - open(my $fh, '<', $path) or return; + my ($self, $path, $mask) = @_; + my ($fh, $watch); + if (-d $path) { + opendir($fh, $path) or return; + my @st = stat($fh); + $watch = bless [ $fh, $path, $st[10] ], + 'PublicInbox::KQNotify::Watchdir'; + } else { + open($fh, '<', $path) or return; + $watch = bless [ $fh, $path ], + 'PublicInbox::KQNotify::Watch'; + } my $ident = fileno($fh); $self->{dskq}->{kq}->EV_SET($ident, # ident EVFILT_VNODE, # filter EV_ADD | EV_CLEAR, # flags $mask, # fflags 0, 0); # data, udata - if ($mask == IN_CLOSE) { - $self->{watch}->{$ident} = [ $fh, $cb ]; + if ($mask == NOTE_WRITE || $mask == MOVED_TO_OR_CREATE) { + $self->{watch}->{$ident} = $watch; } else { die "TODO Not implemented: $mask"; } + $watch; } # emulate Linux::Inotify::fileno @@ -44,17 +56,41 @@ sub on_overflow {} # noop for Linux::Inotify2 compatibility, we use `0' timeout for ->kevent sub blocking {} -# behave like Linux::Inotify2::poll -sub poll { +# behave like Linux::Inotify2->read +sub read { my ($self) = @_; my @kevents = $self->{dskq}->{kq}->kevent(0); + my $events = []; for my $kev (@kevents) { my $ident = $kev->[KQ_IDENT]; my $mask = $kev->[KQ_FFLAGS]; - if (($mask & IN_CLOSE) == IN_CLOSE) { - eval { $self->{watch}->{$ident}->[1]->() }; + my ($dh, $path, $old_ctime) = @{$self->{watch}->{$ident}}; + if (!defined($old_ctime)) { + push @$events, + bless(\$path, 'PublicInbox::FakeInotify::Event') + } elsif ($mask & MOVED_TO_OR_CREATE) { + my @new_st = stat($path) or next; + $self->{watch}->{$ident}->[3] = $new_st[10]; # ctime + rewinddir($dh); + PublicInbox::FakeInotify::on_new_files($events, $dh, + $path, $old_ctime); } } + @$events; } +package PublicInbox::KQNotify::Watch; +use strict; + +sub name { $_[0]->[1] } + +sub cancel { close $_[0]->[0] or die "close: $!" } + +package PublicInbox::KQNotify::Watchdir; +use strict; + +sub name { $_[0]->[1] } + +sub cancel { closedir $_[0]->[0] or die "closedir: $!" } + 1;