1package AnyEvent::Filesys::Notify::Role::KQueue;
2
3# ABSTRACT: Use IO::KQueue to watch for changed files
4
5use Moo::Role;
6use MooX::late;
7use namespace::autoclean;
8use AnyEvent;
9use IO::KQueue;
10use Carp;
11
12our $VERSION = '1.23';
13
14# Arbitrary limit on open filehandles before issuing a warning
15our $WARN_FILEHANDLE_LIMIT = 50;
16
17sub _init {
18    my $self = shift;
19
20    my $kqueue = IO::KQueue->new()
21      or croak "Unable to create new IO::KQueue object";
22    $self->_fs_monitor($kqueue);
23
24    # Need to add all the subdirs to the watch list, this will catch
25    # modifications to files too.
26    my $old_fs = $self->_old_fs;
27    my @paths  = keys %$old_fs;
28
29    # Add each file and each directory to a hash of path => fh
30    my $fhs = {};
31    for my $path (@paths) {
32        my $fh = $self->_watch($path);
33        $fhs->{$path} = $fh if defined $fh;
34    }
35
36    # Now use AE to watch the KQueue
37    my $w;
38    $w = AE::io $$kqueue, 0, sub {
39        if ( my @events = $kqueue->kevent ) {
40            $self->_process_events(@events);
41        }
42    };
43    $self->_watcher( { fhs => $fhs, w => $w } );
44
45    $self->_check_filehandle_count;
46    return 1;
47}
48
49# Need to add newly created items (directories and files) or remove deleted
50# items.  This isn't going to be perfect. If the path is not canonical then we
51# won't deleted it.  This is done after filtering. So entire dirs can be
52# ignored efficiently.
53sub _post_process_events {
54    my ( $self, @events ) = @_;
55
56    for my $event (@events) {
57        if ( $event->is_created ) {
58            my $fh = $self->_watch( $event->path );
59            $self->_watcher->{fhs}->{ $event->path } = $fh if defined $fh;
60        } elsif ( $event->is_deleted ) {
61            delete $self->_watcher->{fhs}->{ $event->path };
62        }
63    }
64
65    $self->_check_filehandle_count;
66    return;
67}
68
69sub _watch {
70    my ( $self, $path ) = @_;
71
72    open my $fh, '<', $path or do {
73        warn
74          "KQueue requires a filehandle for each watched file and directory.\n"
75          . "You have exceeded the number of filehandles permitted by the OS.\n"
76          if $! =~ /^Too many open files/;
77        return if $! =~ /no such file or directory/i;
78        croak "Can't open file ($path): $!";
79    };
80
81    $self->_fs_monitor->EV_SET(
82        fileno($fh),
83        EVFILT_VNODE,
84        EV_ADD | EV_ENABLE | EV_CLEAR,
85        NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK |
86          NOTE_RENAME | NOTE_REVOKE,
87    );
88
89    return $fh;
90}
91
92sub _check_filehandle_count {
93    my ($self) = @_;
94
95    my $count = $self->_watcher_count;
96    carp "KQueue requires a filehandle for each watched file and directory.\n"
97      . "You currently have $count filehandles for this AnyEvent::Filesys::Notify object.\n"
98      . "The use of the KQueue backend is not recommended."
99      if $count > $WARN_FILEHANDLE_LIMIT;
100
101    return $count;
102}
103
104sub _watcher_count {
105    my ($self) = @_;
106    my $fhs = $self->_watcher->{fhs};
107    return scalar keys %$fhs;
108}
109
1101;
111
112__END__
113
114=pod
115
116=head1 NAME
117
118AnyEvent::Filesys::Notify::Role::KQueue - Use IO::KQueue to watch for changed files
119
120=head1 VERSION
121
122version 1.23
123
124=head1 AUTHOR
125
126Mark Grimes, E<lt>mgrimes@cpan.orgE<gt>
127
128=head1 CONTRIBUTORS
129
130=over 4
131
132=item *
133
134Gasol Wu E<lt>gasol.wu@gmail.comE<gt> who contributed the BSD support for IO::KQueue
135
136=item *
137
138Dave Hayes E<lt>dave@jetcafe.orgE<gt>
139
140=item *
141
142Carsten Wolff E<lt>carsten@wolffcarsten.deE<gt>
143
144=item *
145
146Ettore Di Giacinto (@mudler)
147
148=item *
149
150Martin Barth (@ufobat)
151
152=back
153
154=head1 SOURCE
155
156Source repository is at L<https://github.com/mvgrimes/AnyEvent-Filesys-Notify>.
157
158=head1 BUGS
159
160Please report any bugs or feature requests on the bugtracker website L<http://github.com/mvgrimes/AnyEvent-Filesys-Notify/issues>
161
162When submitting a bug or request, please include a test-file or a
163patch to an existing test-file that illustrates the bug or desired
164feature.
165
166=head1 COPYRIGHT AND LICENSE
167
168This software is copyright (c) 2017 by Mark Grimes, E<lt>mgrimes@cpan.orgE<gt>.
169
170This is free software; you can redistribute it and/or modify it under
171the same terms as the Perl 5 programming language system itself.
172
173=cut
174