Skip to content

Instantly share code, notes, and snippets.

@fixpunkt
Last active July 25, 2017 09:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fixpunkt/5881bfeba51d64926c99d0339d3b8c02 to your computer and use it in GitHub Desktop.
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.
<?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";
}
#!/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