aboutsummaryrefslogtreecommitdiff
path: root/producer.sh
diff options
context:
space:
mode:
Diffstat (limited to 'producer.sh')
-rwxr-xr-xproducer.sh69
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"