]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/KQNotify.pm
t/imapd: support FakeInotify and KQNotify
[public-inbox.git] / lib / PublicInbox / KQNotify.pm
1 # Copyright (C) 2020 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3
4 # implements the small subset of Linux::Inotify2 functionality we use
5 # using IO::KQueue on *BSD systems.
6 package PublicInbox::KQNotify;
7 use strict;
8 use IO::KQueue;
9 use PublicInbox::DSKQXS; # wraps IO::KQueue for fork-safe DESTROY
10
11 # only true as far as public-inbox is concerned with .lock files:
12 sub IN_CLOSE () { NOTE_WRITE }
13 #sub IN_CLOSE () { 0x200 } # NOTE_CLOSE_WRITE (FreeBSD 11+ only)
14
15 sub new {
16         my ($class) = @_;
17         bless { dskq => PublicInbox::DSKQXS->new, watch => {} }, $class;
18 }
19
20 sub watch {
21         my ($self, $path, $mask, $cb) = @_;
22         open(my $fh, '<', $path) or return;
23         my $ident = fileno($fh);
24         $self->{dskq}->{kq}->EV_SET($ident, # ident
25                 EVFILT_VNODE, # filter
26                 EV_ADD | EV_CLEAR, # flags
27                 $mask, # fflags
28                 0, 0); # data, udata
29         if ($mask == IN_CLOSE) {
30                 $self->{watch}->{$ident} = [ $fh, $cb ];
31         } else {
32                 die "TODO Not implemented: $mask";
33         }
34         bless \$fh, 'PublicInbox::KQNotify::Watch';
35 }
36
37 # emulate Linux::Inotify::fileno
38 sub fileno { ${$_[0]->{dskq}->{kq}} }
39
40 # noop for Linux::Inotify2 compatibility.  Unlike inotify,
41 # kqueue doesn't seem to overflow since it's limited by the number of
42 # open FDs the process has
43 sub on_overflow {}
44
45 # noop for Linux::Inotify2 compatibility, we use `0' timeout for ->kevent
46 sub blocking {}
47
48 # behave like Linux::Inotify2::poll
49 sub poll {
50         my ($self) = @_;
51         my @kevents = $self->{dskq}->{kq}->kevent(0);
52         for my $kev (@kevents) {
53                 my $ident = $kev->[KQ_IDENT];
54                 my $mask = $kev->[KQ_FFLAGS];
55                 if (($mask & IN_CLOSE) == IN_CLOSE) {
56                         eval { $self->{watch}->{$ident}->[1]->() };
57                 }
58         }
59 }
60
61 package PublicInbox::KQNotify::Watch;
62 use strict;
63
64 sub cancel { close ${$_[0]} or die "close: $!" }
65
66 1;