+ my $k = "$path\0$mask";
+ $self->{watch}->{$k} = $st[10]; # 10 - ctime
+ if ($mask & IN_DELETE) {
+ opendir(my $dh, $path) or return;
+ fill_dirlist($self, $path, $dh);
+ }
+ bless [ $self->{watch}, $k ], 'PublicInbox::FakeInotify::Watch';
+}
+
+# also used by KQNotify since it kevent requires readdir on st_nlink
+# count changes.
+sub on_dir_change ($$$$$) {
+ my ($events, $dh, $path, $old_ctime, $dirlist) = @_;
+ my $oldlist = $dirlist->{$path};
+ my $newlist = $oldlist ? {} : undef;
+ while (defined(my $base = readdir($dh))) {
+ next if $base =~ /\A\.\.?\z/;
+ my $full = "$path/$base";
+ my @st = stat($full);
+ if (@st && $st[10] > $old_ctime) {
+ push @$events,
+ bless(\$full, 'PublicInbox::FakeInotify::Event')
+ }
+ if (!@st) {
+ # ignore ENOENT due to race
+ warn "unhandled stat($full) error: $!\n" if !$!{ENOENT};
+ } elsif ($newlist) {
+ $newlist->{$base} = undef;
+ }
+ }
+ return if !$newlist;
+ delete @$oldlist{keys %$newlist};
+ $dirlist->{$path} = $newlist;
+ push(@$events, map {
+ bless \"$path/$_", 'PublicInbox::FakeInotify::GoneEvent'
+ } keys %$oldlist);