From: Tim Düsterhus Date: Wed, 12 Aug 2020 13:55:02 +0000 (+0200) Subject: Clear thumbnail handles as soon as possible X-Git-Tag: 5.3.0_Alpha_1~70^2 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=993976795cfc182b0fc83d35592af1193323d320;p=GitHub%2FWoltLab%2FWCF.git Clear thumbnail handles as soon as possible This reproduces when rebuilding attachment thumbnails for largish animated GIF files using ImageMagick. The ImageMagick on-disk cache quota is not sufficient to hold: 1) The original 2) The tiny thumbnail 3) The in-progress regular thumbnail The old value of the `$thumbnail` variable will only be destructed once (3) returns. But the memory is already needed during execution of (3). So this commit adjusts the code to `null` out the `$thumbnail` variable as soon as possible, instead of waiting until it goes out of scope naturally. Example stack trace from the worker: ImagickException: cache resources exhausted `/var/www/html/attachments/00/1-*snip*.bin' @ error/cache.c/OpenPixelCache/4083 in /var/www/html/lib/system/image/adapter/ImagickImageAdapter.class.php:132 Stack trace: #0 /var/www/html/lib/system/image/adapter/ImagickImageAdapter.class.php(132): Imagick->cropthumbnailimage(352, 198) #1 /var/www/html/lib/system/image/adapter/ImageAdapter.class.php(82): wcf\system\image\adapter\ImagickImageAdapter->createThumbnail(352, 198, 0) #2 /var/www/html/lib/system/upload/DefaultUploadFileSaveStrategy.class.php(323): wcf\system\image\adapter\ImageAdapter->createThumbnail(352, 198, 0) #3 /var/www/html/lib/data/attachment/AttachmentAction.class.php(226): wcf\system\upload\DefaultUploadFileSaveStrategy->generateThumbnails(Object(wcf\data\attachment\Attachment)) #4 /var/www/html/lib/data/AbstractDatabaseObjectAction.class.php(204): wcf\data\attachment\AttachmentAction->generateThumbnails() #5 /var/www/html/lib/system/worker/AttachmentRebuildDataWorker.class.php(48): wcf\data\AbstractDatabaseObjectAction->executeAction() #6 /var/www/html/lib/system/cli/command/WorkerCLICommand.class.php(152): wcf\system\worker\AttachmentRebuildDataWorker->execute() #7 /var/www/html/lib/system/CLIWCF.class.php(291): wcf\system\cli\command\WorkerCLICommand->execute(Array) #8 /var/www/html/lib/system/CLIWCF.class.php(85): wcf\system\CLIWCF->initCommands() #9 /var/www/html/cli.php(18): wcf\system\CLIWCF->__construct() #10 {main} --- diff --git a/wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php b/wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php index 7f1370c88a..445878a400 100644 --- a/wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/StyleAddForm.class.php @@ -537,6 +537,8 @@ class StyleAddForm extends AbstractForm { $options['size']['preserveAspectRatio'] ?? true ); $adapter->writeImage($thumbnail, $fileLocation); + // Clear thumbnail as soon as possible to free up the memory. + $thumbnail = null; } // Check again after scaling diff --git a/wcfsetup/install/files/lib/data/style/StyleAction.class.php b/wcfsetup/install/files/lib/data/style/StyleAction.class.php index 0d7915767a..4bf7800b3a 100644 --- a/wcfsetup/install/files/lib/data/style/StyleAction.class.php +++ b/wcfsetup/install/files/lib/data/style/StyleAction.class.php @@ -334,6 +334,8 @@ class StyleAction extends AbstractDatabaseObjectAction implements IToggleAction foreach ($images as $filename => $length) { $thumbnail = $adapter->createThumbnail($length, $length); $adapter->writeImage($thumbnail, $style->getAssetPath().$filename); + // Clear thumbnail as soon as possible to free up the memory. + $thumbnail = null; } // Create ICO file. diff --git a/wcfsetup/install/files/lib/system/upload/DefaultUploadFileSaveStrategy.class.php b/wcfsetup/install/files/lib/system/upload/DefaultUploadFileSaveStrategy.class.php index 644a135b9f..3aa6a891e0 100644 --- a/wcfsetup/install/files/lib/system/upload/DefaultUploadFileSaveStrategy.class.php +++ b/wcfsetup/install/files/lib/system/upload/DefaultUploadFileSaveStrategy.class.php @@ -322,6 +322,9 @@ class DefaultUploadFileSaveStrategy implements IUploadFileSaveStrategy { if ($file->width > $sizeData['width'] || $file->height > $sizeData['height']) { $thumbnail = $adapter->createThumbnail($sizeData['width'], $sizeData['height'], isset($sizeData['retainDimensions']) ? $sizeData['retainDimensions'] : true); $adapter->writeImage($thumbnail, $thumbnailLocation); + // Clear thumbnail as soon as possible to free up the memory for the next size. + $thumbnail = null; + if (file_exists($thumbnailLocation) && ($imageData = @getimagesize($thumbnailLocation)) !== false) { $updateData[$prefix.'Type'] = $imageData['mime']; $updateData[$prefix.'Size'] = @filesize($thumbnailLocation); diff --git a/wcfsetup/install/files/lib/system/worker/UserRebuildDataWorker.class.php b/wcfsetup/install/files/lib/system/worker/UserRebuildDataWorker.class.php index 009fb72a18..6985f891dc 100644 --- a/wcfsetup/install/files/lib/system/worker/UserRebuildDataWorker.class.php +++ b/wcfsetup/install/files/lib/system/worker/UserRebuildDataWorker.class.php @@ -205,6 +205,8 @@ class UserRebuildDataWorker extends AbstractRebuildDataWorker { $thumbnail = $adapter->createThumbnail($width, $height, false); $adapter->writeImage($thumbnail, $avatar->getLocation()); + // Clear thumbnail as soon as possible to free up the memory. + $thumbnail = null; } if ($width != UserAvatar::AVATAR_SIZE || $height != UserAvatar::AVATAR_SIZE) { diff --git a/wcfsetup/install/files/lib/util/ImageUtil.class.php b/wcfsetup/install/files/lib/util/ImageUtil.class.php index 6c5cb32f7e..9f274c050d 100644 --- a/wcfsetup/install/files/lib/util/ImageUtil.class.php +++ b/wcfsetup/install/files/lib/util/ImageUtil.class.php @@ -115,6 +115,9 @@ final class ImageUtil { $filename = FileUtil::getTemporaryFilename(); $thumbnail = $adapter->createThumbnail($maxWidth, $maxHeight, $obtainDimensions); $adapter->writeImage($thumbnail, $filename); + // Clear thumbnail as soon as possible to free up the memory. + // This is technically useless, but done for consistency. + $thumbnail = null; } return $filename;