* [PATCH] pure Perl inotify support for x86-32
@ 2023-12-27 13:39 Eric Wong
0 siblings, 0 replies; only message in thread
From: Eric Wong @ 2023-12-27 13:39 UTC (permalink / raw)
To: spew
This is a step towards improving the out-of-the-box experience
in achieving notifications without XS, extra downloads, and .so
loading and indirections.
---
MANIFEST | 4 ++
devel/sysdefs-list | 28 +++++++++
lib/PublicInbox/DirIdle.pm | 16 ++---
lib/PublicInbox/In3Event.pm | 24 +++++++
lib/PublicInbox/In3Watch.pm | 20 ++++++
lib/PublicInbox/InboxIdle.pm | 6 +-
lib/PublicInbox/Inotify.pm | 26 ++++++--
lib/PublicInbox/Inotify3.pm | 115 ++++++++++++++++++++++++++++++++++
lib/PublicInbox/Syscall.pm | 13 ++++
lib/PublicInbox/TailNotify.pm | 6 +-
t/imapd.t | 2 +-
t/inotify3.t | 17 +++++
t/lei-auto-watch.t | 4 +-
t/lei-watch.t | 4 +-
t/nntpd.t | 2 +-
t/watch_maildir.t | 2 +-
16 files changed, 264 insertions(+), 25 deletions(-)
create mode 100644 lib/PublicInbox/In3Event.pm
create mode 100644 lib/PublicInbox/In3Watch.pm
create mode 100644 lib/PublicInbox/Inotify3.pm
create mode 100644 t/inotify3.t
diff --git a/MANIFEST b/MANIFEST
index e22674b7..109ce88a 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -223,10 +223,13 @@ lib/PublicInbox/IPC.pm
lib/PublicInbox/IdxStack.pm
lib/PublicInbox/Import.pm
lib/PublicInbox/In2Tie.pm
+lib/PublicInbox/In3Event.pm
+lib/PublicInbox/In3Watch.pm
lib/PublicInbox/Inbox.pm
lib/PublicInbox/InboxIdle.pm
lib/PublicInbox/InboxWritable.pm
lib/PublicInbox/Inotify.pm
+lib/PublicInbox/Inotify3.pm
lib/PublicInbox/InputPipe.pm
lib/PublicInbox/Isearch.pm
lib/PublicInbox/KQNotify.pm
@@ -493,6 +496,7 @@ t/index-git-times.t
t/indexlevels-mirror-v1.t
t/indexlevels-mirror.t
t/init.t
+t/inotify3.t
t/io.t
t/ipc.t
t/iso-2202-jp.eml
diff --git a/devel/sysdefs-list b/devel/sysdefs-list
index d0166461..61532cf2 100755
--- a/devel/sysdefs-list
+++ b/devel/sysdefs-list
@@ -88,9 +88,37 @@ int main(void)
MAYBE D(SYS_epoll_wait);
D(SYS_epoll_pwait);
D(SYS_signalfd4);
+
+ X(IN_CLOEXEC);
+ X(IN_ACCESS);
+ X(IN_ALL_EVENTS);
+ X(IN_ATTRIB);
+ X(IN_CLOSE);
+ X(IN_CLOSE_NOWRITE);
+ X(IN_CLOSE_WRITE);
+ X(IN_CREATE);
+ X(IN_DELETE);
+ X(IN_DELETE_SELF);
+ X(IN_DONT_FOLLOW);
+ X(IN_EXCL_UNLINK);
+ X(IN_IGNORED);
+ X(IN_ISDIR);
+ X(IN_MASK_ADD);
+ X(IN_MODIFY);
+ X(IN_MOVE);
+ X(IN_MOVED_FROM);
+ X(IN_MOVED_TO);
+ X(IN_MOVE_SELF);
+ X(IN_ONESHOT);
+ X(IN_ONLYDIR);
+ X(IN_OPEN);
+ X(IN_Q_OVERFLOW);
+ X(IN_UNMOUNT);
+
D(SYS_inotify_init1);
D(SYS_inotify_add_watch);
D(SYS_inotify_rm_watch);
+
D(SYS_prctl);
D(SYS_fstatfs);
diff --git a/lib/PublicInbox/DirIdle.pm b/lib/PublicInbox/DirIdle.pm
index e6a326ab..230df166 100644
--- a/lib/PublicInbox/DirIdle.pm
+++ b/lib/PublicInbox/DirIdle.pm
@@ -10,12 +10,12 @@ use PublicInbox::In2Tie;
my ($MAIL_IN, $MAIL_GONE, $ino_cls);
if ($^O eq 'linux' && eval { require PublicInbox::Inotify; 1 }) {
- $MAIL_IN = Linux::Inotify2::IN_MOVED_TO() |
- Linux::Inotify2::IN_CREATE();
- $MAIL_GONE = Linux::Inotify2::IN_DELETE() |
- Linux::Inotify2::IN_DELETE_SELF() |
- Linux::Inotify2::IN_MOVE_SELF() |
- Linux::Inotify2::IN_MOVED_FROM();
+ $MAIL_IN = PublicInbox::Inotify::IN_MOVED_TO() |
+ PublicInbox::Inotify::IN_CREATE();
+ $MAIL_GONE = PublicInbox::Inotify::IN_DELETE() |
+ PublicInbox::Inotify::IN_DELETE_SELF() |
+ PublicInbox::Inotify::IN_MOVE_SELF() |
+ PublicInbox::Inotify::IN_MOVED_FROM();
$ino_cls = 'PublicInbox::Inotify';
# Perl 5.22+ is needed for fileno(DIRHANDLE) support:
} elsif ($^V ge v5.22 && eval { require PublicInbox::KQNotify }) {
@@ -79,7 +79,7 @@ sub event_step {
my $cb = $self->{cb} or return;
local $PublicInbox::DS::in_loop = 0; # waitpid() synchronously (FIXME)
eval {
- my @events = $self->{inot}->read; # Linux::Inotify2->read
+ my @events = $self->{inot}->read; # Inotify3->read
$cb->($_) for @events;
};
warn "$self->{inot}->read err: $@\n" if $@;
@@ -88,7 +88,7 @@ sub event_step {
sub force_close {
my ($self) = @_;
my $inot = delete $self->{inot} // return;
- if ($inot->can('fh')) { # Linux::Inotify2 2.3+
+ if ($inot->can('fh')) { # Inotify3 or Linux::Inotify2 2.3+
$inot->fh->close or warn "CLOSE ERROR: $!";
} elsif ($inot->isa('Linux::Inotify2')) {
require PublicInbox::LI2Wrap;
diff --git a/lib/PublicInbox/In3Event.pm b/lib/PublicInbox/In3Event.pm
new file mode 100644
index 00000000..f93dc0da
--- /dev/null
+++ b/lib/PublicInbox/In3Event.pm
@@ -0,0 +1,24 @@
+# Copyright (C) all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+
+# duck-type compatible with Linux::Inotify2::Event for pure Perl
+# PublicInbox::Inotify3 w/o callback support
+package PublicInbox::In3Event;
+use v5.12;
+
+sub w { $_[0]->[2] } # PublicInbox::In3Watch
+sub mask { $_[0]->[0] }
+sub name { $_[0]->[1] }
+
+sub fullname {
+ my ($name, $wname) = ($_[0]->[1], $_[0]->[2]->name);
+ length($name) ? "$wname/$name" : $wname;
+}
+
+my $buf = '';
+while (my ($sym, $mask) = each %PublicInbox::Inotify3::events) {
+ $buf .= "sub $sym { \$_[0]->[0] & $mask }\n";
+}
+eval $buf;
+
+1;
diff --git a/lib/PublicInbox/In3Watch.pm b/lib/PublicInbox/In3Watch.pm
new file mode 100644
index 00000000..bdb91869
--- /dev/null
+++ b/lib/PublicInbox/In3Watch.pm
@@ -0,0 +1,20 @@
+# Copyright (C) all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+
+# duck-type compatible with Linux::Inotify2::Watch for pure Perl
+# PublicInbox::Inotify3 for our needs, only
+package PublicInbox::In3Watch;
+use v5.12;
+
+sub mask { $_[0]->[1] }
+sub name { $_[0]->[2] }
+
+sub cancel {
+ my ($self) = @_;
+ my ($wd, $in3) = @$self[0, 3];
+ $in3 or return 1; # already canceled
+ pop @$self;
+ $in3->rm_watch($wd);
+}
+
+1;
diff --git a/lib/PublicInbox/InboxIdle.pm b/lib/PublicInbox/InboxIdle.pm
index 4231c0a0..3c4d4a68 100644
--- a/lib/PublicInbox/InboxIdle.pm
+++ b/lib/PublicInbox/InboxIdle.pm
@@ -11,7 +11,7 @@ use PublicInbox::Syscall qw(EPOLLIN);
my $IN_MODIFY = 0x02; # match Linux inotify
my $ino_cls;
if ($^O eq 'linux' && eval { require PublicInbox::Inotify }) {
- $IN_MODIFY = Linux::Inotify2::IN_MODIFY();
+ $IN_MODIFY = PublicInbox::Inotify::IN_MODIFY();
$ino_cls = 'PublicInbox::Inotify';
} elsif (eval { require PublicInbox::KQNotify }) {
$IN_MODIFY = PublicInbox::KQNotify::NOTE_WRITE();
@@ -34,7 +34,7 @@ sub in2_arm ($$) { # PublicInbox::Config::each_inbox callback
$ibx->{unlock_subs} = $old_ibx->{unlock_subs};
%{$ibx->{unlock_subs}} = (%$u, %{$ibx->{unlock_subs}}) if $u;
- # Linux::Inotify2::Watch::name matches if watches are the
+ # *::Inotify*::Watch::name matches if watches are the
# same, no point in replacing a watch of the same name
if ($cur->[1]->name eq $lock) {
$self->{on_unlock}->{$lock} = $ibx;
@@ -87,7 +87,7 @@ sub new {
sub event_step {
my ($self) = @_;
eval {
- my @events = $self->{inot}->read; # Linux::Inotify2::read
+ my @events = $self->{inot}->read; # PublicInbox::Inotify3::read
my $on_unlock = $self->{on_unlock};
for my $ev (@events) {
my $fn = $ev->fullname // next; # cancelled
diff --git a/lib/PublicInbox/Inotify.pm b/lib/PublicInbox/Inotify.pm
index 3ef271c8..4ac54dee 100644
--- a/lib/PublicInbox/Inotify.pm
+++ b/lib/PublicInbox/Inotify.pm
@@ -6,11 +6,29 @@ package PublicInbox::Inotify;
use v5.12;
our @ISA;
BEGIN {
- eval { require Linux::Inotify2 };
- if ($@) { # TODO: get rid of XS dependency
- die "W: Linux::Inotify2 missing: $@\n";
+ # prefer pure Perl
+ my $isa;
+ for my $m (qw(PublicInbox::Inotify3 Linux::Inotify2)) {
+ eval "require $m";
+ next if $@;
+ $isa = $m;
+ }
+ if ($isa) {
+ push @ISA, $isa;
+ my $buf = '';
+ for (qw(IN_MOVED_TO IN_CREATE IN_DELETE IN_DELETE_SELF
+ IN_MOVE_SELF IN_MOVED_FROM IN_MODIFY)) {
+ $buf .= "*$_ = \\&PublicInbox::Inotify3::$_;\n";
+ }
+ eval $buf;
+ die $@ if $@;
} else {
- push @ISA, 'Linux::Inotify2';
+ die <<EOM;
+W: inotify syscall numbers unknown on your platform and
+W: Linux::Inotify2 missing: $@
+W: public-inbox hackers welcome the plain-text output of ./devel/sysdefs-list
+W: at meta\@public-inbox.org
+EOM
}
};
diff --git a/lib/PublicInbox/Inotify3.pm b/lib/PublicInbox/Inotify3.pm
new file mode 100644
index 00000000..4f337a7a
--- /dev/null
+++ b/lib/PublicInbox/Inotify3.pm
@@ -0,0 +1,115 @@
+# Copyright (C) all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+
+# Implements most Linux::Inotify2 functionality we need in pure Perl
+# Anonymous sub support isn't supported since it's expensive in the
+# best case and likely leaky in older Perls (e.g. 5.16.3)
+package PublicInbox::Inotify3;
+use v5.12;
+use autodie qw(open);
+use PublicInbox::Syscall ();
+use Carp;
+use Scalar::Util ();
+
+# this fails if undefined no unsupported platforms
+use constant $PublicInbox::Syscall::INOTIFY;
+our %events;
+
+# extracted from devel/sysdefs-list output, these should be arch-independent
+BEGIN {
+%events = (
+ IN_ACCESS => 0x1,
+ IN_ALL_EVENTS => 0xfff,
+ IN_ATTRIB => 0x4,
+ IN_CLOSE => 0x18,
+ IN_CLOSE_NOWRITE => 0x10,
+ IN_CLOSE_WRITE => 0x8,
+ IN_CREATE => 0x100,
+ IN_DELETE => 0x200,
+ IN_DELETE_SELF => 0x400,
+ IN_DONT_FOLLOW => 0x2000000,
+ IN_EXCL_UNLINK => 0x4000000,
+ IN_IGNORED => 0x8000,
+ IN_ISDIR => 0x40000000,
+ IN_MASK_ADD => 0x20000000,
+ IN_MODIFY => 0x2,
+ IN_MOVE => 0xc0,
+ IN_MOVED_FROM => 0x40,
+ IN_MOVED_TO => 0x80,
+ IN_MOVE_SELF => 0x800,
+ IN_ONESHOT => 0x80000000,
+ IN_ONLYDIR => 0x1000000,
+ IN_OPEN => 0x20,
+ IN_Q_OVERFLOW => 0x4000,
+ IN_UNMOUNT => 0x2000,
+);
+} # /BEGIN
+use constant \%events;
+require PublicInbox::In3Event; # uses %events
+require PublicInbox::In3Watch; # uses SYS_inotify_rm_watch
+
+use constant autocancel =>
+ (IN_IGNORED|IN_UNMOUNT|IN_ONESHOT|IN_DELETE_SELF);
+
+sub new {
+ open my $fh, '+<&=', syscall(SYS_inotify_init1, IN_CLOEXEC);
+ bless { fh => $fh }, __PACKAGE__;
+}
+
+sub read {
+ my ($self) = @_;
+ my (@ret, $wd, $mask, $len, $name, $size, $buf);
+ my $r = sysread($self->{fh}, my $rbuf, 8192);
+ if ($r) {
+ while ($r) {
+ ($wd, $mask, undef, $len) = unpack('lLLL', $rbuf);
+ $size = 16 + $len; # 16: sizeof(struct inotify_event)
+ substr($rbuf, 0, 16, '');
+ $name = $len ? unpack('Z*', substr($rbuf, 0, $len, ''))
+ : undef;
+ $r -= $size;
+ next if $self->{ignore}->{$wd};
+ my $ev = bless [$mask, $name], 'PublicInbox::In3Event';
+ push @ret, $ev;
+ if (my $w = $self->{w}->{$wd}) {
+ $ev->[2] = $w;
+ $w->cancel if $ev->mask & autocancel;
+ } elsif ($mask & IN_Q_OVERFLOW) {
+ carp 'E: IN_Q_OVERFLOW, too busy? (non-fatal)'
+ } else {
+ carp "BUG? wd:$wd unknown (non-fatal)";
+ }
+ }
+ } elsif (defined($r) || ($!{EAGAIN} || $!{EINTR})) {
+ } else {
+ croak "inotify read: $!";
+ }
+ delete $self->{ignore};
+ @ret;
+}
+
+sub fileno { CORE::fileno($_[0]->{fh}) }
+
+sub fh { $_[0]->{fh} }
+
+sub blocking { shift->{fh}->blocking(@_) }
+
+sub watch {
+ my ($self, $name, $mask, $cb) = @_;
+ croak "E: $cb not supported" if $cb; # too much memory
+ my $wd = syscall(SYS_inotify_add_watch, $self->fileno, $name, $mask);
+ return if $wd < 0;
+ my $w = bless [ $wd, $mask, $name, $self ], 'PublicInbox::In3Watch';
+ $self->{w}->{$wd} = $w;
+ Scalar::Util::weaken($w->[3]); # ugh
+ $w;
+}
+
+sub rm_watch {
+ my ($self, $wd) = @_;
+ delete $self->{w}->{$wd};
+ $self->{ignore}->{$wd} = 1; # is this needed?
+ syscall(SYS_inotify_rm_watch, $self->fileno, $wd) < 0 ? undef : 1;
+}
+
+1;
diff --git a/lib/PublicInbox/Syscall.pm b/lib/PublicInbox/Syscall.pm
index 78181bb6..bc8792dd 100644
--- a/lib/PublicInbox/Syscall.pm
+++ b/lib/PublicInbox/Syscall.pm
@@ -22,6 +22,7 @@ use POSIX qw(ENOENT ENOSYS EINVAL O_NONBLOCK);
use Socket qw(SOL_SOCKET SCM_RIGHTS);
use Config;
our %SIGNUM = (WINCH => 28); # most Linux, {Free,Net,Open}BSD, *Darwin
+our $INOTIFY;
# $VERSION = '0.25'; # Sys::Syscall version
our @EXPORT_OK = qw(epoll_ctl epoll_create epoll_wait
@@ -60,6 +61,9 @@ our ($SYS_epoll_create,
$SYS_epoll_wait,
$SYS_signalfd4,
$SYS_renameat2,
+ $SYS_inotify_init1,
+ $SYS_inotify_add_watch,
+ $SYS_inotify_rm_watch,
$F_SETPIPE_SZ);
my ($SYS_sendmsg, $SYS_recvmsg);
@@ -98,6 +102,11 @@ if ($^O eq "linux") {
$SYS_fstatfs = 100;
$SYS_sendmsg = 370;
$SYS_recvmsg = 372;
+ $INOTIFY = { # usage: `use constant $PublicInbox::Syscall::INOTIFY'
+ SYS_inotify_init1 => 332,
+ SYS_inotify_add_watch => 292,
+ SYS_inotify_rm_watch => 293,
+ };
$FS_IOC_GETFLAGS = 0x80046601;
$FS_IOC_SETFLAGS = 0x40046602;
} elsif ($machine eq "x86_64") {
@@ -251,6 +260,10 @@ EOM
*epoll_ctl = \&epoll_ctl_mod4;
}
}
+
+# SFD_CLOEXEC is arch-dependent, so IN_CLOEXEC may be, too
+$INOTIFY->{IN_CLOEXEC} //= 0x80000 if $INOTIFY;
+
# use Inline::C for *BSD-only or general POSIX stuff.
# Linux guarantees stable syscall numbering, BSDs only offer a stable libc
# use devel/sysdefs-list on Linux to detect new syscall numbers and
diff --git a/lib/PublicInbox/TailNotify.pm b/lib/PublicInbox/TailNotify.pm
index bdb92d54..84340a35 100644
--- a/lib/PublicInbox/TailNotify.pm
+++ b/lib/PublicInbox/TailNotify.pm
@@ -9,9 +9,9 @@ use PublicInbox::DS qw(now);
my ($TAIL_MOD, $ino_cls);
if ($^O eq 'linux' && eval { require PublicInbox::Inotify; 1 }) {
- $TAIL_MOD = Linux::Inotify2::IN_MOVED_TO() |
- Linux::Inotify2::IN_CREATE() |
- Linux::Inotify2::IN_MODIFY();
+ $TAIL_MOD = PublicInbox::Inotify::IN_MOVED_TO() |
+ PublicInbox::Inotify::IN_CREATE() |
+ PublicInbox::Inotify::IN_MODIFY();
$ino_cls = 'PublicInbox::Inotify';
} elsif (eval { require PublicInbox::KQNotify }) {
$TAIL_MOD = PublicInbox::KQNotify::MOVED_TO_OR_CREATE() |
diff --git a/t/imapd.t b/t/imapd.t
index 9606291e..549b8766 100644
--- a/t/imapd.t
+++ b/t/imapd.t
@@ -250,7 +250,7 @@ SKIP: {
ok($mic->logout, 'logout works');
-my $have_inotify = eval { require Linux::Inotify2; 1 };
+my $have_inotify = eval { require PublicInbox::Inotify; 1 };
for my $ibx (@ibx) {
my $name = $ibx->{name};
diff --git a/t/inotify3.t b/t/inotify3.t
new file mode 100644
index 00000000..c25c0f42
--- /dev/null
+++ b/t/inotify3.t
@@ -0,0 +1,17 @@
+#!perl -w
+# Copyright (C) all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+use v5.12; use PublicInbox::TestCommon;
+plan skip_all => 'inotify is Linux-only' if $^O ne 'linux';
+use_ok 'PublicInbox::Inotify3';
+my $in = PublicInbox::Inotify3->new;
+my $tmpdir = tmpdir;
+my $w = $in->watch("$tmpdir", PublicInbox::Inotify3::IN_ALL_EVENTS());
+$in->blocking(0);
+is_xdeeply [ $in->read ], [], 'non-blocking has no events, yet';
+undef $tmpdir;
+my @list = $in->read;
+ok scalar(@list), 'got events';
+ok $w->cancel, 'watch canceled';
+
+done_testing;
diff --git a/t/lei-auto-watch.t b/t/lei-auto-watch.t
index f871188d..1e190316 100644
--- a/t/lei-auto-watch.t
+++ b/t/lei-auto-watch.t
@@ -4,10 +4,10 @@
use strict; use v5.10.1; use PublicInbox::TestCommon;
use File::Basename qw(basename);
plan skip_all => "TEST_FLAKY not enabled for $0" if !$ENV{TEST_FLAKY};
-my $have_fast_inotify = eval { require Linux::Inotify2 } ||
+my $have_fast_inotify = eval { require PublicInbox::Inotify } ||
eval { require IO::KQueue };
$have_fast_inotify or
- diag("$0 IO::KQueue or Linux::Inotify2 missing, test will be slow");
+ diag("$0 IO::KQueue or inotify missing, test will be slow");
test_lei(sub {
my ($ro_home, $cfg_path) = setup_public_inboxes;
diff --git a/t/lei-watch.t b/t/lei-watch.t
index 24d9f5c8..7b357ee0 100644
--- a/t/lei-watch.t
+++ b/t/lei-watch.t
@@ -5,11 +5,11 @@ use strict; use v5.10.1; use PublicInbox::TestCommon;
use File::Path qw(make_path remove_tree);
plan skip_all => "TEST_FLAKY not enabled for $0" if !$ENV{TEST_FLAKY};
require_mods('lei');
-my $have_fast_inotify = eval { require Linux::Inotify2 } ||
+my $have_fast_inotify = eval { require PublicInbox::Inotify } ||
eval { require IO::KQueue };
$have_fast_inotify or
- diag("$0 IO::KQueue or Linux::Inotify2 missing, test will be slow");
+ diag("$0 IO::KQueue or inotify missing, test will be slow");
my ($ro_home, $cfg_path) = setup_public_inboxes;
test_lei(sub {
diff --git a/t/nntpd.t b/t/nntpd.t
index 0f3ef596..7052cb6a 100644
--- a/t/nntpd.t
+++ b/t/nntpd.t
@@ -14,7 +14,7 @@ use PublicInbox::DS;
my $version = $ENV{PI_TEST_VERSION} || 1;
require_git('2.6') if $version == 2;
use_ok 'PublicInbox::Msgmap';
-my $fast_idle = eval { require Linux::Inotify2; 1 } //
+my $fast_idle = eval { require PublicInbox::Inotify; 1 } //
eval { require IO::KQueue; 1 };
my ($tmpdir, $for_destroy) = tmpdir();
diff --git a/t/watch_maildir.t b/t/watch_maildir.t
index 07ebeef6..d7f01b1a 100644
--- a/t/watch_maildir.t
+++ b/t/watch_maildir.t
@@ -182,7 +182,7 @@ EOM
# wait for -watch to setup inotify watches
my $sleep = 1;
- if (eval { require Linux::Inotify2 } && -d "/proc/$wm->{pid}/fd") {
+ if (eval { require PublicInbox::Inotify } && -d "/proc/$wm->{pid}/fd") {
my $end = time + 2;
my (@ino, @ino_info);
do {
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2023-12-27 13:39 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-27 13:39 [PATCH] pure Perl inotify support for x86-32 Eric Wong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).