diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bf48e4b9..8c975c17 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,11 +8,15 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] - ruby: ['3.1', '3.2', 'head'] + ruby: ['3.1', '3.2', '3.3', 'head'] name: >- ${{matrix.os}}, ${{matrix.ruby}} + concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{matrix.os}}-${{matrix.ruby}}-libev + cancel-in-progress: true + runs-on: ${{matrix.os}} env: @@ -20,7 +24,7 @@ jobs: steps: - name: Setup machine - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Ruby uses: ruby/setup-ruby@v1 with: diff --git a/.github/workflows/test_io_uring.yml b/.github/workflows/test_io_uring.yml index b7d3afcf..7b945d22 100644 --- a/.github/workflows/test_io_uring.yml +++ b/.github/workflows/test_io_uring.yml @@ -8,15 +8,20 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] - ruby: ['3.1', '3.2', 'head'] + ruby: ['3.1', '3.2', '3.3', 'head'] name: >- ${{matrix.os}}, ${{matrix.ruby}} + concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-${{matrix.os}}-${{matrix.ruby}}-io_uring + cancel-in-progress: true + runs-on: ${{matrix.os}} + steps: - name: Checkout repository and submodules - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: recursive - name: Setup Ruby diff --git a/.rubocop.yml b/.rubocop.yml index c27956f9..cfcc79f5 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,5 +1,5 @@ AllCops: - TargetRubyVersion: 3.2 + TargetRubyVersion: 3.3 RubyInterpreters: - ruby Exclude: @@ -9,6 +9,7 @@ AllCops: - 'Gemfile*' - 'ext/**/*.rb' - lib/polyphony/adapters/irb.rb + NewCops: enable Style/LambdaCall: Enabled: false @@ -202,4 +203,4 @@ Style/SlicingWithRange: Style/RaiseArgs: Exclude: - - lib/polyphony/extensions/fiber.rb \ No newline at end of file + - lib/polyphony/extensions/fiber.rb diff --git a/README.md b/README.md index 4a6af0e9..cc3b68de 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ ## What is Polyphony? Polyphony is a library for building concurrent applications in Ruby. Polyphony -harnesses the power of [Ruby fibers](https://rubyapi.org/3.2/o/fiber) to provide +harnesses the power of [Ruby fibers](https://rubyapi.org/3.3/o/fiber) to provide a cooperative, sequential coroutine-based concurrency model. Under the hood, Polyphony uses [io_uring](https://unixism.net/loti/what_is_io_uring.html) or [libev](https://github.com/enki/libev) to maximize I/O performance. diff --git a/docs/advanced-io.md b/docs/advanced-io.md index 7de4d4d0..22919645 100644 --- a/docs/advanced-io.md +++ b/docs/advanced-io.md @@ -119,7 +119,7 @@ minimizing memory use and GC pressure. ## Compressing and decompressing in-flight data You might be familiar with Ruby's [zlib](https://github.com/ruby/zlib) gem (docs -[here](https://rubyapi.org/3.2/o/zlib)), which can be used to compress and +[here](https://rubyapi.org/3.3/o/zlib)), which can be used to compress and uncompress data using the popular gzip format. Imagine we want to implement an HTTP server that can serve files compressed using gzip: @@ -318,4 +318,4 @@ provided by Polyphony, which lets us write less code, have it run faster, have it run concurrently, and minimize memory allocations and pressure on the Ruby GC. Feel free to browse the [IO examples](https://github.com/digital-fabric/polyphony/tree/master/examples/io) -included in Polyphony. \ No newline at end of file +included in Polyphony. diff --git a/docs/readme.md b/docs/readme.md index ac8b8afb..47a6feb8 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -25,7 +25,7 @@ ## What is Polyphony? Polyphony is a library for building concurrent applications in Ruby. Polyphony -harnesses the power of [Ruby fibers](https://rubyapi.org/3.2/o/fiber) to provide +harnesses the power of [Ruby fibers](https://rubyapi.org/3.3/o/fiber) to provide a cooperative, sequential coroutine-based concurrency model. Under the hood, Polyphony uses [io_uring](https://unixism.net/loti/what_is_io_uring.html) or [libev](https://github.com/enki/libev) to maximize I/O performance. diff --git a/lib/polyphony/core/resource_pool.rb b/lib/polyphony/core/resource_pool.rb index 4613f8bd..a1f997de 100644 --- a/lib/polyphony/core/resource_pool.rb +++ b/lib/polyphony/core/resource_pool.rb @@ -56,9 +56,10 @@ def acquire(&block) # # @param sym [Symbol] method name # @param args [Array] method arguments + # @param kwargs [Hash] keyword arguments # @return [any] result of method call - def method_missing(sym, *args, &block) - acquire { |r| r.send(sym, *args, &block) } + def method_missing(sym, *args, **kwargs, &block) + acquire { |r| r.send(sym, *args, **kwargs, &block) } end # @!visibility private diff --git a/lib/polyphony/extensions/io.rb b/lib/polyphony/extensions/io.rb index dbf315fb..ab2b0570 100644 --- a/lib/polyphony/extensions/io.rb +++ b/lib/polyphony/extensions/io.rb @@ -27,20 +27,17 @@ def binwrite(name, string, offset = nil) end end - # @!visibility private - EMPTY_HASH = {}.freeze - # @!visibility private alias_method :orig_foreach, :foreach # @!visibility private - def foreach(name, sep = $/, limit = nil, getline_args = EMPTY_HASH, &block) + def foreach(name, sep = $/, limit = nil, **kwargs, &block) if sep.is_a?(Integer) sep = $/ limit = sep end File.open(name, 'r') do |f| - f.each_line(sep, limit, chomp: getline_args[:chomp], &block) + f.each_line(sep, limit, chomp: kwargs[:chomp], &block) end end @@ -48,21 +45,21 @@ def foreach(name, sep = $/, limit = nil, getline_args = EMPTY_HASH, &block) alias_method :orig_read, :read # @!visibility private - def read(name, length = nil, offset = nil, opt = EMPTY_HASH) + def read(name, length = nil, offset = nil, **kwargs) if length.is_a?(Hash) - opt = length + kwargs = length length = nil end - File.open(name, opt[:mode] || 'r') do |f| + File.open(name, kwargs[:mode] || 'r') do |f| f.seek(offset) if offset length ? f.read(length) : f.read end end alias_method :orig_readlines, :readlines - def readlines(name, sep = $/, limit = nil, getline_args = EMPTY_HASH) + def readlines(name, sep = $/, limit = nil, **kwargs) File.open(name, 'r') do |f| - f.readlines(sep, **getline_args) + f.readlines(sep, **kwargs) end end @@ -70,8 +67,8 @@ def readlines(name, sep = $/, limit = nil, getline_args = EMPTY_HASH) alias_method :orig_write, :write # @!visibility private - def write(name, string, offset = nil, opt = EMPTY_HASH) - File.open(name, opt[:mode] || 'w') do |f| + def write(name, string, offset = nil, **kwargs) + File.open(name, kwargs[:mode] || 'w') do |f| f.seek(offset) if offset f.write(string) end diff --git a/polyphony.gemspec b/polyphony.gemspec index 2a27afce..2c851b4b 100644 --- a/polyphony.gemspec +++ b/polyphony.gemspec @@ -20,15 +20,21 @@ Gem::Specification.new do |s| s.require_paths = ["lib"] s.required_ruby_version = '>= 3.1' - s.add_development_dependency 'rake-compiler', '1.2.1' - s.add_development_dependency 'minitest', '5.17.0' + s.add_development_dependency 'rake-compiler', '1.2.7' + s.add_development_dependency 'minitest', '5.22.3' s.add_development_dependency 'simplecov', '0.22.0' - s.add_development_dependency 'rubocop', '1.45.1' + s.add_development_dependency 'rubocop', '1.62.1' s.add_development_dependency 'pry', '0.14.2' - s.add_development_dependency 'msgpack', '1.6.0' + s.add_development_dependency 'msgpack', '1.7.2' s.add_development_dependency 'httparty', '0.21.0' - s.add_development_dependency 'localhost', '1.1.10' - s.add_development_dependency 'debug', '1.8.0' - s.add_development_dependency 'benchmark-ips', '2.10.0' + s.add_development_dependency 'localhost', '1.2.0' + s.add_development_dependency 'debug', '1.9.1' + s.add_development_dependency 'benchmark-ips', '2.13.0' + + # FIXME: remove gems when all other dependencies have bundled them (not part of stdlib since Ruby 3.4) + s.add_development_dependency 'base64', '0.2.0' + s.add_development_dependency 'bigdecimal', '3.1.7' + s.add_development_dependency 'csv', '3.3.0' + s.add_development_dependency 'mutex_m', '0.2.0' end diff --git a/test/helper.rb b/test/helper.rb index 6d6ae3a8..008b9f67 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -11,6 +11,7 @@ require_relative './eg' require 'minitest/autorun' +require 'minitest/unit' ::Exception.__disable_sanitized_backtrace__ = true @@ -44,6 +45,14 @@ def format_trace(args) def monotonic_clock ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) end + + def inspect_method_name_for(klass_name, method_name) + if RUBY_VERSION < '3.4.0' + "`#{method_name}'" + else + "'#{klass_name}##{method_name}'" + end + end end class MiniTest::Test diff --git a/test/test_fiber.rb b/test/test_fiber.rb index 3657ea4d..e4c85479 100644 --- a/test/test_fiber.rb +++ b/test/test_fiber.rb @@ -619,7 +619,7 @@ def test_inspect f = spin(:baz) { :foo } expected = format( - '#', + "#", f.object_id, __FILE__, spin_line_no @@ -627,8 +627,9 @@ def test_inspect assert_equal expected, f.inspect f.await + expected = format( - '#', + "#", f.object_id, __FILE__, spin_line_no diff --git a/test/test_thread.rb b/test/test_thread.rb index 21b434c1..72d9649f 100644 --- a/test/test_thread.rb +++ b/test/test_thread.rb @@ -133,7 +133,7 @@ def test_that_suspend_returns_immediately_if_no_watchers Thread.backend.trace_proc = proc {|*r| records << r } suspend assert_equal [ - [:block, Fiber.current, ["#{__FILE__}:#{__LINE__ - 2}:in `test_that_suspend_returns_immediately_if_no_watchers'"] + caller] + [:block, Fiber.current, ["#{__FILE__}:#{__LINE__ - 2}:in #{inspect_method_name_for(self.class.name, __method__.to_s)}"] + caller] ], records ensure Thread.backend.trace_proc = nil