Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A great example unit test for a PHP process that needs to fork to try concurrency locking
<?php
/**
* When lockMutex() happens, we aquire a semaphore
* no other processes have access while this is locked
*
* This unit test forks into 2 processes and verifies locking
*/
// Test that locking actually makes me wait if someone else has the lock.
public function testForkLockMutex() {
// tempFile used for communication between parent and child.
$tempFile = tmpfile();
// Split into two..
$pid = pcntl_fork();
if ($pid == -1) {
$this->assertEqual('shit went down. [failed to fork]', 'no shit went down');
return;
}
if ($pid) {
// I am parent.
// Wait 5000 microseconds / 5 milliseconds. Give child time to grab lock.
usleep(5000);
$timeBeforeLock = microtime(true);
// Get lock.
$this->assertTrue($this->Model->lockMutex(1));
$timeAfterLock = microtime(true);
$this->assertTrue($this->Model->unlockMutex(1));
// How much time elapsed?
$timeElapsed = $timeAfterLock - $timeBeforeLock;
// Amount of time elapsed should be (150ms - 5ms + x + y +/- z), where
// x is the fork time, y is the overhead time from unlockMutex/assert, and z is
// the "fudge factor" from OS scheduling...
// Allow range [140, 200] ms
$this->assertGreaterThanOrEqual(0.140, $timeElapsed);
$this->assertLessThanOrEqual( 0.200, $timeElapsed);
// Make sure Child passed tests and delete tempFile
fseek($tempFile, 0);
$messageFromChild = fread($tempFile, 1024);
$this->assertEqual($messageFromChild, 'pass');
fclose($tempFile);
} else {
// I am child.
// Get lock.
$lockStatus = $this->Model->lockMutex(1);
// Hold lock for 150 milliseconds / 150000 microseconds, then unlock
usleep(150000);
$unlockStatus = $this->Model->unlockMutex(1);
// Write test status to temp file, so parent can read it.
if ($lockStatus === true && $unlockStatus === true) {
fwrite($tempFile, "pass");
} else {
fwrite($tempFile, "fail");
}
// Kill self with a "kill" command.
// If we used exit or die, then CakeTestCase
// would take over and start to tear down all of the
// fixtures! Our parent still needs it.
posix_kill(posix_getpid(), SIGTERM);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment