Skip to content

Commit ddb66a9

Browse files
Attempt to detect QEMU hangs
When building cross platform images with Docker, QEMU is often used under the hood and can have a bug that cause forked processes to deadlock. Before spawning workers we test for that bug. Fix: #495 Closes: #497 Co-Authored-By: Sarun Rattanasiri <[email protected]>
1 parent 5e87800 commit ddb66a9

File tree

2 files changed

+38
-2
lines changed

2 files changed

+38
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Unreleased
22

3+
* Attempt to detect a QEMU bug that can cause `bootsnap precompile` to hang forever when building ARM64 docker images
4+
from x86_64 machines. See #495.
35
* Improve CLI to detect cgroup CPU limits and avoid spawning too many worker processes.
46

57
# 1.18.4

lib/bootsnap/cli/worker_pool.rb

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,18 @@ def create(size:, jobs:)
1717
end
1818

1919
def default_size
20-
size = [Etc.nprocessors, cpu_quota || 0].min
20+
nprocessors = Etc.nprocessors
21+
size = [nprocessors, cpu_quota || nprocessors].min
2122
case size
2223
when 0, 1
2324
0
2425
else
25-
size
26+
if fork_defunct?
27+
$stderr.puts "warning: faulty fork(2) detected, probably in cross platform docker builds. Disabling parallel compilation."
28+
0
29+
else
30+
size
31+
end
2632
end
2733
end
2834

@@ -45,6 +51,34 @@ def cpu_quota
4551
end
4652
end
4753
end
54+
55+
def fork_defunct?
56+
return true unless ::Process.respond_to?(:fork)
57+
58+
# Ref: https://github.com/Shopify/bootsnap/issues/495
59+
# The second forked process will hang on some QEMU environments
60+
r, w = IO.pipe
61+
pids = 2.times.map do
62+
::Process.fork do
63+
exit!(true)
64+
end
65+
end
66+
w.close
67+
r.wait_readable(1) # Wait at most 1s
68+
69+
defunct = false
70+
71+
pids.each do |pid|
72+
_pid, status = ::Process.wait2(pid, ::Process::WNOHANG)
73+
if status.nil? # Didn't exit in 1s
74+
defunct = true
75+
Process.kill(:KILL, pid)
76+
::Process.wait2(pid)
77+
end
78+
end
79+
80+
defunct
81+
end
4882
end
4983

5084
class Inline

0 commit comments

Comments
 (0)