Skip to content

Commit eb65986

Browse files
basvandijkclaude
andcommitted
fix(ic-os): serialize podman's SHM lock creation to stop flaky exit-125 builds
ic-os container builds intermittently die with: Error: failed to get new shm lock manager: failed to create 2048 locks in /libpod_rootless_lock_1001: file exists Podman keeps its container lock manager in a shared memory segment (one per uid when rootless, a single unsuffixed one as root). It opens that segment if it exists and otherwise creates it O_EXCL, with nothing serializing the gap between those two steps. ic-os actions run concurrently and unsandboxed (--strategy_regexp=ic-os[:/].*=local), so on a machine where the segment does not exist yet -- a fresh CI container -- the first burst of actions all find it missing and race to create it, and the losers exit 125. That is why this only ever bites early in a job: measured here, 832 concurrent invocations with the segment already present produced 0 failures, while deleting it and running 16 concurrently reproduced the error. Materialize the segment exactly once under a lock of our own, before the build starts. Once it exists podman only ever opens it, so every later action pays a single `test -e` -- no lock, no extra podman, and the builds themselves are never serialized. Also applied to build-bootloader-tree.sh, which drives podman outside the wrapper and can be part of the same first burst; two independent pre-warms would defeat each other. Measured with 5 rounds of 16 concurrent invocations from a cold segment, counting actions that reach podman while the segment is still absent (the race-exposed state): before after rootless 80/80 0/80 root 80/80 0/80 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 0d845e0 commit eb65986

2 files changed

Lines changed: 55 additions & 0 deletions

File tree

ic-os/bootloader/build-bootloader-tree.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,24 @@ done
2525

2626
TMP_DIR=$(mktemp -d --tmpdir="/tmp/containers" build-image-XXXXXXXXXXXX)
2727

28+
# Materialize podman's SHM lock segment under a lock, exactly as
29+
# toolchains/sysimage/proc_wrapper.sh does -- see the long comment there. This
30+
# script drives podman outside that wrapper, so without this it can be part of
31+
# the same first burst of ic-os actions that race to create the segment.
32+
if [ "$EUID" -eq 0 ]; then
33+
PODMAN_LOCK_SEGMENT="/dev/shm/libpod_lock"
34+
else
35+
PODMAN_LOCK_SEGMENT="/dev/shm/libpod_rootless_lock_$EUID"
36+
fi
37+
if [ -w /dev/shm ] && [ ! -e "$PODMAN_LOCK_SEGMENT" ]; then
38+
(
39+
flock 9
40+
if [ ! -e "$PODMAN_LOCK_SEGMENT" ]; then
41+
podman --root "${TMP_DIR}/root" --runroot "${TMP_DIR}/runroot" info >/dev/null 2>&1 || true
42+
fi
43+
) 9>"/dev/shm/icos-podman-lock-init.$EUID.lck"
44+
fi
45+
2846
BASE_IMAGE="$(cat ${BASE_IMAGE_FILE})"
2947

3048
podman --root "${TMP_DIR}/root" --runroot "${TMP_DIR}/runroot" build --iidfile "${TMP_DIR}/iidfile" - <<<"

toolchains/sysimage/proc_wrapper.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,43 @@ set -euo pipefail
1212
mkdir -p /tmp/containers
1313
podman_storage_dir=$(mktemp -d --tmpdir="/tmp/containers" "icosbuildXXXX")
1414

15+
# Podman keeps its container lock manager in a POSIX shared memory segment: one
16+
# per uid when rootless, and a single unsuffixed one when running as root. It
17+
# opens that segment if it exists and otherwise creates it O_EXCL -- with nothing
18+
# serializing the gap between those two steps. ic-os actions run concurrently and
19+
# unsandboxed, so on a machine where the segment does not exist yet (a fresh CI
20+
# container) the first burst of actions all find it missing and race to create it.
21+
# The losers die:
22+
# Error: failed to get new shm lock manager: failed to create 2048 locks in
23+
# /libpod_rootless_lock_<uid>: file exists
24+
# Close that window by materializing the segment exactly once, under a lock of
25+
# our own, before the build starts. Once it exists podman only ever opens it, so
26+
# every later action pays a single `test -e`: no lock, no extra podman, and the
27+
# builds themselves are never serialized.
28+
#
29+
# Note that root is NOT exempt: rootful podman keeps the same kind of segment,
30+
# just under a different name (/run/libpod is its tmp dir, not its lock), and
31+
# ci/container/container-run.sh runs the build as root whenever the host uid has
32+
# no matching container user. So pick the name by euid rather than skipping.
33+
if [ "$EUID" -eq 0 ]; then
34+
podman_lock_segment="/dev/shm/libpod_lock"
35+
else
36+
podman_lock_segment="/dev/shm/libpod_rootless_lock_$EUID"
37+
fi
38+
if [ -w /dev/shm ] && [ ! -e "$podman_lock_segment" ]; then
39+
(
40+
flock 9
41+
# Re-check under the lock: whoever got here first already created it.
42+
if [ ! -e "$podman_lock_segment" ]; then
43+
# Any podman command initializes the lock manager; `info` is the
44+
# cheapest. A failure here is not fatal -- we are only pre-warming,
45+
# and the build below reports real podman problems itself.
46+
podman --root "$podman_storage_dir/root" --runroot "$podman_storage_dir/runroot" \
47+
info >/dev/null 2>&1 || true
48+
fi
49+
) 9>"/dev/shm/icos-podman-lock-init.$EUID.lck"
50+
fi
51+
1552
tmpdir=$(mktemp -d --tmpdir "icosbuildXXXX")
1653
# podman runs rootless and writes files under the storage dir owned by
1754
# mapped subordinate uids; the calling user can't `rm` them directly.

0 commit comments

Comments
 (0)