diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 41cad1dec41..a11e1327d4f 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -1739,6 +1739,16 @@ class TestIO < Test::Unit::TestCase end end if /cygwin/ !~ RUBY_PLATFORM + def test_read_interrupted_by_wakeup + with_pipe do |r, w| + t = Thread.new { r.read } + Thread.pass until t.stop? + 100_000.times { t.wakeup; w.write("x") } + w.close + assert_equal(100_000, t.value.size) + end + end + def test_read pipe(proc do |w| w.write "foobarbaz" diff --git a/win32/win32.c b/win32/win32.c index 442b3824169..6851e94612c 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -7464,13 +7464,17 @@ rb_w32_read_internal(int fd, void *buf, size_t size, rb_off_t *offset) errno = EINTR; else errno = map_errno(GetLastError()); - CloseHandle(ol.hEvent); CancelIo((HANDLE)_osfhnd(fd)); - rb_acrt_lowio_unlock_fh(fd); - return -1; + /* the read may have completed before the cancel, and the kernel + * writes ol and buf until it ends either way */ + GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE); + if (!read) { + CloseHandle(ol.hEvent); + rb_acrt_lowio_unlock_fh(fd); + return -1; + } } - - if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) && + else if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) && (err = GetLastError()) != ERROR_HANDLE_EOF) { int ret = 0; if (err != ERROR_BROKEN_PIPE) { @@ -7577,13 +7581,15 @@ rb_w32_write_internal(int fd, const void *buf, size_t size, rb_off_t *offset) errno = EINTR; else errno = map_errno(GetLastError()); - CloseHandle(ol.hEvent); CancelIo((HANDLE)_osfhnd(fd)); - rb_acrt_lowio_unlock_fh(fd); - return -1; + GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written, TRUE); + if (!written) { + CloseHandle(ol.hEvent); + rb_acrt_lowio_unlock_fh(fd); + return -1; + } } - - if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written, TRUE)) { + else if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written, TRUE)) { errno = map_errno(GetLastError()); CloseHandle(ol.hEvent); CancelIo((HANDLE)_osfhnd(fd));