diff options
| author | historia <historiavg@proton.me> | 2026-09-12 17:05:21 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-09-12 17:05:21 -0400 |
| commit | d19f7c6b41d1524ce9ab8f8005e3132fa9d07aa0 (patch) | |
| tree | e67992c614f83183f0cb3acf7d1b84cd9636744b | |
| parent | 18346d8deb858cc77d166ffea8ef76d9c9d48bfe (diff) | |
| download | producer-d19f7c6b41d1524ce9ab8f8005e3132fa9d07aa0.tar.gz | |
relative interpreter links, lock holder reporting
| -rwxr-xr-x | producer.sh | 69 |
1 files changed, 63 insertions, 6 deletions
diff --git a/producer.sh b/producer.sh index 884d3ac..cab10dd 100755 --- a/producer.sh +++ b/producer.sh @@ -62,6 +62,27 @@ USAGE log() { printf '[producer] %s\n' "$*" >&2; } fail() { printf '[producer] Error: %s\n' "$*" >&2; exit 1; } +# Print the PIDs of processes holding an open descriptor for the given file +# (a leading space each, e.g. " 1234 5678"). A plain /proc scan avoids a +# dependency on fuser or lsof, which may not be installed. Only processes +# visible in the current PID namespace can be found. +lock_holders() { + local file="$1" id p fd target base + id="$(stat -c '%d:%i' -- "$file" 2>/dev/null)" || return 0 + base="${file##*/}" + for p in /proc/[0-9]*; do + [[ "${p#/proc/}" == "$$" ]] && continue + for fd in "$p"/fd/*; do + target="$(readlink -- "$fd" 2>/dev/null)" || continue + [[ "$target" == *"$base" ]] || continue + if [[ "$(stat -c '%d:%i' -L -- "$fd" 2>/dev/null)" == "$id" ]]; then + printf ' %s' "${p#/proc/}" + fi + done + done + return 0 +} + case "${1:-}" in -h|--help) usage; exit 0 ;; esac @@ -130,13 +151,17 @@ export UV_PYTHON_INSTALL_DIR="$RUNTIME/uv/python" XDG_CACHE_HOME="$RUNTIME/cache export TMPDIR="$RUNTIME/tmp" PRODUCER_HOME="$RUNTIME" marker_current() { - # The marker must name this exact path, and the interpreter must exist: - # a failed or interrupted rebuild leaves neither, forcing a repair. + # Only the version suffix decides; the recorded path is informational. + # The runtime is shared between environments that mount this directory + # at different paths (a container sees /workspace, the host does not), + # and a broken move is caught anyway by the interpreter check below. + # A failed or interrupted rebuild leaves no valid marker, forcing a + # repair. [[ -f "$READY" ]] \ - && [[ "$(cat -- "$READY" 2>/dev/null)" == "$RUNTIME v$RUNTIME_VERSION" ]] \ + && [[ "$(cat -- "$READY" 2>/dev/null)" == *" v$RUNTIME_VERSION" ]] \ && [[ -x "$APP/bin/python" ]] \ && { [[ "$WANT_GUI" -eq 0 ]] || { [[ -f "$GUI_READY" ]] \ - && [[ "$(cat -- "$GUI_READY" 2>/dev/null)" == "$RUNTIME v$RUNTIME_VERSION" ]]; }; } + && [[ "$(cat -- "$GUI_READY" 2>/dev/null)" == *" v$RUNTIME_VERSION" ]]; }; } } bootstrap() { @@ -146,7 +171,17 @@ bootstrap() { # the whole destructive window below. It is released before the # application starts. exec 9>"$RUNTIME/bootstrap.lock" - flock 9 + if ! flock -n 9; then + # Never block silently: another launch is mid-bootstrap. + local holders9="" + holders9="$(lock_holders "$RUNTIME/bootstrap.lock")" + if [[ -n "$holders9" ]]; then + log "Waiting for another producer launch to finish preparing the runtime (held by PID(s):$holders9)" + else + log "Waiting for another producer launch to finish preparing the runtime" + fi + flock 9 + fi if [[ "$REBUILD" -eq 0 ]] && marker_current; then exec 9>&- # Runtime lock (fd 8): held shared for the application's lifetime so @@ -159,7 +194,17 @@ bootstrap() { # deleted, so a rebuild can never clobber a live environment. exec 8>"$RUNTIME/app.lock" if ! flock -n 8; then - log "Waiting for running producer jobs to finish before building the runtime" + # Point at the concrete holder: usually an earlier GUI or processing + # job that is still running, not a leftover lock file (flock locks + # disappear with their process). + local holders="" + holders="$(lock_holders "$RUNTIME/app.lock")" + if [[ -n "${holders//[[:space:]]/}" ]]; then + log "Waiting for running producer jobs to finish before building the runtime (held by PID(s):$holders)" + log "Stop them first, e.g. 'kill$holders' (a running 'gui' server counts as a job)." + else + log "Waiting for running producer jobs to finish before building the runtime" + fi flock 8 fi # Invalidate the marker before any destructive work: if this bootstrap @@ -198,6 +243,18 @@ bootstrap() { # The AI worker keeps its own pinned Python 3.11 (glibc only). "$UV" venv --python 3.13 --managed-python --relocatable --allow-existing "$APP" \ || fail "Creating the managed Python 3.13 environment failed; check network access and free space." + # uv creates the interpreter links with absolute targets. Make them + # relative so one runtime serves every environment that mounts this + # directory at a different path (host and containers included). + local link target + for link in "$APP"/bin/python "$APP"/bin/python3 "$APP"/bin/python3.*; do + [[ -L "$link" ]] || continue + target="$(readlink -- "$link")" + case "$target" in "$RUNTIME"/*) + ln -sfn -- "$(realpath -m --relative-to="$APP/bin" -- "$target")" "$link" + ;; + esac + done # The bundled FFmpeg wheel is preferred; where it does not exist (for # example musl systems), fall back to a system FFmpeg at runtime. local bundled="$PROJECT[bundled-ffmpeg]" core="$PROJECT" |
