Skip to content

Commit 829e86a

Browse files
Fix reorder Process.lock_write outside of block_signals (#16431)
Signals shouldn't be blocked while the process potentially waits to acquire the write lock. So `lock_write` must be the outermost block.
1 parent b609bb7 commit 829e86a

File tree

2 files changed

+44
-28
lines changed

2 files changed

+44
-28
lines changed

src/crystal/system/unix/process.cr

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -197,22 +197,30 @@ struct Crystal::System::Process
197197
def self.fork
198198
{% raise("Process fork is unsupported with multithreaded mode") if flag?(:preview_mt) %}
199199

200-
block_signals do
201-
case pid = lock_write { LibC.fork }
202-
when 0
203-
# forked process
204-
205-
::Process.after_fork_child_callbacks.each(&.call)
206-
207-
nil
208-
when -1
209-
# forking process: error
210-
raise RuntimeError.from_errno("fork")
211-
else
212-
# forking process: success
213-
pid
200+
result = lock_write do
201+
block_signals do
202+
case pid = LibC.fork
203+
when 0
204+
# forked process
205+
206+
::Process.after_fork_child_callbacks.each(&.call)
207+
208+
nil
209+
when -1
210+
# forking process: error
211+
Errno.value
212+
else
213+
# forking process: success
214+
pid
215+
end
214216
end
215217
end
218+
219+
if result.is_a?(Errno)
220+
raise RuntimeError.from_os_error("fork", result)
221+
else
222+
result
223+
end
216224
end
217225

218226
private def self.block_signals(&)

src/crystal/system/unix/spawn.cr

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,25 +59,33 @@ struct Crystal::System::Process
5959
end
6060

6161
private def self.fork_for_exec
62-
block_signals do |sigmask|
63-
case pid = lock_write { LibC.fork }
64-
when 0
65-
# forked process
62+
result = lock_write do
63+
block_signals do |sigmask|
64+
case pid = LibC.fork
65+
when 0
66+
# forked process
6667

67-
Crystal::System::Signal.after_fork_before_exec
68+
Crystal::System::Signal.after_fork_before_exec
6869

69-
# reset sigmask (inherited on exec)
70-
LibC.sigemptyset(sigmask)
70+
# reset sigmask (inherited on exec)
71+
LibC.sigemptyset(sigmask)
7172

72-
nil
73-
when -1
74-
# forking process: error
75-
raise RuntimeError.from_errno("fork")
76-
else
77-
# forking process: success
78-
pid
73+
nil
74+
when -1
75+
# forking process: error
76+
Errno.value
77+
else
78+
# forking process: success
79+
pid
80+
end
7981
end
8082
end
83+
84+
if result.is_a?(Errno)
85+
raise RuntimeError.from_os_error("fork", result)
86+
else
87+
result
88+
end
8189
end
8290

8391
# This method is similar to `.replace` (used for `Process.exec`) with some

0 commit comments

Comments
 (0)