Разница между возвращаемым значением неблокирующей функции flock и аргументом $wouldblock?


Я пытаюсь понять неблокирующее стадо и аргумент wouldblock

$fp = fopen('/tmp/lock.txt', 'r+'); 
if(flock($fp, LOCK_EX | LOCK_NB, $wouldblock)) {
  echo 'Lock obtained';
}
else{
  echo 'Unable to obtain lock';
}
fclose($fp);

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

Необязательный третий аргумент имеет значение 1, если блокировка будет заблокирована (условие EWOULDBLOCK errno).

Воспроизводя в тестовой среде параллельное условие, если другой процесс получил блокировку, функция flock немедленно вернет FALSE (не блокирующий)

Так почему я должен заботиться о $заблокировал бы значение, если возвращаемое значение функции flock в неблокирующем режиме уже говорит мне, что блокировка не может быть получена?

Я не могу понять разницу между функцией flock, возвращающей FALSE, и аргументом $wouldblock, равным 1, и для чего полезен аргумент $wouldblock.

Author: nulll, 2014-06-26

1 answers

Это связано с тем, что flock() может произойти сбой не только потому, что блокировка уже получена где-то в другом месте. В таком случае он не будет блокировать ожидание снятия блокировки, но немедленно вернет значение false. Другими словами, с помощью LOCK_NB, если flock возвращает false и wouldblock=1, это означает, что он пытался получить блокировку, но она уже получена где-то в другом месте. Но если flock с LOCK_NB возвращает false и wouldblock=0, то это означает, что произошло что-то действительно плохое, и flock даже не рассматривал возможность ожидания блокировки так как это было совершенно невозможно.

Проверьте этот код ( здесь также есть суть):

<?php
// Let's create /tmp/ninja-lock1.txt ...
$fp0 = fopen('/tmp/ninja-lock1.txt', 'c');
// ... and close it imiedietly
fclose($fp0);

// File handler $fp0 was closed so flock()
// is unable to use it to gain lock.
// It will fail with wouldblock set to 0
// as it doesn't make sense to wait on unusable file handle.
//
// BTW flock() throws in such case warning "x is not a valid stream resource".
// Just for the purpose of clear output from this example
// I've suppressed it with @ - don't use @ in production
$flockResult = @flock($fp0, LOCK_EX | LOCK_NB, $wouldblock);
printf("flock=%b; wouldblock=%d (Acquire lock: %s)\n", $flockResult, $wouldblock, "failure, broken file handle");

// Two handlers for /tmp/ninja-lock2.txt
// to show flock() blocking result.
$fp1 = fopen('/tmp/ninja-lock2.txt', 'c');
$fp2 = fopen('/tmp/ninja-lock2.txt', 'c'); 

// Nobody is locking on /tmp/ninja-lock2.txt,
// so it will acquire lock and wouldblock will be 0
$flockResult = flock($fp1, LOCK_EX | LOCK_NB, $wouldblock);
printf("flock=%b; wouldblock=%d (Acquire lock: %s)\n", $flockResult, $wouldblock, "success");

// File is locked on $fp1 handle so flock won't acquire lock
// and wouldblock will be 1
$flockResult = flock($fp2, LOCK_EX | LOCK_NB, $wouldblock);
printf("flock=%b; wouldblock=%d (Acquire lock: %s)\n", $flockResult, $wouldblock, "failure, already acquired somewhere else");

// Result:
// $ php flock.php 
// flock=0; wouldblock=0 (Acquire lock: failure, broken file handle)
// flock=1; wouldblock=0 (Acquire lock: success)
// flock=0; wouldblock=1 (Acquire lock: failure, already acquired somewhere else)
?>

Также просто для того, чтобы устранить любую путаницу будущих читателей, стоит отметить, что проверка EWOULDBLOCK имеет смысл только для flock() с флагом LOCK_NB, так как в режиме блокировки это либо успех и блокировка, либо сбой и отсутствие блокировки.

Вы можете подтвердить это, заглянув в исходный код php для flock:

PHPAPI int php_flock(int fd, int operation)
#if HAVE_STRUCT_FLOCK /* {{{ */
{
    struct flock flck;
    int ret;

    flck.l_start = flck.l_len = 0;
    flck.l_whence = SEEK_SET;

    if (operation & LOCK_SH)
        flck.l_type = F_RDLCK;
    else if (operation & LOCK_EX)
        flck.l_type = F_WRLCK;
    else if (operation & LOCK_UN)
        flck.l_type = F_UNLCK;
    else {
        errno = EINVAL;
        return -1;
    }

    ret = fcntl(fd, operation & LOCK_NB ? F_SETLK : F_SETLKW, &flck);

    if (operation & LOCK_NB && ret == -1 && 
            (errno == EACCES || errno == EAGAIN))
        errno = EWOULDBLOCK;

    if (ret != -1) ret = 0;

    return ret;
}

EWOULDBLOCK устанавливается только в том случае, если operation & LOCK_NB && ret == -1 && (errno == EACCES || errno == EAGAIN).

Если вы более заинтересованный в реализации, вы также можете прочитать справочную страницу fcntl, в основном части о F_SETLK и F_SETLKW:

F_SETLK

Получить блокировку (когда l_type равен F_RDLCK или F_WRLCK) или снять блокировку (когда l_type равен F_UNLCK) для байтов, указанных в полях l_whence, l_start и l_len блокировки. Если конфликтующая блокировка удерживается другим процессом, этот вызов возвращает -1 и устанавливает значение errno в EACCES или EAGAIN.

F_SETLKW

Что касается F_SETLK, но если файл заблокирован конфликтующей блокировкой, подождите, пока эта блокировка будет снята. Если сигнал пойман во время ожидания, то вызов прерывается и (после того, как обработчик сигнала вернулся) немедленно возвращается (с возвращаемым значением -1 и значением errno, установленным в EINTR).

 9
Author: Kamil Dziedzic, 2014-07-18 10:58:05