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
19 changes: 19 additions & 0 deletions bootstraptest/test_yjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5416,3 +5416,22 @@ def foo

10.times.map { foo.dup }.last
}

# regression test for [Bug #21772]
# local variable type tracking desync
assert_normal_exit %q{
def some_method = 0

def test_body(key)
some_method
key = key.to_s # setting of local relevant

key == "symbol"
end

def jit_caller = test_body("session_id")

jit_caller # first iteration, non-escaped environment
alias some_method binding # induce environment escape
test_body(:symbol)
}
43 changes: 18 additions & 25 deletions encoding.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ int rb_encdb_alias(const char *alias, const char *orig);
#pragma GCC visibility pop
#endif

static ID id_encoding;
static ID id_encoding, id_i_name;
VALUE rb_cEncoding;

#define ENCODING_LIST_CAPA 256
Expand Down Expand Up @@ -97,6 +97,8 @@ static rb_encoding *global_enc_ascii,
*global_enc_utf_8,
*global_enc_us_ascii;

static int filesystem_encindex = ENCINDEX_ASCII_8BIT;

#define GLOBAL_ENC_TABLE_LOCKING(tbl) \
for (struct enc_table *tbl = &global_enc_table, **locking = &tbl; \
locking; \
Expand Down Expand Up @@ -136,6 +138,7 @@ static VALUE
enc_new(rb_encoding *encoding)
{
VALUE enc = TypedData_Wrap_Struct(rb_cEncoding, &encoding_data_type, (void *)encoding);
rb_ivar_set(enc, id_i_name, rb_fstring_cstr(encoding->name));
RB_OBJ_SET_FROZEN_SHAREABLE(enc);
return enc;
}
Expand Down Expand Up @@ -1356,20 +1359,6 @@ enc_inspect(VALUE self)
rb_enc_autoload_p(enc) ? " (autoload)" : "");
}

/*
* call-seq:
* enc.name -> string
* enc.to_s -> string
*
* Returns the name of the encoding.
*
* Encoding::UTF_8.name #=> "UTF-8"
*/
static VALUE
enc_name(VALUE self)
{
return rb_fstring_cstr(rb_enc_name((rb_encoding*)RTYPEDDATA_GET_DATA(self)));
}

static int
enc_names_i(st_data_t name, st_data_t idx, st_data_t args)
Expand Down Expand Up @@ -1513,7 +1502,7 @@ static VALUE
enc_dump(int argc, VALUE *argv, VALUE self)
{
rb_check_arity(argc, 0, 1);
return enc_name(self);
return rb_attr_get(self, id_i_name);
}

/* :nodoc: */
Expand Down Expand Up @@ -1602,12 +1591,7 @@ rb_locale_encoding(void)
int
rb_filesystem_encindex(void)
{
int idx;
GLOBAL_ENC_TABLE_LOCKING(enc_table) {
idx = enc_registered(enc_table, "filesystem");
}
if (idx < 0) idx = ENCINDEX_ASCII_8BIT;
return idx;
return filesystem_encindex;
}

rb_encoding *
Expand Down Expand Up @@ -1659,7 +1643,9 @@ enc_set_default_encoding(struct default_encoding *def, VALUE encoding, const cha
}

if (def == &default_external) {
enc_alias_internal(enc_table, "filesystem", Init_enc_set_filesystem_encoding());
int fs_idx = Init_enc_set_filesystem_encoding();
enc_alias_internal(enc_table, "filesystem", fs_idx);
filesystem_encindex = fs_idx;
}
}

Expand Down Expand Up @@ -2002,12 +1988,19 @@ Init_Encoding(void)
VALUE list;
int i;

id_i_name = rb_intern_const("@name");
rb_cEncoding = rb_define_class("Encoding", rb_cObject);
rb_define_alloc_func(rb_cEncoding, enc_s_alloc);
rb_undef_method(CLASS_OF(rb_cEncoding), "new");
rb_define_method(rb_cEncoding, "to_s", enc_name, 0);

/* The name of the encoding.
*
* Encoding::UTF_8.name #=> "UTF-8"
*/
rb_attr(rb_cEncoding, rb_intern("name"), TRUE, FALSE, Qfalse);
rb_define_alias(rb_cEncoding, "to_s", "name");

rb_define_method(rb_cEncoding, "inspect", enc_inspect, 0);
rb_define_method(rb_cEncoding, "name", enc_name, 0);
rb_define_method(rb_cEncoding, "names", enc_names, 0);
rb_define_method(rb_cEncoding, "dummy?", enc_dummy_p, 0);
rb_define_method(rb_cEncoding, "ascii_compatible?", enc_ascii_compatible_p, 0);
Expand Down
14 changes: 13 additions & 1 deletion include/ruby/fiber/scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
#define RUBY_FIBER_SCHEDULER_VERSION 3

struct timeval;
struct rb_thread_struct;

/**
* Wrap a `ssize_t` and `int errno` into a single `VALUE`. This interface should
Expand Down Expand Up @@ -118,7 +119,7 @@ VALUE rb_fiber_scheduler_current(void);

/**
* Identical to rb_fiber_scheduler_current(), except it queries for that of the
* passed thread instead of the implicit current one.
* passed thread value instead of the implicit current one.
*
* @param[in] thread Target thread.
* @exception rb_eTypeError `thread` is not a thread.
Expand All @@ -127,6 +128,17 @@ VALUE rb_fiber_scheduler_current(void);
*/
VALUE rb_fiber_scheduler_current_for_thread(VALUE thread);

/**
* Identical to rb_fiber_scheduler_current_for_thread(), except it expects
* a threadptr instead of a thread value.
*
* @param[in] thread Target thread.
* @exception rb_eTypeError `thread` is not a thread.
* @retval RUBY_Qnil No scheduler is in effect in `thread`.
* @retval otherwise The scheduler that is in effect in `thread`.
*/
VALUE rb_fiber_scheduler_current_for_threadptr(struct rb_thread_struct *thread);

/**
* Converts the passed timeout to an expression that rb_fiber_scheduler_block()
* etc. expects.
Expand Down
2 changes: 2 additions & 0 deletions include/ruby/internal/intern/select.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ struct timeval;
* someone else, vastly varies among operating systems. You would better avoid
* touching an fd from more than one threads.
*
* NOTE: this function is used in native extensions, so change its API with care.
*
* @internal
*
* Although any file descriptors are possible here, it makes completely no
Expand Down
4 changes: 2 additions & 2 deletions internal/thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ VALUE rb_mutex_owned_p(VALUE self);
VALUE rb_exec_recursive_outer_mid(VALUE (*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h, ID mid);
void ruby_mn_threads_params(void);

int rb_thread_io_wait(struct rb_io *io, int events, struct timeval * timeout);
int rb_thread_wait_for_single_fd(int fd, int events, struct timeval * timeout);
int rb_thread_io_wait(struct rb_thread_struct *th, struct rb_io *io, int events, struct timeval * timeout);
int rb_thread_wait_for_single_fd(struct rb_thread_struct *th, int fd, int events, struct timeval * timeout);

size_t rb_thread_io_close_interrupt(struct rb_io *);
void rb_thread_io_close_wait(struct rb_io *);
Expand Down
41 changes: 24 additions & 17 deletions io.c
Original file line number Diff line number Diff line change
Expand Up @@ -1291,7 +1291,8 @@ internal_writev_func(void *ptr)
static ssize_t
rb_io_read_memory(rb_io_t *fptr, void *buf, size_t count)
{
VALUE scheduler = rb_fiber_scheduler_current();
rb_thread_t *th = GET_THREAD();
VALUE scheduler = rb_fiber_scheduler_current_for_threadptr(th);
if (scheduler != Qnil) {
VALUE result = rb_fiber_scheduler_io_read_memory(scheduler, fptr->self, buf, count, 0);

Expand All @@ -1301,7 +1302,7 @@ rb_io_read_memory(rb_io_t *fptr, void *buf, size_t count)
}

struct io_internal_read_struct iis = {
.th = rb_thread_current(),
.th = th->self,
.fptr = fptr,
.nonblock = 0,
.fd = fptr->fd,
Expand All @@ -1324,7 +1325,8 @@ rb_io_read_memory(rb_io_t *fptr, void *buf, size_t count)
static ssize_t
rb_io_write_memory(rb_io_t *fptr, const void *buf, size_t count)
{
VALUE scheduler = rb_fiber_scheduler_current();
rb_thread_t *th = GET_THREAD();
VALUE scheduler = rb_fiber_scheduler_current_for_threadptr(th);
if (scheduler != Qnil) {
VALUE result = rb_fiber_scheduler_io_write_memory(scheduler, fptr->self, buf, count, 0);

Expand All @@ -1334,7 +1336,7 @@ rb_io_write_memory(rb_io_t *fptr, const void *buf, size_t count)
}

struct io_internal_write_struct iis = {
.th = rb_thread_current(),
.th = th->self,
.fptr = fptr,
.nonblock = 0,
.fd = fptr->fd,
Expand All @@ -1360,7 +1362,9 @@ rb_writev_internal(rb_io_t *fptr, const struct iovec *iov, int iovcnt)
{
if (!iovcnt) return 0;

VALUE scheduler = rb_fiber_scheduler_current();
rb_thread_t *th = GET_THREAD();

VALUE scheduler = rb_fiber_scheduler_current_for_threadptr(th);
if (scheduler != Qnil) {
// This path assumes at least one `iov`:
VALUE result = rb_fiber_scheduler_io_write_memory(scheduler, fptr->self, iov[0].iov_base, iov[0].iov_len, 0);
Expand All @@ -1371,7 +1375,7 @@ rb_writev_internal(rb_io_t *fptr, const struct iovec *iov, int iovcnt)
}

struct io_internal_writev_struct iis = {
.th = rb_thread_current(),
.th = th->self,
.fptr = fptr,
.nonblock = 0,
.fd = fptr->fd,
Expand Down Expand Up @@ -1453,7 +1457,8 @@ io_fflush(rb_io_t *fptr)
VALUE
rb_io_wait(VALUE io, VALUE events, VALUE timeout)
{
VALUE scheduler = rb_fiber_scheduler_current();
rb_thread_t *th = GET_THREAD();
VALUE scheduler = rb_fiber_scheduler_current_for_threadptr(th);

if (scheduler != Qnil) {
return rb_fiber_scheduler_io_wait(scheduler, io, events, timeout);
Expand All @@ -1474,7 +1479,7 @@ rb_io_wait(VALUE io, VALUE events, VALUE timeout)
tv = &tv_storage;
}

int ready = rb_thread_io_wait(fptr, RB_NUM2INT(events), tv);
int ready = rb_thread_io_wait(th, fptr, RB_NUM2INT(events), tv);

if (ready < 0) {
rb_sys_fail(0);
Expand All @@ -1498,25 +1503,24 @@ io_from_fd(int fd)
}

static int
io_wait_for_single_fd(int fd, int events, struct timeval *timeout)
io_wait_for_single_fd(int fd, int events, struct timeval *timeout, rb_thread_t *th, VALUE scheduler)
{
VALUE scheduler = rb_fiber_scheduler_current();

if (scheduler != Qnil) {
return RTEST(
rb_fiber_scheduler_io_wait(scheduler, io_from_fd(fd), RB_INT2NUM(events), rb_fiber_scheduler_make_timeout(timeout))
);
}

return rb_thread_wait_for_single_fd(fd, events, timeout);
return rb_thread_wait_for_single_fd(th, fd, events, timeout);
}

int
rb_io_wait_readable(int f)
{
io_fd_check_closed(f);

VALUE scheduler = rb_fiber_scheduler_current();
rb_thread_t *th = GET_THREAD();
VALUE scheduler = rb_fiber_scheduler_current_for_threadptr(th);

switch (errno) {
case EINTR:
Expand All @@ -1536,7 +1540,7 @@ rb_io_wait_readable(int f)
);
}
else {
io_wait_for_single_fd(f, RUBY_IO_READABLE, NULL);
io_wait_for_single_fd(f, RUBY_IO_READABLE, NULL, th, scheduler);
}
return TRUE;

Expand All @@ -1550,7 +1554,8 @@ rb_io_wait_writable(int f)
{
io_fd_check_closed(f);

VALUE scheduler = rb_fiber_scheduler_current();
rb_thread_t *th = GET_THREAD();
VALUE scheduler = rb_fiber_scheduler_current_for_threadptr(th);

switch (errno) {
case EINTR:
Expand Down Expand Up @@ -1579,7 +1584,7 @@ rb_io_wait_writable(int f)
);
}
else {
io_wait_for_single_fd(f, RUBY_IO_WRITABLE, NULL);
io_wait_for_single_fd(f, RUBY_IO_WRITABLE, NULL, th, scheduler);
}
return TRUE;

Expand All @@ -1591,7 +1596,9 @@ rb_io_wait_writable(int f)
int
rb_wait_for_single_fd(int fd, int events, struct timeval *timeout)
{
return io_wait_for_single_fd(fd, events, timeout);
rb_thread_t *th = GET_THREAD();
VALUE scheduler = rb_fiber_scheduler_current_for_threadptr(th);
return io_wait_for_single_fd(fd, events, timeout, th, scheduler);
}

int
Expand Down
11 changes: 8 additions & 3 deletions scheduler.c
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ rb_fiber_scheduler_set(VALUE scheduler)
}

static VALUE
rb_fiber_scheduler_current_for_threadptr(rb_thread_t *thread)
fiber_scheduler_current_for_threadptr(rb_thread_t *thread)
{
RUBY_ASSERT(thread);

Expand All @@ -467,13 +467,18 @@ VALUE rb_fiber_scheduler_current(void)
{
RUBY_ASSERT(ruby_thread_has_gvl_p());

return rb_fiber_scheduler_current_for_threadptr(GET_THREAD());
return fiber_scheduler_current_for_threadptr(GET_THREAD());
}

// This function is allowed to be called without holding the GVL.
VALUE rb_fiber_scheduler_current_for_thread(VALUE thread)
{
return rb_fiber_scheduler_current_for_threadptr(rb_thread_ptr(thread));
return fiber_scheduler_current_for_threadptr(rb_thread_ptr(thread));
}

VALUE rb_fiber_scheduler_current_for_threadptr(rb_thread_t *thread)
{
return fiber_scheduler_current_for_threadptr(thread);
}

/*
Expand Down
12 changes: 12 additions & 0 deletions test/ruby/test_ractor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,18 @@ def test_max_cpu_1
RUBY
end

def test_symbol_proc_is_shareable
pr = :symbol.to_proc
assert_make_shareable(pr)
end

# [Bug #21775]
def test_ifunc_proc_not_shareable
h = Hash.new { self }
pr = h.to_proc
assert_unshareable(pr, /not supported yet/, exception: RuntimeError)
end

def assert_make_shareable(obj)
refute Ractor.shareable?(obj), "object was already shareable"
Ractor.make_shareable(obj)
Expand Down
15 changes: 15 additions & 0 deletions test/ruby/test_zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,21 @@ def test(n)
}
end

def test_return_nonparam_local
# Use dead code (if false) to create a local without initialization instructions.
assert_compiles 'nil', %q{
def foo(a)
if false
x = nil
end
x
end
def test = foo(1)
test
test
}, call_threshold: 2
end

def test_setlocal_on_eval
assert_compiles '1', %q{
@b = binding
Expand Down
Loading