1
0
Fork 0

Filesystem: added removeDirectoryAsync() and use it in ArchiveDownloader (#9618)

This turns half of the `rm -rf ...` executions during package installs async and therefore improves performance
pull/9730/head
Markus Staab 2021-02-25 11:15:28 +01:00 committed by GitHub
parent 980eef5b68
commit a5fecc4720
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 19 deletions

View File

@ -139,9 +139,11 @@ abstract class ArchiveDownloader extends FileDownloader
} }
} }
$filesystem->removeDirectory($temporaryDir); $promise = $filesystem->removeDirectoryAsync($temporaryDir);
return $promise->then(function() use ($self, $package, $path, $temporaryDir) {
$self->removeCleanupPath($package, $temporaryDir); $self->removeCleanupPath($package, $temporaryDir);
$self->removeCleanupPath($package, $path); $self->removeCleanupPath($package, $path);
});
}, function ($e) use ($cleanup) { }, function ($e) use ($cleanup) {
$cleanup(); $cleanup();

View File

@ -12,6 +12,7 @@
namespace Composer\Util; namespace Composer\Util;
use React\Promise\Promise;
use RecursiveDirectoryIterator; use RecursiveDirectoryIterator;
use RecursiveIteratorIterator; use RecursiveIteratorIterator;
use Symfony\Component\Filesystem\Exception\IOException; use Symfony\Component\Filesystem\Exception\IOException;
@ -96,6 +97,75 @@ class Filesystem
*/ */
public function removeDirectory($directory) public function removeDirectory($directory)
{ {
$edgeCaseResult = $this->removeEdgeCases($directory);
if ($edgeCaseResult !== null) {
return $edgeCaseResult;
}
if (Platform::isWindows()) {
$cmd = sprintf('rmdir /S /Q %s', ProcessExecutor::escape(realpath($directory)));
} else {
$cmd = sprintf('rm -rf %s', ProcessExecutor::escape($directory));
}
$result = $this->getProcess()->execute($cmd, $output) === 0;
// clear stat cache because external processes aren't tracked by the php stat cache
clearstatcache();
if ($result && !is_dir($directory)) {
return true;
}
return $this->removeDirectoryPhp($directory);
}
/**
* Recursively remove a directory asynchronously
*
* Uses the process component if proc_open is enabled on the PHP
* installation.
*
* @param string $directory
* @throws \RuntimeException
* @return Promise
*/
public function removeDirectoryAsync($directory)
{
$edgeCaseResult = $this->removeEdgeCases($directory);
if ($edgeCaseResult !== null) {
return \React\Promise\resolve($edgeCaseResult);
}
if (Platform::isWindows()) {
$cmd = sprintf('rmdir /S /Q %s', ProcessExecutor::escape(realpath($directory)));
} else {
$cmd = sprintf('rm -rf %s', ProcessExecutor::escape($directory));
}
$promise = $this->getProcess()->executeAsync($cmd);
$self = $this;
return $promise->then(function ($process) use ($directory, $self) {
// clear stat cache because external processes aren't tracked by the php stat cache
clearstatcache();
if ($process->isSuccessful()) {
if (!is_dir($directory)) {
return \React\Promise\resolve(true);
}
}
return \React\Promise\resolve($self->removeDirectoryPhp($directory));
});
}
/**
* @param string $directory
*
* @return bool|null Returns null, when no edge case was hit. Otherwise a bool whether removal was successfull
*/
private function removeEdgeCases($directory) {
if ($this->isSymlinkedDirectory($directory)) { if ($this->isSymlinkedDirectory($directory)) {
return $this->unlinkSymlinkedDirectory($directory); return $this->unlinkSymlinkedDirectory($directory);
} }
@ -120,22 +190,7 @@ class Filesystem
return $this->removeDirectoryPhp($directory); return $this->removeDirectoryPhp($directory);
} }
if (Platform::isWindows()) { return null;
$cmd = sprintf('rmdir /S /Q %s', ProcessExecutor::escape(realpath($directory)));
} else {
$cmd = sprintf('rm -rf %s', ProcessExecutor::escape($directory));
}
$result = $this->getProcess()->execute($cmd, $output) === 0;
// clear stat cache because external processes aren't tracked by the php stat cache
clearstatcache();
if ($result && !is_dir($directory)) {
return true;
}
return $this->removeDirectoryPhp($directory);
} }
/** /**