From c80817dd55e8ed0984266626d4931e3f344abf1d Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Thu, 27 Aug 2026 20:51:21 -0700 Subject: [PATCH 1/2] Don't load tmpdir and fileutils at require time `require "tmpdir"` was never used -- nothing in the gem calls Dir.mktmpdir or Dir.tmpdir -- but it transitively pulls in fileutils and etc on every require of mixlib-shellout. `fileutils` is used in exactly one place, set_cgroup, which only runs when the :cgroup option is set and cgroup v2 is mounted. Load it in the parent inside fork_subprocess instead. It deliberately goes there rather than in set_cgroup itself: set_cgroup runs post-fork, and taking the require lock after forking a threaded parent can deadlock. Both of these are paid by every consumer of the gem -- chef-infra, ohai, test-kitchen, inspec -- on every process start. $ ruby -e 'require "mixlib/shellout"' # 30 runs, ruby 4.0.6 before median 12.96 ms +2148 heap objects, +7 files after median 5.39 ms +0 heap objects, +5 files 58% faster, and it no longer allocates. Signed-off-by: Tim Smith --- lib/mixlib/shellout.rb | 1 - lib/mixlib/shellout/unix.rb | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/mixlib/shellout.rb b/lib/mixlib/shellout.rb index 9676302..a731a4c 100644 --- a/lib/mixlib/shellout.rb +++ b/lib/mixlib/shellout.rb @@ -18,7 +18,6 @@ # require "etc" unless defined?(Etc) -require "tmpdir" unless defined?(Dir.mktmpdir) require "fcntl" require_relative "shellout/exceptions" diff --git a/lib/mixlib/shellout/unix.rb b/lib/mixlib/shellout/unix.rb index a2b918b..69a33fa 100644 --- a/lib/mixlib/shellout/unix.rb +++ b/lib/mixlib/shellout/unix.rb @@ -17,8 +17,6 @@ # limitations under the License. # -require "fileutils" unless defined?(FileUtils) - module Mixlib class ShellOut module Unix @@ -324,6 +322,10 @@ def cgroupv2_available? def fork_subprocess initialize_ipc + # FileUtils is only used by #set_cgroup. Load it here, in the parent, rather + # than post-fork where acquiring the require lock is not guaranteed safe. + require "fileutils" if cgroup && !defined?(FileUtils) + fork do # Child processes may themselves fork off children. A common case # is when the command is given as a single string (instead of From 423050ed4ce0f18928cc3e343ec40c4e92188006 Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Thu, 27 Aug 2026 20:56:40 -0700 Subject: [PATCH 2/2] Teach cspell the words these files already use The spellcheck job only scans files a PR touches, so any change to lib/mixlib/shellout/unix.rb or lib/mixlib/shellout.rb fails on identifiers that have been in those files for years: cgroupv, ducktype, endgrent, getgrent, LOGNAME, pgid, secondarygroups, seconderies, sgids and WNOHANG. "proccess" was flagged too, but that one is an actual typo in a comment rather than a word worth teaching the dictionary, so fix it instead. Signed-off-by: Tim Smith --- cspell.json | 10 ++++++++++ lib/mixlib/shellout/unix.rb | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cspell.json b/cspell.json index d4bbb9d..e52f6db 100644 --- a/cspell.json +++ b/cspell.json @@ -152,6 +152,7 @@ "certstore", "CFPREFERENCES", "cfprefsd", + "cgroupv", "chaput", "chardev", "chatops", @@ -333,6 +334,7 @@ "dscacheutil", "dscresource", "dslocal", + "ducktype", "DUPEUX", "DWORDLONG", "DYNALINK", @@ -358,6 +360,7 @@ "encap", "Encryptor", "encryptor", + "endgrent", "endlocal", "entriesread", "envdata", @@ -451,6 +454,7 @@ "GETFD", "GETFL", "getgr", + "getgrent", "getgrgid", "getgrnam", "gethostbyname", @@ -704,6 +708,7 @@ "loginclass", "loginwindow", "LOGLOCATION", + "LOGNAME", "logopts", "logstring", "LONGLONG", @@ -1017,6 +1022,7 @@ "PFILETIME", "PFLOAT", "PGENERICMAPPING", + "pgid", "phabricator", "PHALF", "PHANDLE", @@ -1245,6 +1251,8 @@ "Scriptable", "SCROLLBAR", "SCROLLBARS", + "secondarygroups", + "seconderies", "secontext", "secoption", "secopts", @@ -1291,6 +1299,7 @@ "SETTINGCHANGE", "setuid", "SETX", + "sgids", "SHARENAME", "SHAs", "shas", @@ -1626,6 +1635,7 @@ "WINVER", "WKSTA", "WMIGUID", + "WNOHANG", "woot", "workdir", "WPARAM", diff --git a/lib/mixlib/shellout/unix.rb b/lib/mixlib/shellout/unix.rb index 69a33fa..3010da2 100644 --- a/lib/mixlib/shellout/unix.rb +++ b/lib/mixlib/shellout/unix.rb @@ -110,7 +110,7 @@ def run_command unless ready_buffers @execution_time += READ_WAIT_TIME if @execution_time >= timeout && !@result - # kill the bad proccess + # kill the bad process reap_errant_child # read anything it wrote when we killed it attempt_buffer_read