Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -14002,7 +14002,11 @@ struct ibf_object_symbol {

#define IBF_ALIGNED_OFFSET(align, offset) /* offset > 0 */ \
((((offset) - 1) / (align) + 1) * (align))
#define IBF_OBJBODY(type, offset) (const type *)\
/* No cast, since it's UB to create an unaligned pointer.
* Leave as void* for use with memcpy in those cases.
* We align the offset, but the buffer pointer is only VALUE aligned,
* so the returned pointer may be unaligned for `type` .*/
#define IBF_OBJBODY(type, offset) \
ibf_load_check_offset(load, IBF_ALIGNED_OFFSET(RUBY_ALIGNOF(type), offset))

static const void *
Expand Down Expand Up @@ -14097,8 +14101,10 @@ ibf_dump_object_float(struct ibf_dump *dump, VALUE obj)
static VALUE
ibf_load_object_float(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
{
const double *dblp = IBF_OBJBODY(double, offset);
return DBL2NUM(*dblp);
double d;
/* Avoid unaligned VFP load on ARMv7; IBF payload may be unaligned (C99 6.3.2.3 p7). */
memcpy(&d, IBF_OBJBODY(double, offset), sizeof(d));
return DBL2NUM(d);
}

static void
Expand Down
1 change: 1 addition & 0 deletions depend
Original file line number Diff line number Diff line change
Expand Up @@ -5797,6 +5797,7 @@ gc.$(OBJEXT): {$(VPATH)}vm_debug.h
gc.$(OBJEXT): {$(VPATH)}vm_opts.h
gc.$(OBJEXT): {$(VPATH)}vm_sync.h
gc.$(OBJEXT): {$(VPATH)}yjit.h
gc.$(OBJEXT): {$(VPATH)}zjit.h
goruby.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
goruby.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
goruby.$(OBJEXT): $(CCAN_DIR)/list/list.h
Expand Down
9 changes: 9 additions & 0 deletions gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
#include "vm_callinfo.h"
#include "ractor_core.h"
#include "yjit.h"
#include "zjit.h"

#include "builtin.h"
#include "shape.h"
Expand Down Expand Up @@ -4106,6 +4107,14 @@ rb_gc_update_vm_references(void *objspace)
rb_yjit_root_update_references();
}
#endif

#if USE_ZJIT
void rb_zjit_root_update_references(void); // in Rust

if (rb_zjit_enabled_p) {
rb_zjit_root_update_references();
}
#endif
}

void
Expand Down
3 changes: 3 additions & 0 deletions iseq.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ rb_iseq_free(const rb_iseq_t *iseq)
RUBY_ASSERT(rb_yjit_live_iseq_count > 0);
rb_yjit_live_iseq_count--;
}
#endif
#if USE_ZJIT
rb_zjit_iseq_free(iseq);
#endif
ruby_xfree((void *)body->iseq_encoded);
ruby_xfree((void *)body->insns_info.body);
Expand Down
33 changes: 29 additions & 4 deletions lib/bundler/cli/exec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ def run
validate_cmd!
SharedHelpers.set_bundle_environment
if bin_path = Bundler.which(cmd)
if !Bundler.settings[:disable_exec_load] && ruby_shebang?(bin_path)
return kernel_load(bin_path, *args)
if !Bundler.settings[:disable_exec_load] && directly_loadable?(bin_path)
bin_path.delete_suffix!(".bat") if Gem.win_platform?
kernel_load(bin_path, *args)
else
bin_path = "./" + bin_path unless File.absolute_path?(bin_path)
kernel_exec(bin_path, *args)
end
bin_path = "./" + bin_path unless File.absolute_path?(bin_path)
kernel_exec(bin_path, *args)
else
# exec using the given command
kernel_exec(cmd, *args)
Expand Down Expand Up @@ -69,6 +71,29 @@ def process_title(file, args)
"#{file} #{args.join(" ")}".strip
end

def directly_loadable?(file)
if Gem.win_platform?
script_wrapper?(file)
else
ruby_shebang?(file)
end
end

def script_wrapper?(file)
script_file = file.delete_suffix(".bat")
return false unless File.exist?(script_file)

if File.zero?(script_file)
Bundler.ui.warn "#{script_file} is empty"
return false
end

header = File.open(file, "r") {|f| f.read(32) }
ruby_exe = "#{RbConfig::CONFIG["RUBY_INSTALL_NAME"]}#{RbConfig::CONFIG["EXEEXT"]}"
ruby_exe = "ruby.exe" if ruby_exe.empty?
header.include?(ruby_exe)
end

def ruby_shebang?(file)
possibilities = [
"#!/usr/bin/env ruby\n",
Expand Down
64 changes: 25 additions & 39 deletions spec/bundler/commands/exec_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -661,8 +661,6 @@

describe "with gems bundled for deployment" do
it "works when calling bundler from another script" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

gemfile <<-G
source "https://gem.repo1"

Expand Down Expand Up @@ -730,13 +728,16 @@ def bin_path(a,b,c)
puts "EXEC: \#{caller.grep(/load/).empty? ? 'exec' : 'load'}"
puts "ARGS: \#{$0} \#{ARGV.join(' ')}"
puts "MYRACK: \#{MYRACK}"
process_title = `ps -o args -p \#{Process.pid}`.split("\n", 2).last.strip
if Gem.win_platform?
process_title = "ruby"
else
process_title = `ps -o args -p \#{Process.pid}`.split("\n", 2).last.strip
end
puts "PROCESS: \#{process_title}"
RUBY

before do
bundled_app(path).open("w") {|f| f << executable }
bundled_app(path).chmod(0o755)
create_file(bundled_app(path), executable)

install_gemfile <<-G
source "https://gem.repo1"
Expand All @@ -748,9 +749,11 @@ def bin_path(a,b,c)
let(:args) { "ARGS: #{path} arg1 arg2" }
let(:myrack) { "MYRACK: 1.0.0" }
let(:process) do
title = "PROCESS: #{path}"
title += " arg1 arg2"
title
if Gem.win_platform?
"PROCESS: ruby"
else
"PROCESS: #{path} arg1 arg2"
end
end
let(:exit_code) { 0 }
let(:expected) { [exec, args, myrack, process].join("\n") }
Expand All @@ -759,8 +762,6 @@ def bin_path(a,b,c)
subject { bundle "exec #{path} arg1 arg2", raise_on_error: false }

it "runs" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
Expand All @@ -772,8 +773,6 @@ def bin_path(a,b,c)

context "with exit 0" do
it "runs" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
Expand All @@ -785,8 +784,6 @@ def bin_path(a,b,c)
let(:exit_code) { 99 }

it "runs" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
Expand Down Expand Up @@ -825,7 +822,12 @@ def bin_path(a,b,c)
let(:expected) { "" }

it "runs" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?
# it's empty, so `create_file` won't add executable permission and bat scripts on Windows
bundled_app(path).chmod(0o755)
path.sub_ext(".bat").write <<~SCRIPT if Gem.win_platform?
@ECHO OFF
@"ruby.exe" "%~dpn0" %*
SCRIPT

subject
expect(exitstatus).to eq(exit_code)
Expand All @@ -838,12 +840,10 @@ def bin_path(a,b,c)
let(:executable) { super() << "\nraise 'ERROR'" }
let(:exit_code) { 1 }
let(:expected_err) do
/\Abundler: failed to load command: #{Regexp.quote(path.to_s)} \(#{Regexp.quote(path.to_s)}\)\n#{Regexp.quote(path.to_s)}:10:in [`']<top \(required\)>': ERROR \(RuntimeError\)/
/\Abundler: failed to load command: #{Regexp.quote(path.to_s)} \(#{Regexp.quote(path.to_s)}\)\n#{Regexp.quote(path.to_s)}:[0-9]+:in [`']<top \(required\)>': ERROR \(RuntimeError\)/
end

it "runs like a normally executed executable" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

subject
expect(exitstatus).to eq(exit_code)
expect(err).to match(expected_err)
Expand All @@ -858,8 +858,6 @@ def bin_path(a,b,c)
let(:expected) { super() }

it "runs" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
Expand All @@ -871,8 +869,6 @@ def bin_path(a,b,c)
let(:shebang) { "#!#{Gem.ruby}" }

it "runs" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
Expand Down Expand Up @@ -903,8 +899,6 @@ def bin_path(a,b,c)
EOS

it "runs" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
Expand All @@ -927,8 +921,6 @@ def bin_path(a,b,c)
let(:expected) { "" }

it "prints proper suggestion" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

subject
expect(exitstatus).to eq(exit_code)
expect(err).to include("Run `bundle install --gemfile CustomGemfile` to install missing gems.")
Expand All @@ -941,8 +933,6 @@ def bin_path(a,b,c)
let(:exit_code) { 1 }

it "runs" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
Expand All @@ -952,15 +942,19 @@ def bin_path(a,b,c)

context "when disable_exec_load is set" do
let(:exec) { "EXEC: exec" }
let(:process) { "PROCESS: ruby #{path} arg1 arg2" }
let(:process) do
if Gem.win_platform?
"PROCESS: ruby"
else
"PROCESS: ruby #{path} arg1 arg2"
end
end

before do
bundle "config set disable_exec_load true"
end

it "runs" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
Expand All @@ -983,8 +977,6 @@ def bin_path(a,b,c)
EOS

it "runs" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
Expand All @@ -1001,8 +993,6 @@ def bin_path(a,b,c)
EOS

it "runs" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
Expand All @@ -1019,8 +1009,6 @@ def bin_path(a,b,c)
EOS

it "runs" do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

subject
expect(exitstatus).to eq(exit_code)
expect(err).to eq(expected_err)
Expand Down Expand Up @@ -1161,8 +1149,6 @@ def require(path)

context "when gemfile and path are configured", :ruby_repo do
before do
skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform?

build_repo2 do
build_gem "rails", "6.1.0" do |s|
s.executables = "rails"
Expand Down
61 changes: 61 additions & 0 deletions test/ruby/test_thread.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1589,4 +1589,65 @@ def frame_for_deadlock_test_2
frame_for_deadlock_test_2 { t.join }
INPUT
end

# [Bug #21342]
def test_unlock_locked_mutex_with_collected_fiber
bug21127 = '[ruby-core:120930] [Bug #21127]'
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
5.times do
m = Mutex.new
Thread.new do
m.synchronize do
end
end.join
Fiber.new do
GC.start
m.lock
end.resume
end
end;
end

def test_unlock_locked_mutex_with_collected_fiber2
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
MUTEXES = []
5.times do
m = Mutex.new
Fiber.new do
GC.start
m.lock
end.resume
MUTEXES << m
end
10.times do
MUTEXES.clear
GC.start
end
end;
end

def test_mutexes_locked_in_fiber_dont_have_aba_issue_with_new_fibers
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
mutexes = 1000.times.map do
Mutex.new
end

mutexes.map do |m|
Fiber.new do
m.lock
end.resume
end

GC.start

1000.times.map do
Fiber.new do
raise "FAILED!" if mutexes.any?(&:owned?)
end.resume
end
end;
end
end
Loading