This code no longer works:
use strict;
use warnings;
use AnyEvent;
use Data::Dumper;
my $done = AnyEvent->condvar;
my $watcher1 = AnyEvent::Filesys::Watcher::FSEvents->new;
warn $watcher1->{__watchers};
$done->recv;
package AnyEvent::Filesys::Watcher::FSEvents;
use AnyEvent;
use Mac::FSEvents;
sub new {
my ($class) = @_;
my $self = {};
my $fs = Mac::FSEvents->new(
latency => 0.1,
file_events => 1,
path => [ '.' ],
);
my $fh = $fs->watch;
my $watchers = [AnyEvent->io(
fh => $fh,
poll => 'r',
cb => sub {
warn "event fired";
exit 0;
}
)];
$self->{__watchers} = $watchers;
bless $self, $class;
}
When you run the code and change a file in the current directory, no event gets fired. The debugging output in line 10 should ensure that the watcher variable has not gone out of scope which is a very common reason for failures like the current one.
This other piece of code should be doing exactly the same:
use strict;
use warnings;
use AnyEvent;
use Mac::FSEvents;
use Data::Dumper;
my $done = AnyEvent->condvar;
my $fs = Mac::FSEvents->new(
latency => 0.1,
file_events => 1,
path => [ '.' ],
);
my $fh = $fs->watch;
my $watcher2 = [AnyEvent->io(
fh => $fh,
poll => 'r',
cb => sub {
warn "event fired";
}
)];
$done->recv;
Change a file in the current directory, and the script terminates with a message "event fired".
I am using MacOS Sequoia (15.1) with Perl 5.40.0 and Mac::FSEvents 0.22, AnyEvent 7.17.
Background: AnyEvent::Filesys::Notify which uses Mac::FSEvents
under the hood no longer works on my system. The test t/30-event.t
fails, showing the same behaviour as my code: No event gets fired. The test also fails for CPAN Testers, see http://matrix.cpantesters.org/?dist=AnyEvent-Filesys-Notify%201.23;os=darwin;perl=5.38.2;reports=1. Although the output looks slightly different there.
Some time ago, I have written a replacement for AnyEvent::Filesys::Notify
https://github.com/gflohr/AnyEvent-Filesys-Watcher. It does the same as AnyEvent::Filesys::Notify
but with fewer dependencies. That module has the same problem with my current setup in the equivalent test t/30-event.t.
Obviously, the tests succeeded at one point. Therefore, I suppose an incompatible change in Mac::FSEvents
, AnyEvent
, perl, or macOS causes the problem.
I only have one Mac, and cannot test with other macOS versions.
I downgraded Mac::FSEvents
to 0.20 with the same result. Earlier versions do not build anymore.
Instead of the file handle returned by the file system watcher, I have used standard input which works.
I have also tried Perl 5.34.0 and 5.30.0. Same result.
The tests for the two AnyEvent
wrappers succeed on a Linux system. The failure seems to be specific to macOS or my specific setup.
The problem in the code is the assignment to $fh
, the return value of $fs->watch
. In the "class" version, the variable goes out of scope, gets undefined, and the call to select()
that AnyEvent->io()
does, fails immediately with EBADF
because the file descriptor in the read set was closed.
The solution is to prevent that by storing a reference to the file handle:
$self->{__fh} = $fs->watch;
my $watchers = [AnyEvent->io(
fh => $self->{__fh},
poll => 'r',
cb => sub {
warn "event fired";
exit 0;
}
)];
The actual problem in https://github.com/gflohr/AnyEvent-Filesys-Watcher gets solved by this fix, see the PR https://github.com/gflohr/AnyEvent-Filesys-Watcher/pull/50.
In the comments to the question, a suggestion was made to move the condvar into the class constructor, see https://pastebin.com/zpjzidy8. This fixes the example code but not the underlying problem in AnyEvent::Filesys::Watcher
. The reason why that seems to work is that the call to $done->recv
now happens before the variable $fh
goes out of scope.
I still cannot say what caused this problem because the code used to work in the past on an older MacBook. I have downgraded Perl to 5.20 and AnyEvent
to version 7.01 from 2012 and the issue was still reproducible. That suggests that the problem is caused by a change in macOS.
See also these test results for AnyEvent::Filesys::Notify
which uses very similar code:
Both tests were done by the same tester, probably on the same machine with different Perl and OS versions. I can reproduce the problem with Perl 5.31.9. That leaves only the OS version as the culprit.