Limit-number-of-concurrent-preview-generations

Signed-off-by: Bowen Ding <dbw9580@live.com>
This commit is contained in:
Bowen Ding 2021-01-28 21:55:15 +08:00
parent 7e7284d790
commit 8e0b2e383d
2 changed files with 108 additions and 1 deletions

View File

@ -44,6 +44,8 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
class Generator {
public const SEMAPHORE_ID_ALL = 0x0a11;
public const SEMAPHORE_ID_NEW = 0x07ea;
/** @var IPreview */
private $previewManager;
@ -204,6 +206,95 @@ class Generator {
return $preview;
}
/**
* @param int $semId
* @param int $concurrency
* @return bool|resource the semaphore on success or false on failure
*/
public static function guardWithSemaphore(int $semId, int $concurrency) {
if (extension_loaded('sysvsem')) {
$sem = sem_get($semId, $concurrency);
if ($sem === false) {
return false;
}
if (!sem_acquire($sem)) {
return false;
}
return $sem;
}
return false;
}
/**
* @param resource $sem
* @return bool
*/
public static function unguardWithSemaphore($sem) {
if (!is_resource($sem)) {
return false;
}
if (extension_loaded('sysvsem')) {
return sem_release($sem);
}
return false;
}
/**
* Get the number of concurrent threads supported by the host
*
* @return int number of concurrent threads, or 0 if it cannot be determined
*/
public static function getHardwareConcurrency() {
static $width;
if (!isset($width)) {
if (is_file("/proc/cpuinfo")) {
$width = substr_count(file_get_contents("/proc/cpuinfo"), "processor");
} else {
$width = 0;
}
}
return $width;
}
/**
* Get number of concurrent preview generations from system config
*
* Two config entries, `preview_concurrency_new` and `preview_concurrency_all`,
* are available. If not set, the default values are determined with the hardware concurrency
* of the host. In case the hardware concurrency cannot be determined, or the user sets an
* invalid value, fallback values are:
* For new images whose previews do not exist and need to be generated, 4;
* For all preview generation requests, 8.
* Value of `preview_concurrency_all` should be greater than or equal to that of
* `preview_concurrency_new`, otherwise, the latter is returned.
*
* @param string $type either `preview_concurrency_new` or `preview_concurrency_all`
* @return int number of concurrent preview generations, or -1 if $type is invalid
*/
public function getNumConcurrentPreviews($type) {
static $cached = array();
if (array_key_exists($type, $cached)) {
return $cached[$type];
}
$hardwareConcurrency = self::getHardwareConcurrency();
switch ($type) {
case "preview_concurrency_all":
$fallback = $hardwareConcurrency > 0 ? $hardwareConcurrency * 2 : 8;
$concurrency_all = $this->config->getSystemValueInt($type, $fallback);
$concurrency_new = $this->getNumConcurrentPreviews("preview_concurrency_new");
$cached[$type] = max($concurrency_all, $concurrency_new);
break;
case "preview_concurrency_new":
$fallback = $hardwareConcurrency > 0 ? $hardwareConcurrency : 4;
$cached[$type] = $this->config->getSystemValueInt($type, $fallback);
break;
default:
return -1;
}
return $cached[$type];
}
/**
* @param ISimpleFolder $previewFolder
* @param File $file
@ -241,8 +332,13 @@ class Generator {
$maxWidth = (int)$this->config->getSystemValue('preview_max_x', 4096);
$maxHeight = (int)$this->config->getSystemValue('preview_max_y', 4096);
$previewConcurrency = $this->getNumConcurrentPreviews('preview_concurrency_new');
$sem = self::guardWithSemaphore(self::SEMAPHORE_ID_NEW, $previewConcurrency);
$preview = $this->helper->getThumbnail($provider, $file, $maxWidth, $maxHeight);
self::unguardWithSemaphore($sem);
if (!($preview instanceof IImage)) {
continue;
}
@ -407,6 +503,9 @@ class Generator {
throw new \InvalidArgumentException('Failed to generate preview, failed to load image');
}
$previewConcurrency = $this->getNumConcurrentPreviews('preview_concurrency_new');
$sem = self::guardWithSemaphore(self::SEMAPHORE_ID_NEW, $previewConcurrency);
if ($crop) {
if ($height !== $preview->height() && $width !== $preview->width()) {
//Resize
@ -429,6 +528,7 @@ class Generator {
$preview = $maxPreview->resizeCopy(max($width, $height));
}
self::unguardWithSemaphore($sem);
$path = $this->generatePath($width, $height, $crop, $preview->dataMimeType(), $prefix);
try {

View File

@ -187,7 +187,14 @@ class PreviewManager implements IPreview {
* @since 11.0.0 - \InvalidArgumentException was added in 12.0.0
*/
public function getPreview(File $file, $width = -1, $height = -1, $crop = false, $mode = IPreview::MODE_FILL, $mimeType = null) {
return $this->getGenerator()->getPreview($file, $width, $height, $crop, $mode, $mimeType);
$previewConcurrency = $this->getGenerator()->getNumConcurrentPreviews('preview_concurrency_all');
$sem = Generator::guardWithSemaphore(Generator::SEMAPHORE_ID_ALL, $previewConcurrency);
$preview = $this->getGenerator()->getPreview($file, $width, $height, $crop, $mode, $mimeType);
Generator::unguardWithSemaphore($sem);
return $preview;
}
/**