From 86b4f8512b3196e35a747c0162004a6355b1737b Mon Sep 17 00:00:00 2001 From: Albert Alef Date: Thu, 11 Jun 2026 22:21:38 -0300 Subject: [PATCH 1/3] perf: improve boot speed --- bin/rubyshell | 4 +++- lib/rubyshell.rb | 4 ++-- lib/rubyshell/chainer.rb | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bin/rubyshell b/bin/rubyshell index 6cfc214..c6caa6d 100755 --- a/bin/rubyshell +++ b/bin/rubyshell @@ -1,6 +1,8 @@ -#!/usr/bin/env ruby +#!/usr/bin/env -S ruby --disable-gems # frozen_string_literal: true +$LOAD_PATH.unshift(File.expand_path("../lib", __dir__)) + require "rubyshell" if ARGV.first&.strip&.start_with? "exec" diff --git a/lib/rubyshell.rb b/lib/rubyshell.rb index c97ade9..0dc63e1 100644 --- a/lib/rubyshell.rb +++ b/lib/rubyshell.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require "logger" - require_relative "rubyshell/version" require_relative "rubyshell/command" require_relative "rubyshell/chainer" @@ -43,6 +41,8 @@ def debug? attr_writer :logger def logger + require "logger" + @logger ||= Logger.new($stdout) end diff --git a/lib/rubyshell/chainer.rb b/lib/rubyshell/chainer.rb index 512c676..ab32002 100644 --- a/lib/rubyshell/chainer.rb +++ b/lib/rubyshell/chainer.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "debug" module RubyShell class Chainer attr_reader :parts, :options From f1468d94d568e4a2198e31479edcb0fd51161a46 Mon Sep 17 00:00:00 2001 From: Albert Alef Date: Thu, 11 Jun 2026 22:24:19 -0300 Subject: [PATCH 2/3] perf: remove redundant pooling --- lib/rubyshell/terminal_executor.rb | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/lib/rubyshell/terminal_executor.rb b/lib/rubyshell/terminal_executor.rb index 3daff1d..6bd1c70 100644 --- a/lib/rubyshell/terminal_executor.rb +++ b/lib/rubyshell/terminal_executor.rb @@ -4,8 +4,6 @@ module RubyShell module TerminalExecutor - SELECT_TIMEOUT = Rational(1, 20).freeze - def self.capture(command, options) # rubocop:disable Metris/MethodLength,Metrics/CyclomaticComplexity,Metrix/AbcSize,Metrics/PerceivedComplexity stdin_value = if options[:_stdin].is_a?(RubyShell::Command) || options[:_stdin].is_a?(RubyShell::Chainer) options[:_stdin].exec @@ -27,17 +25,11 @@ def self.capture(command, options) # rubocop:disable Metris/MethodLength,Metrics error = +"" ios = { stdout => output, stderr => error } - status = nil - - # What was my idea here: - # If has not any io readable and the program exited in the previous loop, stop + # Block until a pipe has data (or hits EOF) instead of polling, so we + # dont burn a CPU core spinning while the command runs. + # EOF on both pipes empties ios and ends the loop, then we reap the exit status until ios.empty? - status ||= w_thread.join(0) - - readable, = IO.select(ios.keys, nil, nil, 0) - - break if !readable && status - next unless readable + readable, = IO.select(ios.keys) readable.each do |io| loop do From a6feb255331e27b3bd30b725abd10ed53f1d9222 Mon Sep 17 00:00:00 2001 From: Albert Alef Date: Thu, 11 Jun 2026 22:39:13 -0300 Subject: [PATCH 3/3] fix: revert regression --- lib/rubyshell/terminal_executor.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/rubyshell/terminal_executor.rb b/lib/rubyshell/terminal_executor.rb index 6bd1c70..1252391 100644 --- a/lib/rubyshell/terminal_executor.rb +++ b/lib/rubyshell/terminal_executor.rb @@ -4,6 +4,8 @@ module RubyShell module TerminalExecutor + SELECT_TIMEOUT = Rational(1, 20).freeze + def self.capture(command, options) # rubocop:disable Metris/MethodLength,Metrics/CyclomaticComplexity,Metrix/AbcSize,Metrics/PerceivedComplexity stdin_value = if options[:_stdin].is_a?(RubyShell::Command) || options[:_stdin].is_a?(RubyShell::Chainer) options[:_stdin].exec @@ -25,11 +27,17 @@ def self.capture(command, options) # rubocop:disable Metris/MethodLength,Metrics error = +"" ios = { stdout => output, stderr => error } - # Block until a pipe has data (or hits EOF) instead of polling, so we - # dont burn a CPU core spinning while the command runs. - # EOF on both pipes empties ios and ends the loop, then we reap the exit status + status = nil + + # What was my idea here: + # If has not any io readable and the program exited in the previous loop, stop until ios.empty? - readable, = IO.select(ios.keys) + status ||= w_thread.join(0) + + readable, = IO.select(ios.keys, nil, nil, SELECT_TIMEOUT) + + break if !readable && status + next unless readable readable.each do |io| loop do