8000 [HttpKernel] Use flock() for HttpCache's lock files · nicolas-grekas/symfony@dc985f1 · GitHub
[go: up one dir, main page]

Skip to content

Commit dc985f1

Browse files
mpdudenicolas-grekas
authored andcommitted
[HttpKernel] Use flock() for HttpCache's lock files
1 parent c4069a4 commit dc985f1

File tree

3 files changed

+64
-32
lines changed

3 files changed

+64
-32
lines changed

src/Symfony/Component/HttpKernel/HttpCache/Store.php

Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class Store implements StoreInterface
3838
public function __construct($root)
3939
{
4040
$this->root = $root;
41-
if (!is_dir($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) {
41+
if (!file_exists($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) {
4242
throw new \RuntimeException(sprintf('Unable to create the store directory (%s).', $this->root));
4343
}
4444
$this->keyCache = new \SplObjectStorage();
@@ -52,44 +52,40 @@ public function cleanup()
5252
{
5353
// unlock everything
5454
foreach ($this->locks as $lock) {
55-
if (file_exists($lock)) {
56-
@unlink($lock);
57-
}
55+
flock($lock, LOCK_UN);
56+
fclose($lock);
5857
}
5958

60-
$error = error_get_last();
61-
if (1 === $error['type'] && false === headers_sent()) {
62-
// send a 503
63-
header('HTTP/1.0 503 Service Unavailable');
64-
header('Retry-After: 10');
65-
echo '503 Service Unavailable';
66-
}
59+
$this->locks = array();
6760
}
6861

6962
/**
70-
* Locks the cache for a given Request.
63+
* Tries to lock the cache for a given Request, without blocking.
7164
*
7265
* @param Request $request A Request instance
7366
*
7467
* @return bool|string true if the lock is acquired, the path to the current lock otherwise
7568
*/
7669
public function lock(Request $request)
7770
{
78-
$path = $this->getPath($this->getCacheKey($request).'.lck');
79-
if (!is_dir(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
80-
return false;
81-
}
71+
$key = $this->getCacheKey($request);
8272

83-
$lock = @fopen($path, 'x');
84-
if (false !== $lock) {
85-
fclose($lock);
73+
if (!isset($this->locks[$key])) {
74+
$path = $this->getPath($key);
75+
if (!file_exists(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
76+
return $path;
77+
}
78+
$handle = fopen($path, 'ab');
79+
if (!flock($handle, LOCK_EX | LOCK_NB)) {
80+
fclose($handle);
8681

87-
$this->locks[] = $path;
82+
return $path;
83+
}
8884

89-
return true;
85+
$this->locks[$key] = $handle;
9086
}
9187

92-
return !file_exists($path) ?: $path;
88+
return true;
9389
}
9490

9591
/**
@@ -101,17 +97,37 @@ public function lock(Request $request)
10197
*/
10298
public function unlock(Request $request)
10399
{
104-
$file = $this->getPath($this->getCacheKey($request).'.lck');
100+
$key = $this->getCacheKey($request);
101+
102+
if (isset($this->locks[$key])) {
103+
flock($this->locks[$key], LOCK_UN);
104+
fclose($this->locks[$key]);
105+
unset($this->locks[$key]);
105106

106-
return is_file($file) ? @unlink($file) : false;
107+
return true;
108+
}
109+
110+
return false;
107111
}
108112

109113
public function isLocked(Request $request)
110114
{
111-
$path = $this->getPath($this->getCacheKey($request).'.lck');
112-
clearstatcache(true, $path);
115+
$key = $this->getCacheKey($request);
116+
117+
if (isset($this->locks[$key])) {
118+
return true; // shortcut if lock held by this process
119+
}
113120

114-
return is_file($path);
121+
if (!file_exists($path = $this->getPath($key))) {
122+
return false;
123+
}
124+
125+
$handle = fopen($path, 'rb');
126+
flock($handle, LOCK_EX | LOCK_NB, $wouldBlock);
127+
flock($handle, LOCK_UN); // release the lock we just acquired
128+
fclose($handle);
129+
130+
return (bool) $wouldBlock;
115131
}
116132

117133
/**
@@ -144,7 +160,7 @@ public function lookup(Request $request)
144160
}
145161

146162
list($req, $headers) = $match;
147-
if (is_file($body = $this->getPath($headers['x-content-digest'][0]))) {
163+
if (file_exists($body = $this->getPath($headers['x-content-digest'][0]))) {
148164
return $this->restoreResponse($headers, $body);
149165
}
150166

@@ -291,7 +307,7 @@ private function requestsMatch($vary, $env1, $env2)
291307
*/
292308
private function getMetadata($key)
293309
{
294-
if (false === $entries = $this->load($key)) {
310+
if (!$entries = $this->load($key)) {
295311
return array();
296312
}
297313

@@ -307,7 +323,15 @@ private function getMetadata($key)
307323
*/
308324
public function purge($url)
309325
{
310-
if (is_file($path = $this->getPath($this->getCacheKey(Request::create($url))))) {
326+
$key = $this->getCacheKey(Request::create($url));
327+
328+
if (isset($this->locks[$key])) {
329+
flock($this->locks[$key], LOCK_UN);
330+
fclose($this->locks[$key]);
331+
unset($this->locks[$key]);
332+
}
333+
334+
if (file_exists($path = $this->getPath($key))) {
311335
unlink($path);
312336

313337
return true;
@@ -327,7 +351,7 @@ private function load($key)
327351
{
328352
$path = $this->getPath($key);
329353

330-
return is_file($path) ? file_get_contents($path) : false;
354+
return file_exists($path) ? file_get_contents($path) : false;
331355
}
332356

333357
/**
@@ -341,7 +365,7 @@ private function load($key)
341365
private function save($key, $data)
342366
{
343367
$path = $this->getPath($key);
344-
if (!is_dir(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
368+
if (!file_exists(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
345369
return false;
346370
}
347371

src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTestCase.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ protected function setUp()
5050

5151
protected function tearDown()
5252
{
53+
if ($this->cache) {
54+
$this->cache->getStore()->cleanup();
55+
}
5356
$this->kernel = null;
5457
$this->cache = null;
5558
$this->caches = null;

src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ class StoreTest extends \PHPUnit_Framework_TestCase
1919
{
2020
protected $request;
2121
protected $response;
22+
23+
/**
24+
* @var Store
25+
*/
2226
protected $store;
2327

2428
protected function setUp()
@@ -222,6 +226,7 @@ public function testOverwritesNonVaryingResponseWithStore()
222226
public function testLocking()
223227
{
224228
$req = Request::create('/test', 'get', array(), array(), array(), array('HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'));
229+
225230
$this->assertTrue($this->store->lock($req));
226231

227232
$path = $this->store->lock($req);

0 commit comments

Comments
 (0)
0