Skip to content

Commit b7f635d

Browse files
committed
add fail callback for DoubleCheckedLocking::then()
1 parent 0e997bf commit b7f635d

File tree

2 files changed

+62
-12
lines changed

2 files changed

+62
-12
lines changed

src/Util/DoubleCheckedLocking.php

+16-12
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ class DoubleCheckedLocking
2424
private $check;
2525

2626
/**
27-
* @param callable(): bool $check Callback that decides if the lock should be acquired and is rechecked
28-
* after a lock has been acquired
27+
* @param callable(): bool $check Decides if a lock should be acquired and is rechecked after the lock has been acquired
2928
*/
3029
public function __construct(Mutex $mutex, callable $check)
3130
{
@@ -34,31 +33,36 @@ public function __construct(Mutex $mutex, callable $check)
3433
}
3534

3635
/**
37-
* Executes a block of code only after the check callback passes
38-
* before and after acquiring a lock.
36+
* Execute a block of code only after the check callback passes before and after acquiring a lock.
3937
*
40-
* @template T
38+
* @template TSuccess
39+
* @template TFail
4140
*
42-
* @param callable(): T $code
41+
* @param callable(): TSuccess $successFx
42+
* @param callable(): TFail $failFx
4343
*
44-
* @return T|false False if check did not pass
44+
* @return TSuccess|TFail|false False if check did not pass
4545
*
4646
* @throws \Throwable
4747
* @throws LockAcquireException
4848
* @throws LockReleaseException
4949
*/
50-
public function then(callable $code)
50+
public function then(callable $successFx, callable $failFx = null)
5151
{
5252
if (!($this->check)()) {
53-
return false;
53+
return $failFx !== null
54+
? $failFx()
55+
: false;
5456
}
5557

56-
return $this->mutex->synchronized(function () use ($code) {
58+
return $this->mutex->synchronized(function () use ($successFx, $failFx) {
5759
if (!($this->check)()) {
58-
return false;
60+
return $failFx !== null
61+
? $failFx()
62+
: false;
5963
}
6064

61-
return $code();
65+
return $successFx();
6266
});
6367
}
6468
}

tests/Util/DoubleCheckedLockingTest.php

+46
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,50 @@ public function testCodeExecuted(): void
129129
self::assertSame(1, $executedCount);
130130
self::assertSame('foo', $result);
131131
}
132+
133+
public function testFailCodeExecutedBeforeLock(): void
134+
{
135+
$this->mutex->expects(self::never())
136+
->method('synchronized');
137+
138+
$checkedLocking = new DoubleCheckedLocking($this->mutex, static function () {
139+
return false;
140+
});
141+
142+
$executedCount = 0;
143+
$result = $checkedLocking->then(static function () {
144+
self::fail();
145+
}, static function () use (&$executedCount) {
146+
++$executedCount;
147+
148+
return 'foo';
149+
});
150+
151+
self::assertSame(1, $executedCount);
152+
self::assertSame('foo', $result);
153+
}
154+
155+
public function testFailCodeExecutedAfterLock(): void
156+
{
157+
$this->mutex->expects(self::once())
158+
->method('synchronized')
159+
->willReturnCallback(static fn (\Closure $block) => $block());
160+
161+
$i = 0;
162+
$checkedLocking = new DoubleCheckedLocking($this->mutex, static function () use (&$i) {
163+
return $i++ === 0;
164+
});
165+
166+
$executedCount = 0;
167+
$result = $checkedLocking->then(static function () {
168+
self::fail();
169+
}, static function () use (&$executedCount) {
170+
++$executedCount;
171+
172+
return 'foo';
173+
});
174+
175+
self::assertSame(1, $executedCount);
176+
self::assertSame('foo', $result);
177+
}
132178
}

0 commit comments

Comments
 (0)