Skip to content

Commit 09c696f

Browse files
committed
Expose an expiringDate and isExpired method in Lock
1 parent b118226 commit 09c696f

File tree

6 files changed

+90
-4
lines changed

6 files changed

+90
-4
lines changed

src/Symfony/Component/Lock/Key.php

+24
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
final class Key
2020
{
2121
private $resource;
22+
private $expiringDate;
2223
private $state = array();
2324

2425
/**
@@ -70,4 +71,27 @@ public function getState($stateKey)
7071
{
7172
return $this->state[$stateKey];
7273
}
74+
75+
public function resetExpiringDate()
76+
{
77+
$this->expiringDate = null;
78+
}
79+
80+
/**
81+
* @param \DateTimeImmutable $expiringDate
82+
*/
83+
public function reduceExpiringDate(\DateTimeImmutable $expiringDate)
84+
{
85+
if (null === $this->expiringDate || $this->expiringDate > $expiringDate) {
86+
$this->expiringDate = $expiringDate;
87+
}
88+
}
89+
90+
/**
91+
* @return \DateTimeImmutable
92+
*/
93+
public function getExpiringDate()
94+
{
95+
return $this->expiringDate;
96+
}
7397
}

src/Symfony/Component/Lock/Lock.php

+18
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public function refresh()
8989
}
9090

9191
try {
92+
$this->key->resetExpiringDate();
9293
$this->store->putOffExpiration($this->key, $this->ttl);
9394
$this->logger->info('Expiration defined for "{resource}" lock for "{ttl}" seconds.', array('resource' => $this->key, 'ttl' => $this->ttl));
9495
} catch (LockConflictedException $e) {
@@ -120,4 +121,21 @@ public function release()
120121
throw new LockReleasingException(sprintf('Failed to release the "%s" lock.', $this->key));
121122
}
122123
}
124+
125+
/**
126+
* @return bool
127+
*/
128+
public function isExpired()
129+
{
130+
if (null === $expireDate = $this->key->getExpiringDate()) {
131+
return false;
132+
}
133+
134+
return $expireDate <= new \DateTime();
135+
}
136+
137+
public function getExpiringDate()
138+
{
139+
return $this->key->getExpiringDate();
140+
}
123141
}

src/Symfony/Component/Lock/Store/MemcachedStore.php

+2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public function save(Key $key)
5858
{
5959
$token = $this->getToken($key);
6060

61+
$key->reduceExpiringDate(\DateTimeImmutable::createFromFormat('U.u', (string) microtime(true) + $this->initialTtl));
6162
if ($this->memcached->add((string) $key, $token, (int) ceil($this->initialTtl))) {
6263
return;
6364
}
@@ -87,6 +88,7 @@ public function putOffExpiration(Key $key, $ttl)
8788

8889
list($value, $cas) = $this->getValueAndCas($key);
8990

91+
$key->reduceExpiringDate(\DateTimeImmutable::createFromFormat('U.u', (string) microtime(true) + $ttl));
9092
// Could happens when we ask a putOff after a timeout but in luck nobody steal the lock
9193
if (\Memcached::RES_NOTFOUND === $this->memcached->getResultCode()) {
9294
if ($this->memcached->add((string) $key, $token, $ttl)) {

src/Symfony/Component/Lock/Store/RedisStore.php

+4-4
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ public function save(Key $key)
5757
end
5858
';
5959

60-
$expire = (int) ceil($this->initialTtl * 1000);
61-
if (!$this->evaluate($script, (string) $key, array($this->getToken($key), $expire))) {
60+
$key->reduceExpiringDate(\DateTimeImmutable::createFromFormat('U.u', (string) microtime(true) + $this->initialTtl));
61+
if (!$this->evaluate($script, (string) $key, array($this->getToken($key), (int) ceil($this->initialTtl * 1000)))) {
6262
throw new LockConflictedException();
6363
}
6464
}
@@ -81,8 +81,8 @@ public function putOffExpiration(Key $key, $ttl)
8181
end
8282
';
8383

84-
$expire = (int) ceil($ttl * 1000);
85-
if (!$this->evaluate($script, (string) $key, array($this->getToken($key), $expire))) {
84+
$key->reduceExpiringDate(\DateTimeImmutable::createFromFormat('U.u', (string) microtime(true) + $ttl));
85+
if (!$this->evaluate($script, (string) $key, array($this->getToken($key), (int) ceil($ttl * 1000)))) {
8686
throw new LockConflictedException();
8787
}
8888
}

src/Symfony/Component/Lock/Tests/LockTest.php

+30
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,34 @@ public function testReleaseThrowsExceptionIfNotWellDeleted()
153153

154154
$lock->release();
155155
}
156+
157+
/**
158+
* @dataProvider provideExpiredDates
159+
*/
160+
public function testExpiration($dates, $expected)
161+
{
162+
$key = new Key(uniqid(__METHOD__, true));
163+
$store = $this->getMockBuilder(StoreInterface::class)->getMock();
164+
$lock = new Lock($key, $store, 10);
165+
166+
foreach ($dates as $date) {
167+
if (null === $date) {
168+
$key->resetExpiringDate();
169+
} else {
170+
$key->reduceExpiringDate(new \DateTimeImmutable($date));
171+
}
172+
}
173+
$this->assertSame($expected, $lock->isExpired());
174+
}
175+
176+
public function provideExpiredDates()
177+
{
178+
yield array(array('yesterday'), true);
179+
yield array(array('tomorrow', 'yesterday'), true);
180+
yield array(array('yesterday', 'tomorrow'), true);
181+
182+
yield array(array(), false);
183+
yield array(array('tomorrow'), false);
184+
yield array(array('yesterday', null), false);
185+
}
156186
}

src/Symfony/Component/Lock/Tests/Store/ExpiringStoreTestTrait.php

+12
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,16 @@ public function testRefreshLock()
7575
usleep(2.1 * $clockDelay);
7676
$this->assertFalse($store->exists($key));
7777
}
78+
79+
public function testSetExpiration()
80+
{
81+
$key = new Key(uniqid(__METHOD__, true));
82+
83+
/** @var StoreInterface $store */
84+
$store = $this->getStore();
85+
86+
$store->save($key);
87+
$store->putOffExpiration($key, 1);
88+
$this->assertNotNull($key->getExpiringDate());
89+
}
7890
}

0 commit comments

Comments
 (0)