Разница между возвращаемым значением неблокирующей функции 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.
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).