[HttpKernel] Use flock() for HttpCache's lock files
This commit is contained in:
parent
c4069a4016
commit
2668edd05b
@ -38,7 +38,7 @@ class Store implements StoreInterface
|
|||||||
public function __construct($root)
|
public function __construct($root)
|
||||||
{
|
{
|
||||||
$this->root = $root;
|
$this->root = $root;
|
||||||
if (!is_dir($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) {
|
if (!file_exists($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) {
|
||||||
throw new \RuntimeException(sprintf('Unable to create the store directory (%s).', $this->root));
|
throw new \RuntimeException(sprintf('Unable to create the store directory (%s).', $this->root));
|
||||||
}
|
}
|
||||||
$this->keyCache = new \SplObjectStorage();
|
$this->keyCache = new \SplObjectStorage();
|
||||||
@ -52,22 +52,15 @@ class Store implements StoreInterface
|
|||||||
{
|
{
|
||||||
// unlock everything
|
// unlock everything
|
||||||
foreach ($this->locks as $lock) {
|
foreach ($this->locks as $lock) {
|
||||||
if (file_exists($lock)) {
|
flock($lock, LOCK_UN);
|
||||||
@unlink($lock);
|
fclose($lock);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$error = error_get_last();
|
$this->locks = array();
|
||||||
if (1 === $error['type'] && false === headers_sent()) {
|
|
||||||
// send a 503
|
|
||||||
header('HTTP/1.0 503 Service Unavailable');
|
|
||||||
header('Retry-After: 10');
|
|
||||||
echo '503 Service Unavailable';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locks the cache for a given Request.
|
* Tries to lock the cache for a given Request, without blocking.
|
||||||
*
|
*
|
||||||
* @param Request $request A Request instance
|
* @param Request $request A Request instance
|
||||||
*
|
*
|
||||||
@ -75,21 +68,24 @@ class Store implements StoreInterface
|
|||||||
*/
|
*/
|
||||||
public function lock(Request $request)
|
public function lock(Request $request)
|
||||||
{
|
{
|
||||||
$path = $this->getPath($this->getCacheKey($request).'.lck');
|
$key = $this->getCacheKey($request);
|
||||||
if (!is_dir(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
|
|
||||||
return false;
|
if (!isset($this->locks[$key])) {
|
||||||
|
$path = $this->getPath($key);
|
||||||
|
if (!file_exists(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
$h = fopen($path, 'cb');
|
||||||
|
if (!flock($h, LOCK_EX | LOCK_NB)) {
|
||||||
|
fclose($h);
|
||||||
|
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->locks[$key] = $h;
|
||||||
}
|
}
|
||||||
|
|
||||||
$lock = @fopen($path, 'x');
|
return true;
|
||||||
if (false !== $lock) {
|
|
||||||
fclose($lock);
|
|
||||||
|
|
||||||
$this->locks[] = $path;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !file_exists($path) ?: $path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,17 +97,37 @@ class Store implements StoreInterface
|
|||||||
*/
|
*/
|
||||||
public function unlock(Request $request)
|
public function unlock(Request $request)
|
||||||
{
|
{
|
||||||
$file = $this->getPath($this->getCacheKey($request).'.lck');
|
$key = $this->getCacheKey($request);
|
||||||
|
|
||||||
return is_file($file) ? @unlink($file) : false;
|
if (isset($this->locks[$key])) {
|
||||||
|
flock($this->locks[$key], LOCK_UN);
|
||||||
|
fclose($this->locks[$key]);
|
||||||
|
unset($this->locks[$key]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isLocked(Request $request)
|
public function isLocked(Request $request)
|
||||||
{
|
{
|
||||||
$path = $this->getPath($this->getCacheKey($request).'.lck');
|
$key = $this->getCacheKey($request);
|
||||||
clearstatcache(true, $path);
|
|
||||||
|
|
||||||
return is_file($path);
|
if (isset($this->locks[$key])) {
|
||||||
|
return true; // shortcut if lock held by this process
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_exists($path = $this->getPath($key))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$h = fopen($path, 'rb');
|
||||||
|
flock($h, LOCK_EX | LOCK_NB, $wouldBlock);
|
||||||
|
flock($h, LOCK_UN); // release the lock we just acquired
|
||||||
|
fclose($h);
|
||||||
|
|
||||||
|
return (bool) $wouldBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,7 +160,7 @@ class Store implements StoreInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
list($req, $headers) = $match;
|
list($req, $headers) = $match;
|
||||||
if (is_file($body = $this->getPath($headers['x-content-digest'][0]))) {
|
if (file_exists($body = $this->getPath($headers['x-content-digest'][0]))) {
|
||||||
return $this->restoreResponse($headers, $body);
|
return $this->restoreResponse($headers, $body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,7 +307,7 @@ class Store implements StoreInterface
|
|||||||
*/
|
*/
|
||||||
private function getMetadata($key)
|
private function getMetadata($key)
|
||||||
{
|
{
|
||||||
if (false === $entries = $this->load($key)) {
|
if (!$entries = $this->load($key)) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,7 +323,15 @@ class Store implements StoreInterface
|
|||||||
*/
|
*/
|
||||||
public function purge($url)
|
public function purge($url)
|
||||||
{
|
{
|
||||||
if (is_file($path = $this->getPath($this->getCacheKey(Request::create($url))))) {
|
$key = $this->getCacheKey(Request::create($url));
|
||||||
|
|
||||||
|
if (isset($this->locks[$key])) {
|
||||||
|
flock($this->locks[$key], LOCK_UN);
|
||||||
|
fclose($this->locks[$key]);
|
||||||
|
unset($this->locks[$key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_exists($path = $this->getPath($key))) {
|
||||||
unlink($path);
|
unlink($path);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -327,7 +351,7 @@ class Store implements StoreInterface
|
|||||||
{
|
{
|
||||||
$path = $this->getPath($key);
|
$path = $this->getPath($key);
|
||||||
|
|
||||||
return is_file($path) ? file_get_contents($path) : false;
|
return file_exists($path) ? file_get_contents($path) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -341,23 +365,36 @@ class Store implements StoreInterface
|
|||||||
private function save($key, $data)
|
private function save($key, $data)
|
||||||
{
|
{
|
||||||
$path = $this->getPath($key);
|
$path = $this->getPath($key);
|
||||||
if (!is_dir(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$tmpFile = tempnam(dirname($path), basename($path));
|
if (isset($this->locks[$key])) {
|
||||||
if (false === $fp = @fopen($tmpFile, 'wb')) {
|
$fp = $this->locks[$key];
|
||||||
return false;
|
@ftruncate($fp, 0);
|
||||||
}
|
@fseek($fp, 0);
|
||||||
@fwrite($fp, $data);
|
$len = @fwrite($fp, $data);
|
||||||
@fclose($fp);
|
if (strlen($data) !== $len) {
|
||||||
|
@ftruncate($fp, 0);
|
||||||
|
|
||||||
if ($data != file_get_contents($tmpFile)) {
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
} else {
|
||||||
|
if (!file_exists(dirname($path)) && false === @mkdir(dirname($path), 0777, true) && !is_dir(dirname($path))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (false === @rename($tmpFile, $path)) {
|
$tmpFile = tempnam(dirname($path), basename($path));
|
||||||
return false;
|
if (false === $fp = @fopen($tmpFile, 'wb')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@fwrite($fp, $data);
|
||||||
|
@fclose($fp);
|
||||||
|
|
||||||
|
if ($data != file_get_contents($tmpFile)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === @rename($tmpFile, $path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@chmod($path, 0666 & ~umask());
|
@chmod($path, 0666 & ~umask());
|
||||||
|
@ -50,6 +50,9 @@ class HttpCacheTestCase extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
protected function tearDown()
|
protected function tearDown()
|
||||||
{
|
{
|
||||||
|
if ($this->cache) {
|
||||||
|
$this->cache->getStore()->cleanup();
|
||||||
|
}
|
||||||
$this->kernel = null;
|
$this->kernel = null;
|
||||||
$this->cache = null;
|
$this->cache = null;
|
||||||
$this->caches = null;
|
$this->caches = null;
|
||||||
|
@ -19,6 +19,10 @@ class StoreTest extends \PHPUnit_Framework_TestCase
|
|||||||
{
|
{
|
||||||
protected $request;
|
protected $request;
|
||||||
protected $response;
|
protected $response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Store
|
||||||
|
*/
|
||||||
protected $store;
|
protected $store;
|
||||||
|
|
||||||
protected function setUp()
|
protected function setUp()
|
||||||
|
Reference in New Issue
Block a user