FileMutex. Exception 'Permission denied' when use mutex in multiple threads
Code for reproduce
/** @var Mutex $mutex */
$mutex = Yii::$app->mutex;
$st = time();
$c = 0;
while(true) {
$mutex->acquire('test', 1);
$c++;
if ($st!==time()) {
$st = time();
echo $c . "\n";
$c = 0;
}
$mutex->release('test');
}
run this code 2 or more times in multiple threads
Expected no error exception
example
1400
1388
1455
...
What do you get instead?
PHP Warning 'yii\base\ErrorException' with message 'fopen(C:\Users\4erk\PhpstormProjects\yii-app\console\runtime/mutex\098f6bcd4621d373cade4e832627b4f6.lock): failed to open str
in C:\Users\4erk\PhpstormProjects\yii-app\vendor\yiisoft\yii2\mutex\FileMutex.php:105
Stack trace:
#0 [internal function]: yii\base\ErrorHandler->handleError()
#1 C:\Users\4erk\PhpstormProjects\yii-app\vendor\yiisoft\yii2\mutex\FileMutex.php(105): fopen()
#2 C:\Users\4erk\PhpstormProjects\yii-app\vendor\yiisoft\yii2\mutex\RetryAcquireTrait.php(33): yii\mutex\FileMutex->yii\mutex\{closure}()
#3 C:\Users\4erk\PhpstormProjects\yii-app\vendor\yiisoft\yii2\mutex\FileMutex.php(138): yii\mutex\FileMutex->retryAcquire()
#4 C:\Users\4erk\PhpstormProjects\yii-app\vendor\yiisoft\yii2\mutex\Mutex.php(72): yii\mutex\FileMutex->acquireLock()
#5 C:\Users\4erk\PhpstormProjects\yii-app\console\controllers\TestController.php(82): yii\mutex\Mutex->acquire()
#6 [internal function]: console\controllers\TestController->actionQ()
#7 C:\Users\4erk\PhpstormProjects\yii-app\vendor\yiisoft\yii2\base\InlineAction.php(57): call_user_func_array()
#8 C:\Users\4erk\PhpstormProjects\yii-app\vendor\yiisoft\yii2\base\Controller.php(181): yii\base\InlineAction->runWithParams()
#9 C:\Users\4erk\PhpstormProjects\yii-app\vendor\yiisoft\yii2\console\Controller.php(184): yii\base\Controller->runAction()
#10 C:\Users\4erk\PhpstormProjects\yii-app\vendor\yiisoft\yii2\base\Module.php(534): yii\console\Controller->runAction()
#11 C:\Users\4erk\PhpstormProjects\yii-app\vendor\yiisoft\yii2\console\Application.php(181): yii\base\Module->runAction()
#12 C:\Users\4erk\PhpstormProjects\yii-app\vendor\yiisoft\yii2\console\Application.php(148): yii\console\Application->runAction()
#13 C:\Users\4erk\PhpstormProjects\yii-app\vendor\yiisoft\yii2\base\Application.php(392): yii\console\Application->handleRequest()
#14 C:\Users\4erk\PhpstormProjects\yii-app\yii(23): yii\base\Application->run()
#15 {main}
Additional info
| Q | A |
|---|---|
| Yii version | 2.0.43 |
| PHP version | 7,4 |
| Operating system | win10/ubuntu20.04 |
Looks like a bug. Any idea on fixing it?
Looks like a bug. Any idea on fixing it?
I fixed it clone FileMutex.php and add @
$file = @fopen($filePath, 'wb+');
@4erk does that really fix the issue and not only hide it?
@samdark yes. fopen() return resource for file without exceptions or false
@4erk do you have time for a pull request?
I fixed it clone FileMutex.php and add
@$file = @fopen($filePath, 'wb+');
I suggest not to supress any error by using @ operator. If this fixes the issue, operator is not needed anyway. Otherwise it's easy to check for file presence e.g. if(is_file($filePath)){...}. Alternatively acquiring a file lock is also an option. Though I am not quite sure if suggested code really fixes the issue. Should be investigated deeper.
EDIT:
I checked the code - and I must take back the comment above. This is a special case in which it is never guaranteed that acquired file still exists and if yes, the only way to keek lock is retaining resource relation via fopen (though in multithread environment it will probably not work anyway). So it seems like the only way to avoid false exception is using supress operator @. The operator @ could be possibly also needed on line 114 - if flock fails.
Just a side thought- what would be use case for mutex? I know how it works and what it does, but could not come up with any use case. Any Yii2 mutex tutorial out there?
@lubosdz is_file won't really work since together with fopen it is not an atomic operation i.e. there's a time gap between the is_file and fopen that doesn't usually matter unless threading or very significant requests number.
@4erk
yes. fopen() return resource for file without exceptions or false
I don't think that's true because it returns false on error and previously you were getting an error. Thus, in the same case it will silently return false. But that should be OK judging from the current code.