Вхождение в состояние гонки, даже с ожиданием

Я сталкиваюсь со странным состоянием гонки в моем bash программа. Я попытался воспроизвести его с помощью достаточно простой демонстрационной программы, но, очевидно, что это верно для всех / большинства попыток демонстрации гонки, связанных со временем, я не смог.

Вот абстрактная версия программы, которая НЕ дублирует проблему, но позвольте мне все же объяснить:

# Abstracted version of the original program
# that is NOT able to demo the race.
#
function foo() {
    local instance=$1

    # [A lot of logic here -
    #  all foreground commands, nothing in the background.]

    echo "$instance: test" > /tmp/foo.$instance.log        
    echo "Instance $instance ended"
}

# Launch the process in background...
#
echo "Launching instance 1"
foo 1 &

# ... and wait for it to complete.
#
echo "Waiting..."
wait
echo "Waiting... done.  (wait exited with: $?)"

# This ls command ALWAYS fails in the real
# program in the 1st while-iteration, complaining about 
# missing files, but works in the 2nd iteration!
#
# It always works in the very 1st while-iteration of the
# abstracted version.
#
while ! ls -l /tmp/foo.*; do
    :
done

В моей оригинальной программе (а НЕ в вышеупомянутой абстрактной версии) я вижу Waiting... done. (wait exited with: 0) на стандартный вывод, как я вижу в приведенной выше версии. Тем не менее, ls -l всегда терпит неудачу в оригинале, но всегда работает в вышеупомянутой абстрактной версии в самом первом while итерация цикла

Так же ls Команда терпит неудачу, несмотря на видение Instance 1 ended сообщение на стандартный вывод. Выход:

$ ./myProgram
Launching instance 1
Waiting...
Waiting... done. (wait exited with: 0)
Instance 1 ended
ls: cannot access '/tmp/foo.*': No such file or directory
/tmp/foo.1
$

Я заметил, что с циклом while можно безопасно покончить, если я поставлю sleep 1 прямо перед ls в моей оригинальной программе вот так:

# This too works in the original program:
sleep 1
ls -l /tmp/foo.*

Вопрос: почему нет wait работа как положено в моей оригинальной программе? Любые предложения, чтобы хотя бы помочь устранить проблему?

я использую bash 4.4.19 на Ubuntu 18.04.

РЕДАКТИРОВАТЬ: я только что подтвердил, что звонок wait в исходном исходящая программа завершается с кодом состояния 0,

РЕДАКТИРОВАТЬ 2: не должен Instance 1 ended сообщение появляется ДО Waiting... done. (wait exited with: 0)? Может ли это быть проблемой сброса дискового буфера / кэша ОС при работе с фоновыми процессами в bash?

РЕДАКТИРОВАТЬ 3: Если вместо while петля или sleep 1 хаки, я выдаю sync команда, тогда, вуаля, это работает! Но почему я должен сделать sync в одной программе, а в другой?

1 ответ

Решение

Я заметил, что каждый из следующих трех хаков работает, но не совсем уверен, почему:

Хак 1

while ! ls -l /tmp/foo.*; do
    :
done

Хак 2

sleep 1
ls -l /tmp/foo.*

Хак 3

sync
ls -l /tmp/foo.*

Может ли это быть проблемой сброса дискового буфера / кэша ОС, особенно при работе с фоновыми процессами, особенно в bash? Другими словами, призыв к wait кажется, возвращается, ДО того, как он очищает дисковый кэш (или, ДО ОС, сам понимает и, завершает очистку дискового кэша).

РЕДАКТИРОВАТЬ Благодаря @Jon, его было очень близкое предположение и заставило меня думать в правильном направлении, наряду с вековым, немного мудрым советом по настройке от @chepner.

Настоящая проблема: я начинал fooне прямо / явно, как показано в моей неточной абстрактной версии в моем первоначальном вопросе, а через другой launchThread функция, которая, после некоторой бухгалтерии, также сказала бы foo 1 & в его теле. И призыв к launchThread был сам по себе с суффиксом &! Так что мой wait действительно ждал launchThread и не на foo! sleep, sync, а также while просто помогали выиграть больше времени для foo чтобы завершить, поэтому введение их работало. Ниже приведена более точная демонстрация проблемы, даже если вы можете или не сможете скопировать ее в своей собственной системе (из-за различий в расписании / синхронизации в разных системах):

#!/bin/bash -u

function now() {
    date +'%Y-%m-%d %H:%M:%S'
}

function log() {
    echo "$(now) - $@" >> $logDir/log # Line 1
}

function foo() {
    local msg=$1
    log "$msg"
    echo "  foo ended"
}

function launchThread() {
    local f=$1
    shift
    "$f" "$@" &  # Line 2
}

logDir=/tmp/log

/bin/rm -rf "$logDir"
mkdir -p "$logDir"

echo "Launching foo..."
launchThread foo 'message abc' &  # Line 3

echo "Waiting for foo to finish..."
wait
echo "Waiting for foo to finish... done. (wait exited with: $?)"

ls "$logDir"/log*

Вывод вышеуказанной глючной программы:

Launching foo...
Waiting for foo to finish...
Waiting for foo to finish... done. (wait exited with: 0)
  foo ended
ls: cannot access '/tmp/log/log*': No such file or directory

Если я удалю & от ЛИБО Line 2 ИЛИ от Line 3программа работает корректно со следующими данными:

Launching foo...
Waiting for foo to finish...
  foo ended
Waiting for foo to finish... done. (wait exited with: 0)
/tmp/log/log

Программа также работает правильно, если я удаляю $(now) часть от Line 1,

Другие вопросы по тегам