From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS6939 65.19.128.0/18 X-Spam-Status: No, score=-0.3 required=3.0 tests=BAYES_00,RCVD_IN_MSPIKE_BL, RCVD_IN_MSPIKE_ZBI,RCVD_IN_SORBS_WEB,RCVD_IN_XBL,RDNS_NONE,SPF_FAIL, SPF_HELO_FAIL,TO_EQ_FM_DOM_SPF_FAIL shortcircuit=no autolearn=no autolearn_force=no version=3.4.0 Received: from 80x24.org (unknown [65.19.167.132]) by dcvr.yhbt.net (Postfix) with ESMTP id 3DC7A20D0C for ; Tue, 6 Jun 2017 00:16:47 +0000 (UTC) From: Eric Wong To: spew@80x24.org Subject: [PATCH] IO#close: do not enqueue redundant interrupts [backport 2.4] Date: Tue, 6 Jun 2017 00:16:46 +0000 Message-Id: <20170606001646.22889-1-e@80x24.org> List-Id: Enqueuing multiple errors for one event causes spurious errors down the line, as reported by Nikolay Vashchenko in https://bugs.ruby-lang.org/issues/13632 * thread.c (rb_notify_fd_close): do not enqueue multiple interrupts [ruby-core:81581] [Bug #13632] * test/ruby/test_io.rb (test_single_exception_on_close): new test based on script from Nikolay --- test/ruby/test_io.rb | 22 ++++++++++++++++++++++ thread.c | 2 ++ 2 files changed, 24 insertions(+) diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb index 61dfba3180..034dac570c 100644 --- a/test/ruby/test_io.rb +++ b/test/ruby/test_io.rb @@ -2809,6 +2809,28 @@ def test_cross_thread_close_stdio end; end + def test_single_exception_on_close + a = [] + t = [] + 10.times do + r, w = IO.pipe + a << [r, w] + t << Thread.new do + while r.gets + end rescue IOError + Thread.current.pending_interrupt? + end + end + a.each do |r, w| + w.write -"\n" + w.close + r.close + end + t.each do |th| + assert_equal false, th.value, '[ruby-core:81581] [Bug #13632]' + end + end + def test_open_mode feature4742 = "[ruby-core:36338]" bug6055 = '[ruby-dev:45268]' diff --git a/thread.c b/thread.c index 7391cf96d2..38c20fe30a 100644 --- a/thread.c +++ b/thread.c @@ -2202,6 +2202,8 @@ rb_notify_fd_close(int fd) list_for_each(&vm->living_threads, th, vmlt_node) { if (th->waiting_fd == fd) { VALUE err = th->vm->special_exceptions[ruby_error_closed_stream]; + + th->waiting_fd = -1; /* ensure we only enqueue once */ rb_threadptr_pending_interrupt_enque(th, err); rb_threadptr_interrupt(th); busy = 1; -- EW