package AnyEvent::Filesys::Notify::Role::KQueue;
# ABSTRACT: Use IO::KQueue to watch for changed files
use Moo::Role;
use MooX::late;
use namespace::sweep;
use AnyEvent;
use IO::KQueue;
use Carp;
# Arbitrary limit on open filehandles before issuing a warning
our $WARN_FILEHANDLE_LIMIT = 50;
sub _init {
my $self = shift;
my $kqueue = IO::KQueue->new()
or croak "Unable to create new IO::KQueue object";
$self->_fs_monitor($kqueue);
# Need to add all the subdirs to the watch list, this will catch
# modifications to files too.
my $old_fs = $self->_old_fs;
my @paths = keys %$old_fs;
# Add each file and each directory
my @fhs;
for my $path (@paths) {
push @fhs, $self->_watch($path);
}
# Now use AE to watch the KQueue
my $w;
$w = AE::io $$kqueue, 0, sub {
if ( my @events = $kqueue->kevent ) {
$self->_process_events(@events);
}
};
$self->_watcher( { fhs => \@fhs, w => $w } );
$self->_check_filehandle_count;
return 1;
}
# Need to add newly created items (directories and files).
# This is done after filtering. So entire dirs can be ignored efficiently.
around '_process_events' => sub {
my ( $orig, $self, @e ) = @_;
my $events = $self->$orig(@e);
for my $event (@$events) {
next unless $event->is_created;
my $fh = $self->_watch( $event->path );
push @{ $self->_watcher->{fhs} }, $fh;
}
$self->_check_filehandle_count;
return $events;
};
sub _watch {
my ( $self, $path ) = @_;
open my $fh, '<', $path or do {
warn
"KQueue requires a filehandle for each watched file and directory.\n"
. "You have exceeded the number of filehandles permitted by the OS.\n"
if $! =~ /^Too many open files/;
croak "Can't open file ($path): $!";
};
$self->_fs_monitor->EV_SET(
fileno($fh),
EVFILT_VNODE,
EV_ADD | EV_ENABLE | EV_CLEAR,
NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK |
NOTE_RENAME | NOTE_REVOKE,
);
return $fh;
}
sub _check_filehandle_count {
my ($self) = @_;
my $count = $self->_watcher_count;
carp "KQueue requires a filehandle for each watched file and directory.\n"
. "You currently have $count filehandles for this AnyEvent::Filesys::Notify object.\n"
. "The use of the KQueue backend is not recommended."
if $count > $WARN_FILEHANDLE_LIMIT;
}
sub _watcher_count {
my ($self) = @_;
my $fhs = $self->_watcher->{fhs};
return scalar @$fhs;
}
1;
__END__
=pod
=head1 NAME
AnyEvent::Filesys::Notify::Role::KQueue - Use IO::KQueue to watch for changed files
=head1 VERSION
version 1.14
=head1 CONTRIBUTORS
Thanks to Gasol Wu E<lt>gasol.wu@gmail.comE<gt> who contributed the FreeBSD
support for IO::KQueue.
=head1 AUTHOR
Mark Grimes, E<lt>mgrimes@cpan.orgE<gt>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2013 by Mark Grimes, E<lt>mgrimes@cpan.orgE<gt>.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut