+ bless { kq => IO::KQueue->new, owner_pid => $$ }, $class;
+}
+
+# returns a new instance which behaves like signalfd on Linux.
+# It's wasteful in that it uses another FD, but it simplifies
+# our epoll-oriented code.
+sub signalfd {
+ my ($class, $signo, $flags) = @_;
+ my $sym = gensym;
+ tie *$sym, $class, $signo, $flags; # calls TIEHANDLE
+ $sym
+}
+
+sub TIEHANDLE { # similar to signalfd()
+ my ($class, $signo, $flags) = @_;
+ my $self = $class->new;
+ $self->{timeout} = ($flags & $SFD_NONBLOCK) ? 0 : -1;
+ my $kq = $self->{kq};
+ $kq->EV_SET($_, EVFILT_SIGNAL, EV_ADD) for @$signo;
+ $self;
+}
+
+sub READ { # called by sysread() for signalfd compatibility
+ my ($self, undef, $len, $off) = @_; # $_[1] = buf
+ die "bad args for signalfd read" if ($len % 128) // defined($off);
+ my $timeout = $self->{timeout};
+ my $sigbuf = $self->{sigbuf} //= [];
+ my $nr = $len / 128;
+ my $r = 0;
+ $_[1] = '';
+ do {
+ while ($nr--) {
+ my $signo = shift(@$sigbuf) or last;
+ # caller only cares about signalfd_siginfo.ssi_signo:
+ $_[1] .= pack('L', $signo) . ("\0" x 124);
+ $r += 128;
+ }
+ return $r if $r;
+ my @events = eval { $self->{kq}->kevent($timeout) };
+ # workaround https://rt.cpan.org/Ticket/Display.html?id=116615
+ if ($@) {
+ next if $@ =~ /Interrupted system call/;
+ die;
+ }
+ if (!scalar(@events) && $timeout == 0) {
+ $! = EAGAIN;
+ return;
+ }
+
+ # Grab the kevent.ident (signal number). The kevent.data
+ # field shows coalesced signals, and maybe we'll use it
+ # in the future...
+ @$sigbuf = map { $_->[0] } @events;
+ } while (1);