question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Race-condition when writing 32-bit png images with text in parallel.

See original GitHub issue

Prerequisites

  • I have written a descriptive issue title
  • I have verified that I am running the latest version of ImageSharp
  • I have verified if the problem exist in both DEBUG and RELEASE mode
  • I have searched open and closed issues to ensure it has not already been reported

ImageSharp version

3.0.0-alpha.0.73

Other ImageSharp packages and versions

SixLabors.ImageSharp.Drawing: 1.0.0-beta15.3, SixLabors.Fonts: 1.0.0-beta19.5

Environment (Operating system, version and so on)

Windows 10, 22H2, Build 19045.2486

.NET Framework version

.NET 7

Description

In unit test I experienced that when running test locally, the text in images would sometimes move a tiny bit, but only when running the tests in parallel, which made me think there was a race-condition somewhere.

Below I have created the smallest reproducing example I could come up with and commented with what changes removes what looks like a race-condition to me.

To repeat to conditions here:

  • Running in parallel
  • Using Rgba32
  • Writing some text with a space and some character, e.g. " A".
  • Saving to gif, tga, or png.

Steps to Reproduce

using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

// Works for: single thread
// Fails for: two threads
const int NumberOfThreads = 2;

Parallel.ForEach(Enumerable.Range(0, NumberOfThreads), _ =>
{
    var imageLength0 = Generate();
    var imageLength1 = Generate();

    if (imageLength0 != imageLength1)
        throw new InvalidOperationException($"Expected {imageLength0} to equal {imageLength1}");
});

static long Generate()
{
    using var memoryStream = new MemoryStream();

    // Works for: Rgb24
    // Fails for: Rgba32
    using var image = new Image<Rgba32>(30, 15);

    // Works for: " ", "  ", "A" or "AA"
    // Fails for: " A", "A "
    const string Text = " A";
    image.Mutate(x => x.DrawText(Text, SystemFonts.CreateFont("Arial", 15), Color.Black, new PointF(0, 0)));

    // Works for bmp, jpeg
    // Fails for png, tga, gif
    image.SaveAsPng(memoryStream);
    return memoryStream.Length;
}
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net7.0</TargetFramework>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="SixLabors.ImageSharp" Version="3.0.0-alpha.0.73" />
        <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15.3" />
        <PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta19.5" />
    </ItemGroup>
</Project>

Images

No response

Issue Analytics

  • State:closed
  • Created 8 months ago
  • Comments:14 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
jnyrupcommented, Jan 23, 2023

Some more findings.

Removing ascender -= metric.TopSideBearing * scaleY; from https://github.com/SixLabors/Fonts/commit/39773e680338b229cc285b52a3ba97ef9bf63182 fixes my problem and all SixLabor.Fonts test at that commit pass.

Similarly removing ascender -= tsbOffset * scaleY; from current main fixes my problem and all SixLabor.Fonts test at that commit pass.

1reaction
jnyrupcommented, Jan 22, 2023

Sure! See the difference in the amount of vertical space above the text.

thread_1_image0 thread_1_image1

Read more comments on GitHub >

github_iconTop Results From Across the Web

Is it possible to have a race condition for a write-only ...
Although on the surface the answer would seem to be no, there are no race conditions, the answer is a bit more nuanced....
Read more >
C++ Tutorial: Multi-Threaded Programming - C++ Class ...
We now have a problem: a race condition. Imagine two producers both calling into put() at the same time.
Read more >
Parallel Thread Execution 8.2
One set of local 32-bit registers per processor,. A parallel data cache or shared memory that is shared by all scalar processor cores...
Read more >
Parallel computing
Parallel computing is a type of computation in which many calculations or processes are carried out simultaneously. Large problems can often be divided...
Read more >
Race condition not seen while two scripts write to a same file
Simply, the echo command triggers one write syscall which is atomic. Note that write doesn't guarantee to write all bytes it is given, ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found