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
4 changes: 0 additions & 4 deletions .github/workflows/check_misc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,6 @@ jobs:
# Skip 'push' events because post_push.yml fixes them on push
if: ${{ github.repository == 'ruby/ruby' && startsWith(github.event_name, 'pull') }}

- name: Check if C-sources are US-ASCII
run: |
grep -r -n --exclude-dir=vendor --include='*.[chyS]' --include='*.asm' $'[^\t-~]' -- . && exit 1 || :

- name: Check for bash specific substitution in configure.ac
run: |
git grep -n '\${[A-Za-z_0-9]*/' -- configure.ac && exit 1 || :
Expand Down
70 changes: 61 additions & 9 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ Note: We're only listing outstanding class updates.
* `Enumerator.produce` now accepts an optional `size` keyword argument
to specify the size of the enumerator. It can be an integer,
`Float::INFINITY`, a callable object (such as a lambda), or `nil` to
indicate unknown size. When not specified, the size is unknown (`nil`).
Previously, the size was always `Float::INFINITY` and not specifiable.
indicate unknown size. When not specified, the size defaults to
`Float::INFINITY`.

```ruby
# Infinite enumerator
Expand Down Expand Up @@ -98,6 +98,24 @@ Note: We're only listing outstanding class updates.
`Binding#implicit_parameter_defined?` have been added to access
numbered parameters and "it" parameter. [[Bug #21049]]

* ErrorHighlight

* When an ArgumentError is raised, it now displays code snippets for
both the method call (caller) and the method definition (callee).
[[Feature #21543]]

```
test.rb:1:in 'Object#add': wrong number of arguments (given 1, expected 2) (ArgumentError)

caller: test.rb:3
| add(1)
^^^
callee: test.rb:1
| def add(x, y) = x + y
^^^
from test.rb:3:in '<main>'
```

* File

* `File::Stat#birthtime` is now available on Linux via the statx
Expand Down Expand Up @@ -170,8 +188,8 @@ Note: We're only listing outstanding class updates.

* Range

* `Range#to_set` and `Enumerator#to_set` now perform size checks to prevent
issues with endless ranges. [[Bug #21654]]
* `Range#to_set` now performs size checks to prevent issues with
endless ranges. [[Bug #21654]]

* `Range#overlap?` now correctly handles infinite (unbounded) ranges.
[[Bug #21185]]
Expand Down Expand Up @@ -206,6 +224,11 @@ Note: We're only listing outstanding class updates.

* `Socket.tcp` & `TCPSocket.new` accepts an `open_timeout` keyword argument to specify
the timeout for the initial connection. [[Feature #21347]]
* When a user-specified timeout occurred in `TCPSocket.new`, either `Errno::ETIMEDOUT`
or `IO::TimeoutError` could previously be raised depending on the situation.
This behavior has been unified so that `IO::TimeoutError` is now consistently raised.
(Please note that, in `Socket.tcp`, there are still cases where `Errno::ETIMEDOUT` may
be raised in similar situations.)

* String

Expand Down Expand Up @@ -279,16 +302,17 @@ The following default gems are updated.
* io-wait 0.4.0
* ipaddr 1.2.8
* json 2.18.0
* net-http 0.8.0
* net-http 0.9.1
* openssl 4.0.0
* optparse 0.8.1
* pp 0.6.3
* prism 1.6.0
* psych 5.3.0
* psych 5.3.1
* resolv 0.7.0
* stringio 3.1.9.dev
* strscan 3.1.6.dev
* timeout 0.5.0
* stringio 3.2.0
* strscan 3.1.6
* time 0.4.2
* timeout 0.6.0
* uri 1.1.1
* weakref 0.1.4
* zlib 3.2.2
Expand Down Expand Up @@ -346,6 +370,31 @@ The following bundled gems are updated.
and was already deprecated,.
[[Feature #20971]]

* A backtrace for `ArgumentError` of "wrong number of arguments" now
include the receiver's class or module name (e.g., in `Foo#bar`
instead of in `bar`). [[Bug #21698]]

* Backtraces no longer display `internal` frames.
These methods now appear as if it is in the Ruby source file,
consistent with other C-implemented methods. [[Bug #20968]]

Before:
```
ruby -e '[1].fetch_values(42)'
<internal:array>:211:in 'Array#fetch': index 42 outside of array bounds: -1...1 (IndexError)
from <internal:array>:211:in 'block in Array#fetch_values'
from <internal:array>:211:in 'Array#map!'
from <internal:array>:211:in 'Array#fetch_values'
from -e:1:in '<main>'
```

After:
```
$ ruby -e '[1].fetch_values(42)'
-e:1:in 'Array#fetch_values': index 42 outside of array bounds: -1...1 (IndexError)
from -e:1:in '<main>'
```

## Stdlib compatibility issues

* CGI library is removed from the default gems. Now we only provide `cgi/escape` for
Expand Down Expand Up @@ -445,6 +494,7 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
[Feature #20750]: https://bugs.ruby-lang.org/issues/20750
[Feature #20884]: https://bugs.ruby-lang.org/issues/20884
[Feature #20925]: https://bugs.ruby-lang.org/issues/20925
[Bug #20968]: https://bugs.ruby-lang.org/issues/20968
[Feature #20971]: https://bugs.ruby-lang.org/issues/20971
[Bug #20974]: https://bugs.ruby-lang.org/issues/20974
[Feature #21047]: https://bugs.ruby-lang.org/issues/21047
Expand All @@ -469,8 +519,10 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
[Feature #21390]: https://bugs.ruby-lang.org/issues/21390
[Feature #21459]: https://bugs.ruby-lang.org/issues/21459
[Feature #21527]: https://bugs.ruby-lang.org/issues/21527
[Feature #21543]: https://bugs.ruby-lang.org/issues/21543
[Feature #21550]: https://bugs.ruby-lang.org/issues/21550
[Feature #21557]: https://bugs.ruby-lang.org/issues/21557
[Bug #21654]: https://bugs.ruby-lang.org/issues/21654
[Feature #21678]: https://bugs.ruby-lang.org/issues/21678
[Bug #21698]: https://bugs.ruby-lang.org/issues/21698
[Feature #21701]: https://bugs.ruby-lang.org/issues/21701
24 changes: 3 additions & 21 deletions enumerator.c
Original file line number Diff line number Diff line change
Expand Up @@ -3037,7 +3037,8 @@ producer_size(VALUE obj, VALUE args, VALUE eobj)
* The optional +size+ keyword argument specifies the size of the enumerator,
* which can be retrieved by Enumerator#size. It can be an integer,
* +Float::INFINITY+, a callable object (such as a lambda), or +nil+ to
* indicate unknown size. When not specified, the size is unknown (+nil+).
* indicate unknown size. When not specified, the size defaults to
* +Float::INFINITY+.
*
* # Infinite enumerator
* enum = Enumerator.produce(1, size: Float::INFINITY, &:succ)
Expand All @@ -3063,7 +3064,7 @@ enumerator_s_produce(int argc, VALUE *argv, VALUE klass)
rb_scan_args_kw(RB_SCAN_ARGS_LAST_HASH_KEYWORDS, argc, argv, "01:", &init, &opts);
rb_get_kwargs(opts, keyword_ids, 0, 1, &size);

size = UNDEF_P(size) ? Qnil : convert_to_feasible_size_value(size);
size = UNDEF_P(size) ? DBL2NUM(HUGE_VAL) : convert_to_feasible_size_value(size);

if (argc == 0 || (argc == 1 && !NIL_P(opts))) {
init = Qundef;
Expand Down Expand Up @@ -3366,24 +3367,6 @@ enumerator_plus(VALUE obj, VALUE eobj)
return new_enum_chain(rb_ary_new_from_args(2, obj, eobj));
}

/*
* call-seq:
* e.to_set -> set
*
* Returns a set generated from this enumerator.
*
* e = Enumerator.new { |y| y << 1 << 1 << 2 << 3 << 5 }
* e.to_set #=> #<Set: {1, 2, 3, 5}>
*/
static VALUE enumerator_to_set(int argc, VALUE *argv, VALUE obj)
{
VALUE size = rb_funcall(obj, id_size, 0);
if (RB_TYPE_P(size, T_FLOAT) && RFLOAT_VALUE(size) == INFINITY) {
rb_raise(rb_eArgError, "cannot convert an infinite enumerator to a set");
}
return rb_call_super(argc, argv);
}

/*
* Document-class: Enumerator::Product
*
Expand Down Expand Up @@ -4540,7 +4523,6 @@ InitVM_Enumerator(void)
rb_define_method(rb_cEnumerator, "rewind", enumerator_rewind, 0);
rb_define_method(rb_cEnumerator, "inspect", enumerator_inspect, 0);
rb_define_method(rb_cEnumerator, "size", enumerator_size, 0);
rb_define_method(rb_cEnumerator, "to_set", enumerator_to_set, -1);
rb_define_method(rb_cEnumerator, "+", enumerator_plus, 1);
rb_define_method(rb_mEnumerable, "chain", enum_chain, -1);

Expand Down
9 changes: 6 additions & 3 deletions ext/socket/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ rsock_socket(int domain, int type, int proto)

/* emulate blocking connect behavior on EINTR or non-blocking socket */
static int
wait_connectable(VALUE self, VALUE timeout)
wait_connectable(VALUE self, VALUE timeout, const struct sockaddr *sockaddr, int len)
{
int sockerr;
socklen_t sockerrlen;
Expand Down Expand Up @@ -514,7 +514,10 @@ wait_connectable(VALUE self, VALUE timeout)
VALUE result = rb_io_wait(self, RB_INT2NUM(RUBY_IO_READABLE|RUBY_IO_WRITABLE), timeout);

if (result == Qfalse) {
rb_raise(rb_eIOTimeoutError, "Connect timed out!");
VALUE rai = rsock_addrinfo_new((struct sockaddr *)sockaddr, len, PF_UNSPEC, 0, 0, Qnil, Qnil);
VALUE addr_str = rsock_addrinfo_inspect_sockaddr(rai);
VALUE message = rb_sprintf("user specified timeout for %" PRIsVALUE, addr_str);
rb_raise(rb_eIOTimeoutError, "%" PRIsVALUE, message);
}

int revents = RB_NUM2INT(result);
Expand Down Expand Up @@ -603,7 +606,7 @@ rsock_connect(VALUE self, const struct sockaddr *sockaddr, int len, int socks, V
#ifdef EINPROGRESS
case EINPROGRESS:
#endif
return wait_connectable(self, timeout);
return wait_connectable(self, timeout, sockaddr, len);
}
}
return status;
Expand Down
28 changes: 23 additions & 5 deletions ext/socket/ipsocket.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,19 @@ struct inetsock_arg
};

void
rsock_raise_user_specified_timeout(void)
rsock_raise_user_specified_timeout(struct addrinfo *ai, VALUE host, VALUE port)
{
rb_raise(rb_eIOTimeoutError, "user specified timeout");
VALUE message;

if (ai && ai->ai_addr) {
VALUE rai = rsock_addrinfo_new((struct sockaddr *)ai->ai_addr, (socklen_t)ai->ai_addrlen, PF_UNSPEC, 0, 0, Qnil, Qnil);
VALUE addr_str = rsock_addrinfo_inspect_sockaddr(rai);
message = rb_sprintf("user specified timeout for %" PRIsVALUE, addr_str);
} else {
message = rb_sprintf("user specified timeout for %" PRIsVALUE " port %" PRIsVALUE, host, port);
}

rb_raise(rb_eIOTimeoutError, "%" PRIsVALUE, message);
}

static VALUE
Expand Down Expand Up @@ -149,7 +159,9 @@ init_inetsock_internal(VALUE v)
} else {
VALUE elapsed = rb_funcall(current_clocktime(), '-', 1, starts_at);
timeout = rb_funcall(open_timeout, '-', 1, elapsed);
if (rb_funcall(timeout, '<', 1, INT2FIX(0)) == Qtrue) rsock_raise_user_specified_timeout();
if (rb_funcall(timeout, '<', 1, INT2FIX(0)) == Qtrue) {
rsock_raise_user_specified_timeout(res, arg->remote.host, arg->remote.serv);
}
}

if (status >= 0) {
Expand Down Expand Up @@ -844,6 +856,10 @@ init_fast_fallback_inetsock_internal(VALUE v)
if (!NIL_P(open_timeout)) {
VALUE elapsed = rb_funcall(current_clocktime(), '-', 1, starts_at);
timeout = rb_funcall(open_timeout, '-', 1, elapsed);

if (rb_funcall(timeout, '<', 1, INT2FIX(0)) == Qtrue) {
rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv);
}
}
if (NIL_P(timeout)) {
if (!NIL_P(connect_timeout)) {
Expand Down Expand Up @@ -1180,7 +1196,9 @@ init_fast_fallback_inetsock_internal(VALUE v)
}
}

if (is_timeout_tv(user_specified_open_timeout_at, now)) rsock_raise_user_specified_timeout();
if (is_timeout_tv(user_specified_open_timeout_at, now)) {
rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv);
}

if (!any_addrinfos(&resolution_store)) {
if (!in_progress_fds(arg->connection_attempt_fds_size) &&
Expand All @@ -1203,7 +1221,7 @@ init_fast_fallback_inetsock_internal(VALUE v)
resolution_store.is_all_finished) &&
(is_timeout_tv(user_specified_connect_timeout_at, now) ||
!in_progress_fds(arg->connection_attempt_fds_size))) {
rsock_raise_user_specified_timeout();
rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv);
}
}
}
Expand Down
8 changes: 5 additions & 3 deletions ext/socket/lib/socket.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def connect_internal(local_addrinfo, timeout=nil) # :yields: socket
break
when :wait_writable
sock.wait_writable(timeout) or
raise Errno::ETIMEDOUT, 'user specified timeout'
raise Errno::ETIMEDOUT, "user specified timeout for #{self.ip_address}:#{self.ip_port}"
end while true
else
sock.connect(self)
Expand Down Expand Up @@ -905,7 +905,9 @@ def self.tcp_with_fast_fallback(host, port, local_host = nil, local_port = nil,
end
end

raise(IO::TimeoutError, 'user specified timeout') if expired?(now, user_specified_open_timeout_at)
if expired?(now, user_specified_open_timeout_at)
raise(IO::TimeoutError, "user specified timeout for #{host}:#{port}")
end

if resolution_store.empty_addrinfos?
if connecting_sockets.empty? && resolution_store.resolved_all_families?
Expand All @@ -918,7 +920,7 @@ def self.tcp_with_fast_fallback(host, port, local_host = nil, local_port = nil,

if (expired?(now, user_specified_resolv_timeout_at) || resolution_store.resolved_all_families?) &&
(expired?(now, user_specified_connect_timeout_at) || connecting_sockets.empty?)
raise IO::TimeoutError, 'user specified timeout'
raise(IO::TimeoutError, "user specified timeout for #{host}:#{port}")
end
end
end
Expand Down
10 changes: 9 additions & 1 deletion ext/socket/raddrinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,15 @@ rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hint

if (need_free) free_getaddrinfo_arg(arg);

if (timedout) rsock_raise_user_specified_timeout();
if (timedout) {
if (arg->ai) {
rsock_raise_user_specified_timeout(arg->ai, Qnil, Qnil);
} else {
VALUE host = rb_str_new_cstr(hostp);
VALUE port = rb_str_new_cstr(portp);
rsock_raise_user_specified_timeout(NULL, host, port);
}
}

// If the current thread is interrupted by asynchronous exception, the following raises the exception.
// But if the current thread is interrupted by timer thread, the following returns; we need to manually retry.
Expand Down
2 changes: 1 addition & 1 deletion ext/socket/rubysocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ void free_fast_fallback_getaddrinfo_shared(struct fast_fallback_getaddrinfo_shar
#endif

unsigned int rsock_value_timeout_to_msec(VALUE);
NORETURN(void rsock_raise_user_specified_timeout(void));
NORETURN(void rsock_raise_user_specified_timeout(struct addrinfo *ai, VALUE host, VALUE port));

void rsock_init_basicsocket(void);
void rsock_init_ipsocket(void);
Expand Down
Loading