From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-3.9 required=3.0 tests=ALL_TRUSTED,AWL,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 0900A211B3 for ; Mon, 3 Dec 2018 07:42:56 +0000 (UTC) From: Eric Wong To: spew@80x24.org Subject: [PATCH] test_process.rb (test_execopt_env_path): fix ETXTBUSY from MJIT Date: Mon, 3 Dec 2018 07:42:56 +0000 Message-Id: <20181203074256.14307-1-e@80x24.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: Since MJIT uses vfork+execve in a separate thread, there can be small window in-between vfork and execve where tmp_script.cmd is held open by the vforked child. vfork only pauses the MJIT thread, not any Ruby Threads, so our call to Process.spawn will hit ETXTBUSY in that window unless we fork. main thread | MJIT thread ---------------------------------------------------- fd = open(tmp) | | | vfork for CC | CC running write | | --------------- fchmod | | sees "fd" here close(fd) | | Process.spawn called | | vfork (spawn)| (new process) | | | execve => TXTBUSY | | | | | execve (FD_CLOEXEC on fd) | | vfork returns | --- test/ruby/test_process.rb | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/test/ruby/test_process.rb b/test/ruby/test_process.rb index 4c212c13f2..d94b0a791b 100644 --- a/test/ruby/test_process.rb +++ b/test/ruby/test_process.rb @@ -343,14 +343,26 @@ def test_execopts_env end def test_execopt_env_path - # http://ci.rvm.jp/results/trunk-mjit@silicon-docker/1455223 - # http://ci.rvm.jp/results/trunk-mjit@silicon-docker/1450027 - # http://ci.rvm.jp/results/trunk-mjit@silicon-docker/1469867 - skip 'this randomly fails with MJIT' if RubyVM::MJIT.enabled? + mkexe = lambda do |d| + open("#{d}/tmp_script.cmd", "w") {|f| f.puts ": ;"; f.chmod(0755)} + end + + # Since MJIT uses vfork+execve in a separate thread, there can be small + # window in-between vfork and execve where tmp_script.cmd is held open + # by the vforked child. vfork only pauses the MJIT thread, not any Ruby + # Threads, so our call to Process.spawn will hit ETXTBUSY in that + # window unless we fork + if RubyVM::MJIT.enabled? && Process.respond_to?(:fork) + mkexe_real = mkexe + mkexe = lambda do |d| + _, st = Process.waitpid2(fork { mkexe_real.call(d); exit!(0) }) + assert_predicate st, :success? + end + end bug8004 = '[ruby-core:53103] [Bug #8004]' Dir.mktmpdir do |d| - open("#{d}/tmp_script.cmd", "w") {|f| f.puts ": ;"; f.chmod(0755)} + mkexe.call(d) assert_not_nil(pid = Process.spawn({"PATH" => d}, "tmp_script.cmd"), bug8004) wpid, st = Process.waitpid2(pid) assert_equal([pid, true], [wpid, st.success?], bug8004) -- EW