@@ -27,6 +27,10 @@ namespace OC\Memcache; | |||
use OCP\IMemcache; | |||
class APC extends Cache implements IMemcache { | |||
use CASTrait { | |||
cas as casEmulated; | |||
} | |||
public function get($key) { | |||
$result = apc_fetch($this->getPrefix() . $key, $success); | |||
if (!$success) { | |||
@@ -89,6 +93,23 @@ class APC extends Cache implements IMemcache { | |||
return apc_dec($this->getPrefix() . $key, $step); | |||
} | |||
/** | |||
* Compare and set | |||
* | |||
* @param string $key | |||
* @param mixed $old | |||
* @param mixed $new | |||
* @return bool | |||
*/ | |||
public function cas($key, $old, $new) { | |||
// apc only does cas for ints | |||
if (is_int($old) and is_int($new)) { | |||
return apc_cas($this->getPrefix() . $key, $old, $new); | |||
} else { | |||
return $this->casEmulated($key, $old, $new); | |||
} | |||
} | |||
static public function isAvailable() { | |||
if (!extension_loaded('apc')) { | |||
return false; |
@@ -130,6 +130,22 @@ class ArrayCache extends Cache implements IMemcache { | |||
} | |||
} | |||
/** | |||
* Compare and set | |||
* | |||
* @param string $key | |||
* @param mixed $old | |||
* @param mixed $new | |||
* @return bool | |||
*/ | |||
public function cas($key, $old, $new) { | |||
if ($this->get($key) === $old) { | |||
return $this->set($key, $new); | |||
} else { | |||
return false; | |||
} | |||
} | |||
/** | |||
* {@inheritDoc} | |||
*/ |
@@ -0,0 +1,56 @@ | |||
<?php | |||
/** | |||
* @author Robin Appelman <icewind@owncloud.com> | |||
* | |||
* @copyright Copyright (c) 2015, ownCloud, Inc. | |||
* @license AGPL-3.0 | |||
* | |||
* This code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU Affero General Public License, version 3, | |||
* as published by the Free Software Foundation. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU Affero General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU Affero General Public License, version 3, | |||
* along with this program. If not, see <http://www.gnu.org/licenses/> | |||
* | |||
*/ | |||
namespace OC\Memcache; | |||
trait CASTrait { | |||
abstract public function get($key); | |||
abstract public function set($key, $value, $ttl = 0); | |||
abstract public function remove($key); | |||
abstract public function add($key, $value, $ttl = 0); | |||
/** | |||
* Compare and set | |||
* | |||
* @param string $key | |||
* @param mixed $old | |||
* @param mixed $new | |||
* @return bool | |||
*/ | |||
public function cas($key, $old, $new) { | |||
//no native cas, emulate with locking | |||
if ($this->add($key . '_lock', true)) { | |||
if ($this->get($key) === $old) { | |||
$this->set($key, $new); | |||
$this->remove($key . '_lock'); | |||
return true; | |||
} else { | |||
$this->remove($key . '_lock'); | |||
return false; | |||
} | |||
} else { | |||
return false; | |||
} | |||
} | |||
} |
@@ -27,6 +27,8 @@ namespace OC\Memcache; | |||
use OCP\IMemcache; | |||
class Memcached extends Cache implements IMemcache { | |||
use CASTrait; | |||
/** | |||
* @var \Memcached $cache | |||
*/ |
@@ -26,6 +26,7 @@ namespace OC\Memcache; | |||
use OCP\IMemcache; | |||
class Redis extends Cache implements IMemcache { | |||
use CASTrait; | |||
/** | |||
* @var \Redis $cache |
@@ -24,6 +24,7 @@ | |||
*/ | |||
namespace OC\Memcache; | |||
use OCP\IMemcache; | |||
/** | |||
@@ -92,7 +93,6 @@ class XCache extends Cache implements IMemcache { | |||
* @return int | bool | |||
*/ | |||
public function inc($key, $step = 1) { | |||
$this->add($key, 0); | |||
return xcache_inc($this->getPrefix() . $key, $step); | |||
} | |||
@@ -107,11 +107,35 @@ class XCache extends Cache implements IMemcache { | |||
return xcache_dec($this->getPrefix() . $key, $step); | |||
} | |||
/** | |||
* Compare and set | |||
* | |||
* @param string $key | |||
* @param mixed $old | |||
* @param mixed $new | |||
* @return bool | |||
*/ | |||
public function cas($key, $old, $new) { | |||
//no native cas, emulate with locking | |||
if ($this->add($key . '_lock', true)) { | |||
if ($this->get($key) === $old) { | |||
$this->set($key, $new); | |||
$this->remove($key . '_lock'); | |||
return true; | |||
} else { | |||
$this->remove($key . '_lock'); | |||
return false; | |||
} | |||
} else { | |||
return false; | |||
} | |||
} | |||
static public function isAvailable() { | |||
if (!extension_loaded('xcache')) { | |||
return false; | |||
} | |||
if (\OC::$CLI) { | |||
if (\OC::$CLI && !getenv('XCACHE_TEST')) { | |||
return false; | |||
} | |||
if (!function_exists('xcache_unset_by_prefix') && ini_get('xcache.admin.enable_auth')) { |
@@ -65,4 +65,14 @@ interface IMemcache extends ICache { | |||
* @since 8.0.0 | |||
*/ | |||
public function dec($key, $step = 1); | |||
/** | |||
* Compare and set | |||
* | |||
* @param string $key | |||
* @param mixed $old | |||
* @param mixed $new | |||
* @return bool | |||
*/ | |||
public function cas($key, $old, $new); | |||
} |
@@ -91,6 +91,18 @@ abstract class Cache extends \Test_Cache { | |||
$this->assertEquals('bar', $this->instance->get('foo')); | |||
} | |||
public function testCasNotChanged() { | |||
$this->instance->set('foo', 'bar'); | |||
$this->assertTrue($this->instance->cas('foo', 'bar', 'asd')); | |||
$this->assertEquals('asd', $this->instance->get('foo')); | |||
} | |||
public function testCasChanged() { | |||
$this->instance->set('foo', 'bar1'); | |||
$this->assertFalse($this->instance->cas('foo', 'bar', 'asd')); | |||
$this->assertEquals('bar1', $this->instance->get('foo')); | |||
} | |||
protected function tearDown() { | |||
if ($this->instance) { |