[#114703] [Ruby master Bug#19875] Ruby 2.7 -> 3.1 Performance regression in String#count — "iz (Illia Zub) via ruby-core" <ruby-core@...>

Issue #19875 has been reported by iz (Illia Zub).

18 messages 2023/09/12

[#114774] [Ruby master Feature#19884] Make Safe Navigation Operator work on classes — "p8 (Petrik de Heus) via ruby-core" <ruby-core@...>

Issue #19884 has been reported by p8 (Petrik de Heus).

13 messages 2023/09/15

[#114796] [Ruby master Feature#19889] Let `Kernel.#require` search for files relative to the current working directory for non ./, ../ relative paths — "sawa (Tsuyoshi Sawada) via ruby-core" <ruby-core@...>

Issue #19889 has been reported by sawa (Tsuyoshi Sawada).

6 messages 2023/09/18

[#114803] [Ruby master Bug#19890] File#realine(chomp: true) slower/more allocations than readline.chomp! — "segiddins (Samuel Giddins) via ruby-core" <ruby-core@...>

Issue #19890 has been reported by segiddins (Samuel Giddins).

12 messages 2023/09/18

[#114817] [Ruby master Bug#19892] Build failure with 8f1b688177 — "vo.x (Vit Ondruch) via ruby-core" <ruby-core@...>

Issue #19892 has been reported by vo.x (Vit Ondruch).

8 messages 2023/09/19

[#114915] [Ruby master Feature#19905] Introduce `Queue#peek` — "hi@... (Joao Fernandes) via ruby-core" <ruby-core@...>

Issue #19905 has been reported by hi@joaofernandes.me (Joao Fernandes).

8 messages 2023/09/28

[ruby-core:114838] [Ruby master Bug#19624] Backticks - IO object leakage

From: "byroot (Jean Boussier) via ruby-core" <ruby-core@...>
Date: 2023-09-20 13:01:12 UTC
List: ruby-core #114838
Issue #19624 has been updated by byroot (Jean Boussier).





> Do you have any short reproducible code?



No sorry, I don't understand the fiber scheduler enough to reduce the test =
suite.



I discovered the bug via: https://github.com/rmosolgo/graphql-ruby/issues/4=
640#issuecomment-1727220148



```

GraphQL::Dataloader::AsyncDataloader::With the toy scheduler from Ruby's te=
sts#test_0003_works with GraphQL:

NotImplementedError: method `hash' called on hidden T_FILE object (0x000000=
0119cd5ca0 flags=3D0xb)

    src/graphql-ruby/spec/support/dummy_scheduler.rb:159:in `io_wait'

    src/graphql-ruby/spec/graphql/dataloader/async_dataloader_spec.rb:69:in=
 ``'

    src/graphql-ruby/spec/graphql/dataloader/async_dataloader_spec.rb:69:in=
 `sleep'

```



I tried extracting their scheduler to make a self contained repro, but with=
out success:



```ruby

class DummyScheduler

  def initialize

    @readable =3D {}

    @writable =3D {}

    @waiting =3D {}



    @closed =3D false



    @lock =3D Mutex.new

    @blocking =3D 0

    @ready =3D []



    @urgent =3D IO.pipe

  end



  attr :readable

  attr :writable

  attr :waiting



  def next_timeout

    _fiber, timeout =3D @waiting.min_by{|key, value| value}



    if timeout

      offset =3D timeout - current_time



      if offset < 0

        return 0

      else

        return offset

      end

    end

  end



  def run

    # $stderr.puts [__method__, Fiber.current].inspect



    while @readable.any? or @writable.any? or @waiting.any? or @blocking.po=
sitive?

      # Can only handle file descriptors up to 1024...

      readable, writable =3D IO.select(@readable.keys + [@urgent.first], @w=
ritable.keys, [], next_timeout)



      # puts "readable: #{readable}" if readable&.any?

      # puts "writable: #{writable}" if writable&.any?



      selected =3D {}



      readable && readable.each do |io|

        if fiber =3D @readable.delete(io)

          selected[fiber] =3D IO::READABLE

        elsif io =3D=3D @urgent.first

          @urgent.first.read_nonblock(1024)

        end

      end



      writable && writable.each do |io|

        if fiber =3D @writable.delete(io)

          selected[fiber] |=3D IO::WRITABLE

        end

      end



      selected.each do |fiber, events|

        fiber.resume(events)

      end



      if @waiting.any?

        time =3D current_time

        waiting, @waiting =3D @waiting, {}



        waiting.each do |fiber, timeout|

          if fiber.alive?

            if timeout <=3D time

              fiber.resume

            else

              @waiting[fiber] =3D timeout

            end

          end

        end

      end



      if @ready.any?

        ready =3D nil



        @lock.synchronize do

          ready, @ready =3D @ready, []

        end



        ready.each do |fiber|

          fiber.resume

        end

      end

    end

  end



  def close

    # $stderr.puts [__method__, Fiber.current].inspect



    raise "Scheduler already closed!" if @closed



    self.run

  ensure

    @urgent.each(&:close)

    @urgent =3D nil



    @closed =3D true



    # We freeze to detect any unintended modifications after the scheduler =
is closed:

    self.freeze

  end



  def closed?

    @closed

  end



  def current_time

    Process.clock_gettime(Process::CLOCK_MONOTONIC)

  end



  def timeout_after(duration, klass, message, &block)

    fiber =3D Fiber.current



    self.fiber do

      sleep(duration)



      if fiber && fiber.alive?

        fiber.raise(klass, message)

      end

    end



    begin

      yield(duration)

    ensure

      fiber =3D nil

    end

  end



  def process_wait(pid, flags)

    # $stderr.puts [__method__, pid, flags, Fiber.current].inspect



    # This is a very simple way to implement a non-blocking wait:

    Thread.new do

      Process::Status.wait(pid, flags)

    end.value

  end



  def io_wait(io, events, duration)

    p io

    # $stderr.puts [__method__, io, events, duration, Fiber.current].inspect



    unless (events & IO::READABLE).zero?

      @readable[io] =3D Fiber.current

    end



    unless (events & IO::WRITABLE).zero?

      @writable[io] =3D Fiber.current

    end



    Fiber.yield

  end



  # Used for Kernel#sleep and Mutex#sleep

  def kernel_sleep(duration =3D nil)

    # $stderr.puts [__method__, duration, Fiber.current].inspect



    self.block(:sleep, duration)



    return true

  end



  # Used when blocking on synchronization (Mutex#lock, Queue#pop, SizedQueu=
e#push, ...)

  def block(blocker, timeout =3D nil)

    # $stderr.puts [__method__, blocker, timeout].inspect



    if timeout

      @waiting[Fiber.current] =3D current_time + timeout

      begin

        Fiber.yield

      ensure

        # Remove from @waiting in the case #unblock was called before the t=
imeout expired:

        @waiting.delete(Fiber.current)

      end

    else

      @blocking +=3D 1

      begin

        Fiber.yield

      ensure

        @blocking -=3D 1

      end

    end

  end



  # Used when synchronization wakes up a previously-blocked fiber (Mutex#un=
lock, Queue#push, ...).

  # This might be called from another thread.

  def unblock(blocker, fiber)

    # $stderr.puts [__method__, blocker, fiber].inspect

    # $stderr.puts blocker.backtrace.inspect

    # $stderr.puts fiber.backtrace.inspect



    @lock.synchronize do

      @ready << fiber

    end



    io =3D @urgent.last

    io.write_nonblock('.')

  end



  def fiber(&block)

    fiber =3D Fiber.new(blocking: false, &block)



    fiber.resume



    return fiber

  end



  def address_resolve(hostname)

    Thread.new do

      Addrinfo.getaddrinfo(hostname, nil).map(&:ip_address).uniq

    end.value

  end

end



Fiber.set_scheduler(DummyScheduler.new)



p `sleep 2`

```



Perhaps @ioquatix would know how to reproduce?





----------------------------------------

Bug #19624: Backticks - IO object leakage

https://bugs.ruby-lang.org/issues/19624#change-104683



* Author: pineman (Jo=E3o Pinheiro)

* Status: Open

* Priority: Normal

* ruby -v: ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]

* Backport: 3.0: UNKNOWN, 3.1: UNKNOWN, 3.2: UNKNOWN

----------------------------------------

Hi,



This code works on ruby 3.0.6:



```ruby

`echo`

ObjectSpace.each_object(IO) do |io|

  if ![STDIN, STDOUT, STDERR].include?(io)

    io.close

  end

end

```

but raises `IOError` on 3.2.2:

```

minimal-repro-case.rb:8:in `close': uninitialized stream (IOError)

```





I found it started failing on ruby 3.1.0 and after, on macOS and Linux.

This code is useful for closing unneeded IO objects in forked processes.

It looks like backticks is 'leaking' IO objects, waiting for GC, and it did=
n't used to before 3.1.0.

In ruby 3.1.0, inside `rb_f_backquote` in `io.c`, `rb_gc_force_recycle` was=
 removed in favor of `RB_GC_GUARD` (commit `aeae6e2842e`). I wonder if this=
 has something to do with the problem.

Is this code incorrect since ruby 3.1.0 or is it a bug in ruby?

Thanks.



---Files--------------------------------

minimal-repro-case.rb (109 Bytes)





--=20

https://bugs.ruby-lang.org/

 ______________________________________________
 ruby-core mailing list -- ruby-core@ml.ruby-lang.org
 To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org
 ruby-core info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-c=
ore.ml.ruby-lang.org/

In This Thread