Как я могу сделать неблокирующий запрос на эксклюзивную блокировку с помощью File#flock?

Как мне запросить неблокирующую блокировку?

Почему File#flock Ruby не работает должным образом при отдельных попытках сделаны для блокировки файла? Блокировка файла в блоке не является правильным решением для этой проблемы, поскольку цель состоит в том, чтобы показать поведение блокировки при постоянных блокировках. Использование File#flock внутри блока снимает блокировку при выходе из блока, поэтому проблема не демонстрируется должным образом.

File#flock дает сбой по разным причинам, особенно при запросе неблокирующей блокировки. Ниже приведены некоторые примеры.

Неудачные примеры с File#flock

  • Бесконечное ожидание при использовании нескольких эксклюзивных блокировок, так как #flock не предоставляет способ истечения времени ожидания запроса на блокировку.

    # First lock succeeds.
    f1 = File.open('foo', File::RDWR|File::CREAT, 0644)
    f1.flock(File::LOCK_EX)
    # => 0
    
    # This never returns.
    f2 = File.open('foo', File::RDWR|File::CREAT, 0644)
    f2.flock(File::LOCK_EX)
    
  • Запрос на неблокирующую блокировку, когда файл монопольно заблокирован, приводит к исключению недопустимого аргумента.

    f1 = File.open('foo', File::RDWR|File::CREAT, 0644)
    f1.flock(File::LOCK_EX)
    # => 0
    
    f2 = File.open('foo', File::RDWR|File::CREAT, 0644)
    f2.flock(File::LOCK_NB)
    # => Errno::EINVAL: Invalid argument - foo
    
  • В документации сказано, что #flock «Блокирует или разблокирует файл в соответствии с locking_constant (логическим или из значений в таблице ниже)». Однако логическое ИЛИ вызывает Errno::EINVAL или Errno::EBADF в зависимости от платформы.

    f1 = File.open('foo', File::RDWR|File::CREAT, 0644)
    f1.flock(File::LOCK_EX)
    # => 0
    
    f2 = File.open('foo', File::RDWR|File::CREAT, 0644)
    f2.flock(File::LOCK_NB || File::LOCK_EX)
    # => Errno::EINVAL: Invalid argument - foo
    

Предпочтение отдается собственному решению File#flock

Хотя можно использовать модуль тайм-аута, чтобы поднять Timeout::Error, когда невозможно получить эксклюзивная блокировка, кажется, что File#flock должен решить эту проблему изначально. Итак, как на самом деле можно запросить эксклюзивную блокировку без блокировки?


person Todd A. Jacobs    schedule 08.03.2013    source источник


Ответы (1)


Используйте модуль тайм-аута с эксклюзивными блокировками

Вы можете использовать модуль таймаута, чтобы установить длительность #flock для получения эксклюзивной блокировки. В следующем примере будет вызвано Timeout::Error: execution expired, которое затем можно будет спасти любым способом, который кажется подходящим для приложения. Возврат nil по истечении таймера позволяет проверить истинность выражения #flock.

require 'timeout'

f1 = File.open('foo', File::RDWR|File::CREAT, 0644)
f1.flock(File::LOCK_EX)
# => 0

f2 = File.open('foo', File::RDWR|File::CREAT, 0644)
Timeout::timeout(0.001) { f2.flock(File::LOCK_EX) } rescue nil
# => nil

Используйте побитовое ИЛИ для попыток неблокирующей блокировки

В документации для File#flock говорится:

Блокирует или разблокирует файл в соответствии с locking_constant (логическим или из значений в таблице ниже). Возвращает false, если указан File::LOCK_NB и в противном случае операция была бы заблокирована.

Однако на самом деле метод ожидает оператора побитового ИЛИ, вместо ключевого слова логического ИЛИ, как определенного в parse.y tOROP токен парсера. В результате правильный аргумент, который позволяет #flock возвращать false в случае сбоя монопольной блокировки, на самом деле равен File::LOCK_NB|File::LOCK_EX. Например:

f1 = File.open('foo', File::RDWR|File::CREAT, 0644)
f1.flock(File::LOCK_EX|File::LOCK_NB)
# => 0

f2 = File.open('foo', File::RDWR|File::CREAT, 0644)
f2.flock(File::LOCK_NB|File::LOCK_EX)
# => false

f1.close; f2.close
# => nil

Это будет постоянно генерировать эксклюзивную блокировку, когда она доступна; в противном случае он немедленно возвращает ложное значение без накладных расходов на создание или спасение исключений. Очевидно, что именно так модуль предназначен для использования, но в документации могут быть некоторые разъяснения и дополнительные примеры, чтобы облегчить понимание.

person Todd A. Jacobs    schedule 08.03.2013