From a6c15fb2424148fe1247dcf0fd2186fd4b46bc4d Mon Sep 17 00:00:00 2001 From: "Yamashita, Yuu" Date: Thu, 19 Apr 2018 01:02:16 +0000 Subject: [PATCH 1/4] Experimental implementation to wait rehash until acquiring lock For now the code is using traditional pseudo locking mechanism based on `noclobber`. --- libexec/pyenv-rehash | 56 ++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/libexec/pyenv-rehash b/libexec/pyenv-rehash index 0c822e51..b03c0d1b 100755 --- a/libexec/pyenv-rehash +++ b/libexec/pyenv-rehash @@ -10,29 +10,49 @@ PROTOTYPE_SHIM_PATH="${SHIM_PATH}/.pyenv-shim" # Create the shims directory if it doesn't already exist. mkdir -p "$SHIM_PATH" -# Ensure only one instance of pyenv-rehash is running at a time by -# setting the shell's `noclobber` option and attempting to write to -# the prototype shim file. If the file already exists, print a warning -# to stderr and exit with a non-zero status. -set -o noclobber -{ echo > "$PROTOTYPE_SHIM_PATH" -} 2>| /dev/null || -{ if [ -w "$SHIM_PATH" ]; then +acquire_lock() { + # Ensure only one instance of pyenv-rehash is running at a time by + # setting the shell's `noclobber` option and attempting to write to + # the prototype shim file. If the file already exists, print a warning + # to stderr and exit with a non-zero status. + local ret + set -o noclobber + echo > "$PROTOTYPE_SHIM_PATH" 2>| /dev/null || ret=1 + set +o noclobber + [ -z "${ret}" ] +} + +# If we were able to obtain a lock, register a trap to clean up the +# prototype shim when the process exits. +trap release_lock EXIT + +remove_prototype_shim() { + rm -f "$PROTOTYPE_SHIM_PATH" +} + +release_lock() { + remove_prototype_shim +} + +unset acquired +for _ in $(seq "${PYENV_REHASH_LOCK_TIMEOUT:-60}"); do + if acquire_lock 2>/dev/null; then + acquired=1 + break + else + # POSIX sleep(1) doesn't provides time precision of subsecond + sleep 1 + fi +done + +if [ -z "${acquired}" ]; then + if [ -w "$SHIM_PATH" ]; then echo "pyenv: cannot rehash: $PROTOTYPE_SHIM_PATH exists" else echo "pyenv: cannot rehash: $SHIM_PATH isn't writable" fi exit 1 -} >&2 -set +o noclobber - -# If we were able to obtain a lock, register a trap to clean up the -# prototype shim when the process exits. -trap remove_prototype_shim EXIT - -remove_prototype_shim() { - rm -f "$PROTOTYPE_SHIM_PATH" -} +fi # The prototype shim file is a script that re-execs itself, passing # its filename and any arguments to `pyenv exec`. This file is From b1e3f13a85b30ddf380a8dc3bfeffb96eeb83a68 Mon Sep 17 00:00:00 2001 From: "Yamashita, Yuu" Date: Tue, 24 Apr 2018 00:35:55 +0000 Subject: [PATCH 2/4] Renamed variable; s/PYENV_REHASH_LOCK_TIMEOUT/PYENV_REHASH_TIMEOUT/ --- libexec/pyenv-rehash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libexec/pyenv-rehash b/libexec/pyenv-rehash index b03c0d1b..a4f651c5 100755 --- a/libexec/pyenv-rehash +++ b/libexec/pyenv-rehash @@ -35,7 +35,7 @@ release_lock() { } unset acquired -for _ in $(seq "${PYENV_REHASH_LOCK_TIMEOUT:-60}"); do +for _ in $(seq "${PYENV_REHASH_TIMEOUT:-60}"); do if acquire_lock 2>/dev/null; then acquired=1 break From 7973e5947395b3def52ca623e7571ec57e2393f9 Mon Sep 17 00:00:00 2001 From: "Yamashita, Yuu" Date: Tue, 24 Apr 2018 00:37:29 +0000 Subject: [PATCH 3/4] Fix rehash test to give up sooner after lock file's presence --- libexec/pyenv-rehash | 11 ++++++----- test/rehash.bats | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/libexec/pyenv-rehash b/libexec/pyenv-rehash index a4f651c5..2b2b8248 100755 --- a/libexec/pyenv-rehash +++ b/libexec/pyenv-rehash @@ -34,6 +34,11 @@ release_lock() { remove_prototype_shim } +if [ ! -w "$SHIM_PATH" ]; then + echo "pyenv: cannot rehash: $SHIM_PATH isn't writable" + exit 1 +fi + unset acquired for _ in $(seq "${PYENV_REHASH_TIMEOUT:-60}"); do if acquire_lock 2>/dev/null; then @@ -46,11 +51,7 @@ for _ in $(seq "${PYENV_REHASH_TIMEOUT:-60}"); do done if [ -z "${acquired}" ]; then - if [ -w "$SHIM_PATH" ]; then - echo "pyenv: cannot rehash: $PROTOTYPE_SHIM_PATH exists" - else - echo "pyenv: cannot rehash: $SHIM_PATH isn't writable" - fi + echo "pyenv: cannot rehash: $PROTOTYPE_SHIM_PATH exists" exit 1 fi diff --git a/test/rehash.bats b/test/rehash.bats index 99612733..c5694f4e 100755 --- a/test/rehash.bats +++ b/test/rehash.bats @@ -25,6 +25,7 @@ create_executable() { } @test "rehash in progress" { + export PYENV_REHASH_TIMEOUT=1 mkdir -p "${PYENV_ROOT}/shims" touch "${PYENV_ROOT}/shims/.pyenv-shim" run pyenv-rehash From 1ec57a0c6886eab277da507afa8316d03bc051dd Mon Sep 17 00:00:00 2001 From: "Yamashita, Yuu" Date: Tue, 24 Apr 2018 00:42:32 +0000 Subject: [PATCH 4/4] Add basic test for rehash wait --- test/rehash.bats | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/rehash.bats b/test/rehash.bats index c5694f4e..8afe87e6 100755 --- a/test/rehash.bats +++ b/test/rehash.bats @@ -32,6 +32,15 @@ create_executable() { assert_failure "pyenv: cannot rehash: ${PYENV_ROOT}/shims/.pyenv-shim exists" } +@test "wait until lock acquisition" { + export PYENV_REHASH_TIMEOUT=5 + mkdir -p "${PYENV_ROOT}/shims" + touch "${PYENV_ROOT}/shims/.pyenv-shim" + bash -c "sleep 1 && rm -f ${PYENV_ROOT}/shims/.pyenv-shim" & + run pyenv-rehash + assert_success +} + @test "creates shims" { create_executable "2.7" "python" create_executable "2.7" "fab"