@@ -94,8 +94,36 @@ def close
9494 @shell_id = nil
9595 end
9696
97+ # Ruby 3.1+ introduced a change where finalizers cannot allocate new threads.
98+ # This can raise a ThreadError when the WinRM shell cleanup tries to run in a Thread
99+ # during garbage collection. Previously, the gem used Thread.new in finalizers.
100+ #
101+ # Our solution preserves original behavior safely:
102+ # 1. Attempt cleanup in a new thread (same as before) for normal execution.
103+ # 2. If a ThreadError occurs specifically due to "can't alloc thread",
104+ # defer cleanup to an at_exit block to ensure the shell is closed properly
105+ # without crashing the program.
106+ # 3. Any other ThreadError is re-raised to avoid hiding unexpected issues.
97107 def self . finalize ( connection_opts , transport , shell_id )
98- proc { Thread . new { close_shell ( connection_opts , transport , shell_id ) } }
108+ proc do
109+ begin
110+ # Attempt normal cleanup in a new thread (preserves existing behavior)
111+ Thread . new { close_shell ( connection_opts , transport , shell_id ) }
112+ rescue ThreadError => e
113+ # Only handle the specific Ruby 3.1+ GC thread allocation restriction
114+ raise unless e . message . include? ( "can't alloc thread" )
115+
116+ # Defer cleanup to at_exit when thread allocation is permitted
117+ at_exit do
118+ begin
119+ close_shell ( connection_opts , transport , shell_id )
120+ rescue StandardError => cleanup_error
121+ # Warn about cleanup failures in deferred context only
122+ warn "[WinRM] Deferred shell cleanup failed: #{ cleanup_error . class } : #{ cleanup_error . message } "
123+ end
124+ end
125+ end
126+ end
99127 end
100128
101129 protected
0 commit comments