# Copyright (C) all contributors # License: AGPL-3.0+ # implements the small subset of Linux::Inotify2 functionality we use # using IO::KQueue on *BSD systems. package PublicInbox::KQNotify; use v5.12; use parent qw(PublicInbox::FakeInotify); use IO::KQueue; use PublicInbox::DSKQXS; # wraps IO::KQueue for fork-safe DESTROY use Errno qw(ENOENT); # NOTE_EXTEND detects rename(2), NOTE_WRITE detects link(2) sub MOVED_TO_OR_CREATE () { NOTE_EXTEND|NOTE_WRITE } sub new { my ($class) = @_; bless { dskq => PublicInbox::DSKQXS->new }, $class; } sub watch { my ($self, $path, $mask) = @_; my $dir_delete = $mask & NOTE_DELETE ? 1 : 0; my $w = $self->watch_open($path, \$dir_delete) or return; $w->[2] = pop @$w; # ctime is unused by this subclass my $ident = fileno($w->[2]) // die "BUG: bad fileno $w->[2]: $!"; $self->{dskq}->{kq}->EV_SET($ident, # ident (fd) EVFILT_VNODE, # filter EV_ADD | EV_CLEAR, # flags $mask, # fflags 0, $dir_delete); # data, udata $self->{watch}->{$ident} = $w; } # emulate Linux::Inotify::fileno sub fileno { ${$_[0]->{dskq}->{kq}} } # noop for Linux::Inotify2 compatibility. Unlike inotify, # kqueue doesn't seem to overflow since it's limited by the number of # open FDs the process has sub on_overflow {} # noop for Linux::Inotify2 compatibility, we use `0' timeout for ->kevent sub blocking {} # behave like Linux::Inotify2->read sub read { my ($self) = @_; my $events = []; for my $kev ($self->{dskq}->{kq}->kevent(0)) { my $ident = $kev->[KQ_IDENT]; my $w = $self->{watch}->{$ident} or next; if (!@$w) { # cancelled delete($self->{watch}->{$ident}); next; } my $dir_delete = $kev->[KQ_UDATA]; my ($old_dev, $old_ino, $fh, $path) = @$w; my @new_st = stat($path); warn "W: stat($path): $!\n" if !@new_st && $! != ENOENT; if (!@new_st || "$old_dev $old_ino" ne "@new_st[0,1]") { push(@$events, $self->gone($ident, $path)); next; } if (-d _) { rewinddir($fh); $self->on_dir_change($events, $fh, $path, $dir_delete); } else { push @$events, bless(\$path, 'PublicInbox::FakeInotify::Event'); } } @$events; } 1;