Error when using multiple threads

Open
#912 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
35/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Stale
Tech stack
csharp
Domain
backend

Research direction

Start with src/ComputeSharp/Shaders/Loading/PipelineDataLoader{T}.cs, following GetPipelineData and CreatePipelineData from the reported stack trace. Run the provided three-task reproduction against ComputeSharp 3.0.2 and inspect concurrent shader execution. Done means simultaneous executions neither hang nor raise the duplicate-key exception.

Written by the indexing model from the issue text.

Description

bug :bug: untriaged :toolbox:

ComputeSharp 3.0.2
.net 9.0.203

If you run the shader execution from two threads at the same time, then one of the threads will hang forever without any errors.

If you run three threads, the process will fail:

Unhandled exception. System.ArgumentException: An item with the same key has already been added.
   at System.Runtime.CompilerServices.ConditionalWeakTable`2.Add(TKey key, TValue value)
   at ComputeSharp.Shaders.Loading.PipelineDataLoader`1.CreatePipelineData(GraphicsDevice device) in /_/src/ComputeSharp/Shaders/Loading/PipelineDataLoader{T}.cs:line 53
   at System.Runtime.CompilerServices.ConditionalWeakTable`2.GetValueLocked(TKey key, CreateValueCallback createValueCallback)
   at System.Runtime.CompilerServices.ConditionalWeakTable`2.GetValue(TKey key, CreateValueCallback createValueCallback)
   at ComputeSharp.Shaders.Loading.PipelineDataLoader`1.GetPipelineData(GraphicsDevice device) in /_/src/ComputeSharp/Shaders/Loading/PipelineDataLoader{T}.cs:line 31
   at ComputeSharp.ComputeContext.Run[T](Int32 x, Int32 y, Int32 z, T& shader) in /_/src/ComputeSharp/Shaders/ComputeContext.cs:line 166
   at ComputeSharp.ComputeContext.Run[T](Int32 x, Int32 y, T& shader) in /_/src/ComputeSharp/Shaders/ComputeContext.cs:line 139
   at ComputeSharp.ComputeContextExtensions.For[T](ComputeContext& context, Int32 x, Int32 y, T& shader) in /_/src/ComputeSharp/Shaders/Extensions/ComputeContextExtensions.cs:line 503
   at Program.<<Main>$>g__Execute|0_3(Byte[] sourceBytes, Rectangle cropRect, ImageFormat targetImageFormat, GraphicsDevice graphicsDevice) in C:\ConsoleApp\Program.cs:line 40
   at Program.<>c__DisplayClass0_0.<<Main>$>b__0() in C:\ConsoleApp\Program.cs:line 12
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)
--- End of stack trace from previous location ---
   at Program.<Main>$(String[] args) in C:\ConsoleApp\Program.cs:line 28
   at Program.<Main>(String[] args)

Here is a sample code to reproduce the error:

using System.Drawing;
using ComputeSharp;

#pragma warning disable CA1416

var graphicsDevice = GraphicsDevice.GetDefault();
var fileBytes = File.ReadAllBytes("0.png");
var rectangle = new Rectangle(100, 100, 100, 100);

var task0 = Task.Run(() =>
{
    var bytes = Execute(fileBytes, rectangle, ImageFormat.Bmp, graphicsDevice);
    Console.WriteLine($"0 {bytes.Length}");
});

var task1 = Task.Run(() =>
{
    var bytes = Execute(fileBytes, rectangle, ImageFormat.Bmp, graphicsDevice);
    Console.WriteLine($"1 {bytes.Length}");
});

var task2 = Task.Run(() =>
{
    var bytes = Execute(fileBytes, rectangle, ImageFormat.Bmp, graphicsDevice);
    Console.WriteLine($"1 {bytes.Length}");
});

await Task.WhenAll(task0, task1, task2);

Console.WriteLine("Complete");
return;

byte[] Execute(byte[] sourceBytes, Rectangle cropRect, ImageFormat targetImageFormat,  GraphicsDevice graphicsDevice)
{
    var sourceTex = graphicsDevice.LoadReadOnlyTexture2D<Bgra32, float4>(sourceBytes);
    var targetTex = graphicsDevice.AllocateReadWriteTexture2D<Bgra32, float4>(cropRect.Width, cropRect.Height);

    using (var context = graphicsDevice.CreateComputeContext())
    {
        context.For(targetTex.Width,
                    targetTex.Height,
                    new InternalCropShader(sourceTex,
                                           targetTex,
                                           cropRect.X,
                                           cropRect.Y));
    }

    var ms = new MemoryStream();
    targetTex.Save(ms, targetImageFormat);
    return ms.ToArray();
}

[ThreadGroupSize(DefaultThreadGroupSizes.XY)]
[GeneratedComputeShaderDescriptor]
internal readonly partial struct InternalCropShader
(
    IReadOnlyNormalizedTexture2D<float4> sourceTex,
    IReadWriteNormalizedTexture2D<float4> targetTex,
    int offsetX,
    int offsetY
) : IComputeShader
{
    public void Execute()
    {
        var sourceXY = ThreadIds.XY + new int2(offsetX, offsetY);
        targetTex[ThreadIds.XY] = sourceTex[sourceXY];
    }
}
Dominant language
C#
Stars
3.2k
Forks
148
Avg merge
4h 32m
Merged PRs (30d)
3

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Sergio0694/ComputeSharp

All issues in Sergio0694/ComputeSharp

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.