From 87274e354620ef4a10b72ab3af1b038039f09c96 Mon Sep 17 00:00:00 2001 From: Shizuo Fujita Date: Sun, 28 Jun 2026 13:06:11 +0900 Subject: [PATCH] Fix flaky async_watcher_spec on macOS Ruby 3.2+ IO.pipe on macOS Ruby 3.2+ returns pipes with O_NONBLOCK set by default. sysread(1) calls read() directly without EAGAIN retry, so the handshake raises Errno::EAGAIN when the child has not yet written before the parent reads. Replacing sysread with read lets Ruby's I/O layer handle EAGAIN transparently and wait for data. Reproduced without cool.io (25% EAGAIN rate in 200 attempts). Linux is unaffected because IO.pipe there does not set O_NONBLOCK. Co-Authored-By: Claude Sonnet 4.6 --- spec/async_watcher_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/async_watcher_spec.rb b/spec/async_watcher_spec.rb index 486d4da..2750a62 100644 --- a/spec/async_watcher_spec.rb +++ b/spec/async_watcher_spec.rb @@ -30,7 +30,9 @@ end # ensure children are ready - nr_fork.times { expect(rd.sysread(1)).to eq('.') } + # rd may be O_NONBLOCK on macOS Ruby 3.2+ (IO.pipe sets O_NONBLOCK); use read + # instead of sysread so EAGAIN on an empty pipe is retried transparently. + nr_fork.times { expect(rd.read(1)).to eq('.') } # send our signals nr_signal.times { aw.signal }