Skip to content

Commit 92915a6

Browse files
authored
Use flock and fflush for locking (#1356)
1 parent f2183fb commit 92915a6

File tree

1 file changed

+76
-9
lines changed

1 file changed

+76
-9
lines changed

src/Service/FileLock.php

Lines changed: 76 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
class FileLock
66
{
77
private $config;
8-
private $fs;
98

109
private $checkIntervalMs;
1110
private $timeLimit;
@@ -16,7 +15,6 @@ class FileLock
1615
public function __construct(Config $config)
1716
{
1817
$this->config = $config;
19-
$this->fs = new \Symfony\Component\Filesystem\Filesystem();
2018
$this->checkIntervalMs = 500;
2119
$this->timeLimit = 30;
2220
$this->disabled = (bool) $this->config->getWithDefault('api.disable_locks', false);
@@ -48,10 +46,7 @@ public function acquireOrWait($lockName, callable $onWait = null, callable $chec
4846
if (!\file_exists($filename)) {
4947
break;
5048
}
51-
$content = \file_get_contents($filename);
52-
if ($content === false || $content === '') {
53-
break;
54-
}
49+
$content = $this->readWithLock($filename);
5550
$lockedAt = \intval($content);
5651
if ($lockedAt === 0 || \time() >= $lockedAt + $this->timeLimit) {
5752
break;
@@ -69,7 +64,7 @@ public function acquireOrWait($lockName, callable $onWait = null, callable $chec
6964
}
7065
}
7166
}
72-
$this->fs->dumpFile($filename, (string) \time());
67+
$this->writeWithLock($filename, (string) \time());
7368
$this->locks[$lockName] = $lockName;
7469
return null;
7570
}
@@ -82,13 +77,13 @@ public function acquireOrWait($lockName, callable $onWait = null, callable $chec
8277
public function release($lockName)
8378
{
8479
if (!$this->disabled && isset($this->locks[$lockName])) {
85-
$this->fs->dumpFile($this->filename($lockName), '');
80+
$this->writeWithLock($this->filename($lockName), '');
8681
unset($this->locks[$lockName]);
8782
}
8883
}
8984

9085
/**
91-
* Destructor. Release locks that still exist on exit.
86+
* Destructor. Releases locks that still exist on exit.
9287
*/
9388
public function __destruct()
9489
{
@@ -98,6 +93,8 @@ public function __destruct()
9893
}
9994

10095
/**
96+
* Finds the filename for a lock.
97+
*
10198
* @param string $lockName
10299
* @return string
103100
*/
@@ -109,4 +106,74 @@ private function filename($lockName)
109106
. preg_replace('/[^\w_-]+/', '-', $lockName)
110107
. '.lock';
111108
}
109+
110+
/**
111+
* Reads a file using a shared lock.
112+
*
113+
* @param string $filename
114+
* @return string
115+
*/
116+
private function readWithLock($filename)
117+
{
118+
$handle = \fopen($filename, 'r');
119+
if (!$handle) {
120+
throw new \RuntimeException('Failed to open file for reading: ' . $filename);
121+
}
122+
try {
123+
if (!\flock($handle, LOCK_SH)) {
124+
\trigger_error('Failed to lock file: ' . $filename, E_USER_WARNING);
125+
}
126+
$content = \fgets($handle);
127+
if ($content === false && !\feof($handle)) {
128+
throw new \RuntimeException('Failed to read file: ' . $filename);
129+
}
130+
} finally {
131+
if (!\flock($handle, LOCK_UN)) {
132+
\trigger_error('Failed to unlock file: ' . $filename, E_USER_WARNING);
133+
}
134+
if (!\fclose($handle)) {
135+
\trigger_error('Failed to close file: ' . $filename, E_USER_WARNING);
136+
}
137+
}
138+
return $content;
139+
}
140+
141+
/**
142+
* Writes to a file using an exclusive lock.
143+
*
144+
* @param string $filename
145+
* @param string $content
146+
* @return void
147+
*/
148+
private function writeWithLock($filename, $content)
149+
{
150+
$dir = \dirname($filename);
151+
if (!\is_dir($dir)) {
152+
if (!\mkdir($dir, 0777, true)) {
153+
throw new \RuntimeException('Failed to create directory: ' . $dir);
154+
}
155+
}
156+
$handle = \fopen($filename, 'w');
157+
if (!$handle) {
158+
throw new \RuntimeException('Failed to open file for writing: ' . $filename);
159+
}
160+
try {
161+
if (!\flock($handle, LOCK_EX)) {
162+
\trigger_error('Failed to lock file: ' . $filename, E_USER_WARNING);
163+
}
164+
if (\fputs($handle, $content) === false) {
165+
throw new \RuntimeException('Failed to write to file: ' . $filename);
166+
}
167+
if (!\fflush($handle)) {
168+
\trigger_error('Failed to flush file: ' . $filename, E_USER_WARNING);
169+
}
170+
} finally {
171+
if (!\flock($handle, LOCK_UN)) {
172+
\trigger_error('Failed to unlock file: ' . $filename, E_USER_WARNING);
173+
}
174+
if (!\fclose($handle)) {
175+
\trigger_error('Failed to close file: ' . $filename, E_USER_WARNING);
176+
}
177+
}
178+
}
112179
}

0 commit comments

Comments
 (0)