Last active
July 25, 2017 09:18
-
-
Save fixpunkt/5881bfeba51d64926c99d0339d3b8c02 to your computer and use it in GitHub Desktop.
Demonstration of PHP's recursive mkdir() race condition and a PHP-reimplementation that fixes it. Run ./run.sh multiple times to show the issue, then switch out the mkdir implementation and try if you can still reproduce the issue.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Recursively creates the missing parts of a directory path in a manner that is concurrency-safe. | |
* | |
* @see https://bugs.php.net/bug.php?id=35326 | |
* | |
* @param string $pathname a (nested) directory path to create | |
* @param integer $mode the permission to use | |
* @return bool true iff the directory path was successfully created | |
*/ | |
function ensureDirectoryExists($pathname, $mode) | |
{ | |
$path_segments = explode(DIRECTORY_SEPARATOR, $pathname); | |
$current_pathname = ''; | |
foreach ($path_segments as $path_segment) { | |
$current_pathname = $current_pathname . $path_segment . DIRECTORY_SEPARATOR; | |
@mkdir($current_pathname, $mode); | |
} | |
return is_dir($pathname); | |
} | |
$path = 'a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z'; | |
$result = true; | |
if (!is_dir($path)) { | |
// Comment the next line out and the one after that in to test the fix | |
$result = mkdir($path, 0777, true); | |
// $result = ensureDirectoryExists($path, 0777); | |
} | |
if ($result) { | |
echo "Success!\n"; | |
} else { | |
echo "Failed!\n"; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
NUM_PROCESSES=20 | |
rm -rf ./a | |
echo "If this doesn't fail on the first try, run it a couple of times. This problem is non-deterministic." | |
echo "Starting ${NUM_PROCESSES} parallel processes..." | |
for i in $(seq 1 $NUM_PROCESSES) | |
do | |
php mkdir-concurrency-demo.php & | |
done | |
wait | |
echo "Finished." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment