Multithreading - Export image may result in exception due to race condition

When exporting images in a multi threaded environment an Aspose.Imaging.CoreExceptions.ImageSaveException may occur with the message “Image export failed.”


Looking through the inner exception (attached), there is a private static Dictionary<JpegMarkers, int> variable. When using multiple threads, a race condition can be experienced whereby an attempt to add the key twice can be made. If this were to be converted to a System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>, the issue would be averted.

I have included attachments of the inner exception and a image showing where the issue is (with the obfuscation).
Hi Richard,

Thank you for your inquiry.

We have investigated the issue at our end. We are unable to reproduce the said issue. It is therefore requested to share a working sample of multi threaded .Net application with us so that we may reproduce the issue at our end and analyze the issue faced by you.

Hi Ikram,


I’ve attached a sample project which demonstrates the issue.

Regards
Richard
Hi Richard,

Thank you for providing sample application.

We have tried to reproduce the issue at our end. Initial investigation shows that the issue persists. The issue has been logged into our system with ID IMAGING-35407. Our product team will further look into it. We will update you accordingly via this forum thread.

Hi Ikram,


Has there been any progress on this?

Regards
Richard
Hi Richard,

Thank you for your inquiry.

This is to update you that the issue against ID IMAGING-35407 has been fixed. Please download the latest version Aspose.Imaging for .NET 3.8.0 and verify at your end.

Hi Ikram,


While I can confirm that the exception does not happen any longer, the resolution is very disappointing. Creating a lock whenever an image (base class) is to be exported (and limiting ALL concurrent image exports to a single thread), is an exceedingly poor solution for a commercial library.

The “fix” severely limits the usefulness of this library in a corporate environment.

Please can this issue be reopened and an appropriate solution be provided?

Regards
Richard
Hi Richard,

Thank you for writing us back.

This is to update you that the information you have shared has been forward to the product team. Our product team will look into it. we will update you accordingly via this forum thread.

Hi Richard,

This is to update you that the issue against ticket ID IMAGING-35407 has been resolved. Please download the latest version of Aspose.Imaging and verify at your end.

Hi Ikram,


The code I presented in my last post is from the latest Apose.Imaging release (3.8.0).

From what I can see; the resolution to ticket ID IMAGING-35407 was to place a lock in the BASE image class’ Save(Stream stream, ImageOptionsBase optionsBase, Rectangle boundsRectangle) method. This was added so that multiple calls to supportedExporter.Export(image, stream, optionsBase, boundsRectangle) would not happen concurrently.

This resolution does two things:
  1. Stop the bug I posted from being exposed.
  2. Ensure that ALL image exports (not just TIFF’s) are processed in series. In other words, if working in a multi-threaded environment; no matter how many processes are working concurrently, all calls to export an image will be placed in a queue and processed one by one.
The application affected converts documents of one type to another. This is for a large corporate where high throughput is essential and the above fix effectively renders image processing with the Aspose libraries useless. I am certain that I am not be the only one adversely affected by the “fix”.

Regards
Richard
Hi Richard,

Thank for your feedback.

We have shared the information with our product team. This is to update you that your findings are correct. Multi threading is not currently supported. We have logged the exporting images in multi threaded environment feature into our system with ID IMAGINGNET-1824. Our product team will further look into it. We need more time to properly analyze the request. We will update you with the progress via this forum thread.

The issues you have found earlier (filed as IMAGINGNET-1824) have been fixed in this update.


This message was posted using Notification2Forum from Downloads module by Aspose Notifier.

The issue has not been resolved.


Steps to reproduce:
1. Update Aspose Imaging to 16.12.0 in previously uploaded sample (also updated/attached for your convenience).
2. Repeat execution of tests until there is a failure (race condition).

The below exception/stack trace is produced:
System.AggregateException : One or more errors occurred.
  ----> Aspose.Imaging.CoreExceptions.ImageSaveException : Image export failed.
  ----> Aspose.Imaging.CoreExceptions.FrameworkException : Object reference not set to an instance of an object.
  ----> System.NullReferenceException : Object reference not set to an instance of an object.
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Parallel.ForWorker[TLocal](Int32 fromInclusive, Int32 toExclusive, ParallelOptions parallelOptions, Action`1 body, Action`2 bodyWithState, Func`4 bodyWithLocal, Func`1 localInit, Action`1 localFinally)
   at System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal](IEnumerable`1 source, ParallelOptions parallelOptions, Action`1 body, Action`2 bodyWithState, Action`3 bodyWithStateAndIndex, Func`4 bodyWithStateAndLocal, Func`5 bodyWithEverything, Func`1 localInit, Action`1 localFinally)
   at System.Threading.Tasks.Parallel.ForEach[TSource](IEnumerable`1 source, Action`1 body)
   at AsposeThreadBug.AsposeImaging.MultipleThreads() in C:\AsposeThreadBug\AsposeThreadBug\AsposeImaging.cs:line 41
--ImageSaveException
   at Aspose.Imaging.Image.Save(Stream stream, ImageOptionsBase optionsBase, Rectangle boundsRectangle)
   at Aspose.Imaging.Image.Save(Stream stream, ImageOptionsBase optionsBase)
   at AsposeThreadBug.AsposeImaging.SaveFramesFromTiff(String resourceName) in C:\AsposeThreadBug\AsposeThreadBug\AsposeImaging.cs:line 55
   at AsposeThreadBug.AsposeImaging.b__1_1() in C:\AsposeThreadBug\AsposeThreadBug\AsposeImaging.cs:line 23
   at AsposeThreadBug.AsposeImaging.<>c.b__3_0(Action action) in C:\AsposeThreadBug\AsposeThreadBug\AsposeImaging.cs:line 41
   at System.Threading.Tasks.Parallel.<>c__DisplayClass17_0`1.b__1()
   at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
   at System.Threading.Tasks.Task.<>c__DisplayClass176_0.b__0(Object )
--FrameworkException
   at \u0006   .\u0002(Int32 \u0002)
   at \u0002   .\u0002(\u0005    \u0002, Rectangle \u0003, Int32 \u0005, Int32 \u0008, Int32 \u0006, Boolean \u000e)
   at \u0002   .\u0002(Int32 \u0002, Int32 \u0003, Int32 \u0005, Rectangle \u0008)
   at \u0006   .\u000f     \u0002(Rectangle \u0002)
   at \u0006   .LoadRawData(Rectangle \u0002, RawDataSettings \u0003, IPartialRawDataLoader \u0005)
   at \u0006   .LoadPartialArgb32Pixels(Rectangle \u0002, IPartialArgb32PixelLoader \u0003)
   at \u000f   .\u0002.\u000f     \u0002(Rectangle \u0002)
   at \u0008   .\u0002(\u000f    \u0002, IList`1 \u0003)
   at \u0008   .\u0002(Rectangle \u0002, \u000f    \u0003, \u0002 \u0005, Int32 \u0008, Int32 \u0006)
   at \u000f   .\u0003     \u0002(TiffStreamReader \u0002, Rectangle \u0003, IPartialArgb32PixelLoader \u0005)
   at \u000f   .LoadPartialArgb32Pixels(Rectangle \u0002, IPartialArgb32PixelLoader \u0003)
   at Aspose.Imaging.RasterImage.\u0008.\u000f     \u0002(Rectangle \u0002)
   at \u0008   .\u0002(\u000f    \u0002, IList`1 \u0003)
   at \u0008   .\u0002(Rectangle \u0002, \u000f    \u0003, \u0002 \u0005, Int32 \u0008, Int32 \u0006)
   at Aspose.Imaging.RasterImage.\u0002(Rectangle \u0002, Int32[] \u0003, Boolean \u0005, IPartialArgb32PixelLoader \u0008)
   at Aspose.Imaging.RasterImage.\u0002(Rectangle \u0002, Boolean \u0003, IPartialArgb32PixelLoader \u0005)
   at Aspose.Imaging.RasterImage.LoadPartialArgb32Pixels(Rectangle desiredRectangle, IPartialArgb32PixelLoader pixelLoader)
   at Aspose.Imaging.FileFormats.Bmp.BmpImage.\u0002.\u000f     \u0002(Rectangle \u0002)
   at \u0008   .\u0002(\u0002    \u0002, IList`1 \u0003)
   at \u0008   .\u0002(Rectangle \u0002, \u000f    \u0003, \u0002 \u0005, Int32 \u0008, Int32 \u0006)
   at Aspose.Imaging.FileFormats.Bmp.BmpImage.\u0002(RasterImage \u0002, StreamContainer \u0003, \u0006    \u0005, IColorPalette \u0008, Rectangle \u0006)
   at \u0005   .Export(Image \u0002, Stream \u0003, ImageOptionsBase \u0005, Rectangle \u0008)
   at Aspose.Imaging.Image.Save(Stream stream, ImageOptionsBase optionsBase, Rectangle boundsRectangle)
--NullReferenceException
   at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   at \u0003   .\u0002(IObjectWithBounds \u0002)
   at Aspose.Imaging.RasterImage.\u000f.Process(Rectangle \u0002, Int32[] \u0003, Point \u0005, Point \u0008)
   at \u000f   .\u0002.\u0002.Process(Rectangle \u0002, Int32[] \u0003, Point \u0005, Point \u0008)
   at \u0008   .Process(Rectangle \u0002, Byte[] \u0003, Point \u0005, Point \u0008)
   at \u0006   .\u0002(Byte[][] \u0002)
   at \u0006   .\u0002(Int32 \u0002)
Hi Richard,

Thank you for your inquiry.

This is to update you that the information has been forward to product team for further analysis. We will update you about the finding via this forum thread.

The issues you have found earlier (filed as ) have been fixed in this Aspose.Words for JasperReports 18.3 update.