Skip to content

Commit d3a313b

Browse files
committed
#23: Improved double checked locking
- The `then` method now returns the return value of the locked code callable. - The `check` method now returns false if check fails. - Updated tests for double checked locking.
1 parent 52f296f commit d3a313b

File tree

2 files changed

+38
-10
lines changed

2 files changed

+38
-10
lines changed

classes/util/DoubleCheckedLocking.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,14 @@ public function setCheck(callable $check)
5656
* Both the check and the code execution are locked by a mutex.
5757
* Only if the check fails the method returns before acquiring a lock.
5858
*
59-
* @param callable $code The locked code.
59+
* If then returns boolean FALSE, the check did not pass before or after
60+
* acquiring the lock. A boolean FALSE can also be returned from the
61+
* synchronized code to indicate that processing did not occure or has
62+
* failed. It is up to the user to decide.
63+
*
64+
* @param callable $code The locked code.
65+
* @return mixed Boolean FALSE if check did not pass or mixed for what ever
66+
* the synchronized code returns.
6067
*
6168
* @throws \Exception The execution block or the check threw an exception.
6269
* @throws LockAcquireException The mutex could not be acquired.
@@ -65,12 +72,15 @@ public function setCheck(callable $check)
6572
public function then(callable $code)
6673
{
6774
if (!call_user_func($this->check)) {
68-
return;
75+
return false;
6976
}
70-
$this->mutex->synchronized(function () use ($code) {
71-
if (call_user_func($this->check)) {
72-
call_user_func($code);
77+
78+
return $this->mutex->synchronized(function () use ($code) {
79+
if (!call_user_func($this->check)) {
80+
return false;
7381
}
82+
83+
return call_user_func($code);
7484
});
7585
}
7686
}

tests/util/DoubleCheckedLockingTest.php

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,12 @@ public function testCheckFailsAcquiresNoLock()
4444
$this->checkedLocking->setCheck(function () {
4545
return false;
4646
});
47-
$this->checkedLocking->then(function () {
47+
$result = $this->checkedLocking->then(function () {
4848
$this->fail();
4949
});
50+
51+
// Failed check should return false.
52+
$this->assertFalse($result);
5053
}
5154

5255
/**
@@ -63,23 +66,31 @@ public function testLockedCheckAndExecution()
6366
->method("synchronized")
6467
->willReturnCallback(function (callable $block) use (&$lock) {
6568
$lock++;
66-
call_user_func($block);
69+
$result = call_user_func($block);
6770
$lock++;
71+
72+
return $result;
6873
});
6974

7075
$this->checkedLocking->setCheck(function () use (&$lock, &$check) {
7176
if ($check == 1) {
7277
$this->assertEquals(1, $lock);
7378
}
7479
$check++;
80+
7581
return true;
7682
});
7783

78-
$this->checkedLocking->then(function () use (&$lock) {
84+
$result = $this->checkedLocking->then(function () use (&$lock) {
7985
$this->assertEquals(1, $lock);
86+
87+
return 'test';
8088
});
8189

8290
$this->assertEquals(2, $check);
91+
92+
// Synchronized code should return a test string.
93+
$this->assertEquals('test', $result);
8394
}
8495

8596
/**
@@ -98,9 +109,12 @@ public function testCodeNotExecuted(callable $check)
98109
});
99110

100111
$this->checkedLocking->setCheck($check);
101-
$this->checkedLocking->then(function () {
112+
$result = $this->checkedLocking->then(function () {
102113
$this->fail();
103114
});
115+
116+
// Each failed check should return false.
117+
$this->assertFalse($result);
104118
}
105119

106120
/**
@@ -142,9 +156,13 @@ public function testCodeExecuted()
142156
});
143157

144158
$executed = false;
145-
$this->checkedLocking->then(function () use (&$executed) {
159+
$result = $this->checkedLocking->then(function () use (&$executed) {
146160
$executed = true;
161+
162+
return 'test';
147163
});
164+
148165
$this->assertTrue($executed);
166+
$this->assertEquals('test', $result);
149167
}
150168
}

0 commit comments

Comments
 (0)