1 # Copyright (C) 2020 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
4 # implements the small subset of Linux::Inotify2 functionality we use
5 # using IO::KQueue on *BSD systems.
6 package PublicInbox::KQNotify;
9 use PublicInbox::DSKQXS; # wraps IO::KQueue for fork-safe DESTROY
13 bless { dskq => PublicInbox::DSKQXS->new, watch => {} }, $class;
17 my ($self, $path, $mask, $cb) = @_;
18 open(my $fh, '<', $path) or return;
19 my $ident = fileno($fh);
20 $self->{dskq}->{kq}->EV_SET($ident, # ident
21 EVFILT_VNODE, # filter
22 EV_ADD | EV_CLEAR, # flags
25 if ($mask == NOTE_WRITE) {
26 $self->{watch}->{$ident} = [ $fh, $cb ];
28 die "TODO Not implemented: $mask";
30 bless \$fh, 'PublicInbox::KQNotify::Watch';
33 # emulate Linux::Inotify::fileno
34 sub fileno { ${$_[0]->{dskq}->{kq}} }
36 # noop for Linux::Inotify2 compatibility. Unlike inotify,
37 # kqueue doesn't seem to overflow since it's limited by the number of
38 # open FDs the process has
41 # noop for Linux::Inotify2 compatibility, we use `0' timeout for ->kevent
44 # behave like Linux::Inotify2::poll
47 my @kevents = $self->{dskq}->{kq}->kevent(0);
48 for my $kev (@kevents) {
49 my $ident = $kev->[KQ_IDENT];
50 my $mask = $kev->[KQ_FFLAGS];
51 if (($mask & NOTE_WRITE) == NOTE_WRITE) {
52 eval { $self->{watch}->{$ident}->[1]->() };
57 package PublicInbox::KQNotify::Watch;
60 sub cancel { close ${$_[0]} or die "close: $!" }