Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Thread safety #100

Closed
jihiggins opened this issue Sep 14, 2022 · 21 comments
Closed

Thread safety #100

jihiggins opened this issue Sep 14, 2022 · 21 comments

Comments

@jihiggins
Copy link

If I call this function from multiple threads at the same time (it's currently using 8 worker threads):

#include "FastNoise/FastNoise.h"

void Generate2dNoise(
        int xOffset, int yOffset, int xSize, int ySize, int seed, int octaves, float frequency, float* output
) {
    auto fnSimplex = FastNoise::New<FastNoise::OpenSimplex2>();
    auto fnFractal = FastNoise::New<FastNoise::FractalFBm>();

    fnFractal->SetSource(fnSimplex);
    fnFractal->SetOctaveCount(octaves);
    fnFractal->GenUniformGrid2D(
            output, xOffset, yOffset, xSize, ySize, frequency, seed
    );
}

I get random segfaults every once in awhile (it seems to happen pretty early on, or not at all)
Relevant part of the callstack:
image

Using a global mutex around that function solves the issue, so it seems to be something concurrency related.

(Is there any obvious issue with how I'm using the library / is FastNoise2 actually meant to be threadsafe?)

@Auburn
Copy link
Owner

Auburn commented Sep 14, 2022

There was a bug found in the thread safety of SmartNodes, the latest release (0.9.5) contains the fix for it

@jihiggins
Copy link
Author

There was a bug found in the thread safety of SmartNodes, the latest release (0.9.5) contains the fix for it

This happens with 0.9.5-alpha (from the release download)

@Auburn
Copy link
Owner

Auburn commented Sep 16, 2022

From your code above it looks like you are creating a new SmartNode per function call, ie no cross-thread access for a given node, is that true? or is the code above simplified?

@jihiggins
Copy link
Author

jihiggins commented Sep 16, 2022

That's the function being called each time (it's just copy pasted directly.) There's an FFI boundary to get to that function, which I should have mentioned in the original ticket (sorry, just forgot to include it.) No cross thread access for a given node, or at least, there shouldn't be afaict.
The FFI code is basically this right now:

    // Setup an empty array filled with 0.0s
    let mut result =
        Vec::with_capacity(x_size as usize * y_size as usize);
    result.resize(result.capacity(), 0.0);
    // RAII mutex lock guard
    let _lock = MUTEX.lock();
    unsafe {
        // Calls the previously pasted function
        ffi::Generate2dNoise(
            x_offset.into(),
            y_offset.into(),
            x_size.into(),
            y_size.into(),
            seed.into(),
            octaves.into(),
            frequency,
            result.as_mut_ptr(),
        );
    }
    // Return the result / drop the mutex lock guard
    return result;

@jihiggins
Copy link
Author

jihiggins commented Sep 16, 2022

If this is only showing up for me, I can try to repro it in just C++ later, but was curious if there were any known issues or anything

@Auburn
Copy link
Owner

Auburn commented Sep 16, 2022

Is your xSize * ySize < 8 it could be the same bug as #89 I'm working on a fix for it now

@jihiggins
Copy link
Author

jihiggins commented Sep 16, 2022

They're all 32x32 (so xSize * ySize = 1024)

I'll see if I can come up with a better set of repro steps , but it also hasn't happened again today. (Maybe I missed clearing out some build artifacts from the old version? Or could just be some weird thing affecting a race condition? It did seem to happen more often when the rest of the app was running at higher framerates, for whatever reason) edit: it happened again today, just happening less often for whatever reason

@Auburn
Copy link
Owner

Auburn commented Sep 16, 2022

Can you see what line of code it is crashing on?

@jihiggins
Copy link
Author

jihiggins commented Sep 17, 2022

It's hard to pinpoint exactly right now because can only get disassembly after it's linked etc, but it looks like it's somewhere in this:
GenUniformGrid2D or here
and then here: (from the assembly it seems like it's when the function returns? which seems weird)
OpenSimplex2 generator

@Auburn
Copy link
Owner

Auburn commented Sep 17, 2022

Could you show the assembly where it crashes? Can you get the crash on a debug build for more info?

@jihiggins
Copy link
Author

Yep! I'll see if I can setup the debug build etc, if I can't get that working I'll try setting up a purely C++ repro using the same compiler tooling etc later (probably won't have time this week for that one tho)

@jihiggins
Copy link
Author

jihiggins commented Sep 22, 2022

@Auburn
Copy link
Owner

Auburn commented Sep 24, 2022

Thanks. In FastNoise_Config.h could you change #define FASTNOISE_USE_SHARED_PTR false to true will help determine if it's a SmartNode issue or something else

@jihiggins
Copy link
Author

That's set, got a new crash from an assertion in debug mode.

First assertion:

https://github.com/Auburn/FastNoise2/blob/v0.9.5-alpha/include/FastNoise/Generators/Generator.h#L141
https://github.com/Auburn/FastNoise2/blob/v0.9.5-alpha/include/FastNoise/Generators/Fractal.h#L10
fnFractal->SetSource(fnSimplex); (from the first post)

If I continue, next assert is:

https://github.com/Auburn/FastNoise2/blob/v0.9.5-alpha/include/FastNoise/Generators/Generator.inl#L42
https://github.com/Auburn/FastNoise2/blob/v0.9.5-alpha/include/FastNoise/Generators/Generator.h#L143
https://github.com/Auburn/FastNoise2/blob/v0.9.5-alpha/include/FastNoise/Generators/Fractal.h#L10
fnFractal->SetSource(fnSimplex); (from the first post)

Then:

image
https://github.com/Auburn/FastNoise2/blob/v0.9.5-alpha/include/FastNoise/Generators/Generator.inl#L61
https://github.com/Auburn/FastNoise2/blob/v0.9.5-alpha/include/FastNoise/Generators/Fractal.inl#L24
https://github.com/Auburn/FastNoise2/blob/v0.9.5-alpha/include/FastNoise/Generators/Fractal.inl#L15
https://github.com/Auburn/FastNoise2/blob/v0.9.5-alpha/include/FastNoise/Generators/Generator.inl#L102

And finally, access violation here:

image
https://github.com/Auburn/FastNoise2/blob/v0.9.5-alpha/include/FastNoise/Generators/Generator.inl#L42
https://github.com/Auburn/FastNoise2/blob/v0.9.5-alpha/include/FastNoise/Generators/Fractal.inl#L24
https://github.com/Auburn/FastNoise2/blob/v0.9.5-alpha/include/FastNoise/Generators/Fractal.inl#L15
https://github.com/Auburn/FastNoise2/blob/v0.9.5-alpha/include/FastNoise/Generators/Generator.inl#L102

    fnFractal->GenUniformGrid2D(
            output, xOffset, yOffset, xSize, ySize, frequency, seed
    );

@jihiggins
Copy link
Author

(and then no problems next run, etc)

@Auburn
Copy link
Owner

Auburn commented Sep 28, 2022

Really weird that it only happens occasionally... in your code can you check that 'fnSimplex` is not nullptr. Did you try this?

In FastNoise_Config.h could you change #define FASTNOISE_USE_SHARED_PTR false to true will help determine if it's a SmartNode issue or something else

@jihiggins
Copy link
Author

Yep, this latest set of stuff is with that set to true. I'll add an assert for the fnSimplex check

@jihiggins
Copy link
Author

(Sorry in advance if this turns out to be some wonky FFI thing haha)

@BasBuur
Copy link

BasBuur commented Dec 5, 2022

I've ran into the exact same problem. I was able to partially track it down, for some reason dynamic_cast<VoidPtrStorageType>( base ); returns null in SetSourceSIMDPtr in Generator.inl. That's the reason of the crash. Unfortunately in my project I cannot debug it further as the problem does not occur in debug builds (likely due to the fact that debug builds run a lot slower) and the variables I'd need for debugging it get optimized out somehow (or at least don't load on breakpoints).

Either way for now I'm simply re-creating the base generator and re-setting the source in derived generators when simdGeneratorPtr is null. Works, but obviously far from ideal.

@Auburn
Copy link
Owner

Auburn commented Dec 5, 2022

That does sound weird. Are you sure the generator you are setting the source for and the source are both not null?

Can you show your workaround I'm not 100% sure what you mean

@jihiggins
Copy link
Author

jihiggins commented Jul 19, 2023

It seems like with the latest release this problem doesn't happen anymore (afaict, I've had it running without the mutex for awhile now), so it's probably fine to just close out this issue :u

Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants