Async.run_once not working in latest dev

Hey @danngreen, async threads seem not to be working in the latest dev version. I had my file loads set up as an async run once and now they fail to fire. Moving the call back to the load function itself (exact same call just not async) seems to work fine.

Hmm, I just ran my test code for AsyncThread and it performed normally. Here’s the reference code:

I haven’t changed anything in the AsyncThread code in a while. Can you check again, maybe try modelling it after the reference example and see if you get that to work?

hmmmm… maybe it’s me but i’m definitely getting intermittent issues where it’s not triggering the async function. I can’t pin down exactly why yet, but at some point I have the issue that it stops running and does not start again until I reboot.

For clarity I’m using aysnc run_once to load samples, and interestingly it doesn’t really appear to be async when I do this. It always pauses until everything is loaded before returning to the menu. Maybe the issue is from calling it within the async file load routine? (obviously referencing loadedFolder in the async thread is not great since the string is non-atomic, but I don’t think it’s going to be possible to change it fast enough for any issue to happen, but if you have a better pattern I’m all ears!)

My code is as follows:

    MetaModule::AsyncThread asyncAddFolder{this, [this]()
                                           {
                                               printf("asyncAddFolder %s\n", loadedFolder.c_str());
                                               auto paths = samples::loadFolder(loadedFolder, &sampleInfo, &sampleBuffer, true);
                                               // move the paths into the storage vector
                                               samplePaths.insert(samplePaths.end(), std::make_move_iterator(paths.begin()), std::make_move_iterator(paths.end()));
                                           }};

//CALLED FROM

 void menuLoadFolder()
    {
        printf("menuLoadFolder\n");
#if defined(METAMODULE)
        async_osdialog_file(OSDIALOG_OPEN_DIR, NULL, NULL, NULL, [this](char *path)
                            {
#else
        char *path = osdialog_file(OSDIALOG_OPEN_DIR, NULL, NULL, NULL);
#endif
                                if (path)
                                {
                                    if (doClear.load())
                                    {
                                        printf("Do Clear\n");
                                        sampleBuffer.clear();
                                        sampleInfo.clear();
                                        samplePaths.clear();
                                        // TODO - v.shrink_to_fit(); ?
                                        // See what happens with memory!
                                        lastIdx = 0;
                                        currentMinIdx = 0;
                                        playBufferIdx = 0;
                                    }

                                    loadedFolder = path;
#if defined(METAMODULE)
                                    asyncAddFolder.run_once();
#else
            auto paths = samples::loadFolder(path, &sampleInfo, &sampleBuffer, true);
            // move the paths into the storage vector
            samplePaths.insert(samplePaths.end(), std::make_move_iterator(paths.begin()), std::make_move_iterator(paths.end()));
#endif
                                }
                                free(path);
#if defined(METAMODULE)
                            });
#endif
    };

AsyncThreads are async in regards to the audio thread, not to the GUI thread. The Async runner runs in a higher priority than the GUI thread, so a big load operation might stall the GUI but the audio won’t skip or overload.

But I can see it would be useful to have something run in the background of the GUI thread as well. Hmm… I have to think about the implications of that-- first thing that comes to mind is if a user uses the GUI to delete a module or unload the patch (which deletes the modules) while its async task is running. We’d need to set it up right so that can be handled safely.

Yeah I definitely think that for loading samples, which is pretty time consuming, it would be great to get back to the UI faster, but definitely appreciate that this creates a load of complexity.

In the meantime loading async to the audio thread is certainly good enough so you can load new samples mid set without interruption.

I still need to work out why the code above stops working sometimes though. I can’t really figure it out since once it stops working it’s fully done until I restart the unit and very very occasionally it will crash the mm totally as well. In those cases I get the __kill message in the console, but I’m not exactly sure what’s causing it. It’s certainly not due to a lack of free ram as I’m always looking at that and barely using any.

Hmm… .yeah that’s not good! _kill could mean an uncaught exception (ran into that a few times, various stdlib functions call abort() → _kill() if there’s an error). Running out mem will at least print “Out of memory”.
You can pass me the code to try out, maybe with a debugger attached I can get a stack trace and see where its faulting.

1 Like

Hey @danngreen , know you’re busy with getting the rc-1 ready for v2, but I’ve sent you a github invite to see the repo with all my code in as I figured this was easier than passing around zips. I’ve left the async loads in it but locally I have had to pretty much give up on them as they seem to be pretty equally divided between working, doing nothing at all and completely locking up the MM!

oh I just saw you accepted the invite :slight_smile: Thank you! Just give me a shout if you need any more info etc… Thanks for your time on looking into this, I hope it’s not just me doing something stupid!

I started looking at it… I never intended AsyncThreads to be started from a non-audio context, but it shouldn’t make it crash. Though, I don’t think there’s any advantage to starting an AsyncThread from say, dataFromJson() since the audio thread is paused when the engine calls dataFromJson() anyways.
Have you tried replacing all these Async calls that are outside of Module::process() with just non-async regular functions?

I don’t think the initial load dataFromJSON ones have made it crash actually, it’s usually loading when you’re playing that causes issues. But yeah it’s true that I probably don’t need those.

Everything works totally fine when I comment out the async thread versions of the sample loads and just load the files in the same way as on the desktop.

The real advantage to me of using the async loads from the menu is if, for example, I’m loading a file of drum samples with the async load the first loaded sample will start playing the second it’s in memory which makes it feel really seamless to switch between folders of samples, which is amazing for playing live.