Skip to content

WpfDrawingSettings is not thread safe #312

Description

@cz-dev-ge

Hi there,
first of all thank you for your great work.

Now to the issue:
We convert lots and lots of icons from SVG to XAML and do this in parallel. Unfortunately we had exception and I noticed that there is a HashSet inside WpfDrawingSettings which is changed during rendering. I did not except something like a settings container to be used as cache and thereby not being thread safe. As a workaround I create a new instance of WpfDrawingSettings for every execution. But it might be better to handle that cache differently and use the settings object as a simple read-only-settings thing.

From WpfDrawingContext:98

_registeredIds = settings[RegisteredIdKey] as HashSet<string>;
if (_registeredIds == null)
{
  _registeredIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
  settings[RegisteredIdKey] = _registeredIds;
}

Best regards,
G

PS: The stacktrace and some sample code:

Unhandled Exception: System.AggregateException: One or more errors occurred. ---> System.IndexOutOfRangeException: Index was outside the bounds of the array.
        at System.Collections.Generic.HashSet`1.AddIfNotPresent(T value)
        at SharpVectors.Renderers.Wpf.WpfDrawingContext.RegisterId(String elementId)
        at SharpVectors.Renderers.Wpf.WpfPathRendering.RenderPath(WpfDrawingRenderer renderer)
        at SharpVectors.Renderers.Wpf.WpfRenderingHelper.RenderElement(ISvgElement svgElement)
        at SharpVectors.Renderers.Wpf.WpfRenderingHelper.RenderElementChildren(ISvgElement svgElement)
        at SharpVectors.Renderers.Wpf.WpfRenderingHelper.RenderElement(ISvgElement svgElement)
        at SharpVectors.Renderers.Wpf.WpfRenderingHelper.Render(ISvgDocument docElement)
        at SharpVectors.Renderers.Wpf.WpfDrawingRenderer.Render(ISvgDocument node)
        at SharpVectors.Converters.StreamSvgConverter.ProcessFile(Stream svgStream, Stream imageStream)
        at VectorImageMerger.Converter.Convert(FileInfo file, Dictionary`2 replacements, WpfDrawingSettings settings)
        at System.Linq.Parallel.PartitionedDataSource`1.ListContiguousIndexRangeEnumerator.MoveNext(T& currentElement, Int32& currentKey)
        at System.Linq.Parallel.PipelineSpoolingTask`2.SpoolingWork()
        at System.Linq.Parallel.SpoolingTaskBase.Work()
        at System.Linq.Parallel.QueryTask.BaseWork(Object unused)
        at System.Threading.Tasks.Task.Execute()
        --- End of inner exception stack trace ---
        at System.Linq.Parallel.QueryTaskGroupState.QueryEnd(Boolean userInitiatedDispose)
        at System.Linq.Parallel.AsynchronousChannelMergeEnumerator`1.MoveNextSlowPath()
        at System.Linq.Parallel.QueryOpeningEnumerator`1.MoveNext()
        at MyConverter.Converter.ConvertSvgToDrawingGroup(IEnumerable`1 files, Dictionary`2 replacements)
        at MyConverter.Converter.GenerateResourceDictionaryFile(DirectoryInfo vectorIconDirectory, FileInfo outputResourceFile, String resourcePrefix, String mappingFile, String[] namesToSkip)
        at MyConverter.Console.Program.Main(String[] args)
// IEnumerable<FileInfo> files
var settings = new WpfDrawingSettings { IncludeRuntime = false, IgnoreRootViewbox = false };
var convertedFiles = files
			.AsParallel()
			.Select( file => Convert( file, replacements, settings ) );

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions