dechamps / FlexASIO

A flexible universal ASIO driver that uses the PortAudio sound I/O library. Supports WASAPI (shared and exclusive), KS, DirectSound and MME.
Other
1.29k stars 70 forks source link

Crash in PortAudioDebugRedirector #230

Closed sjoerdvankreel closed 1 month ago

sjoerdvankreel commented 1 month ago

Hi!

I'm trying to track down a problem of a user of my library xt-audio over here: https://github.com/sjoerdvankreel/xt-audio/issues/26. That project is pretty much like PortAudio, i.e. a single API with multiple backends. When FlexASIO is plugged in as an ASIO backend, i get a crash caused by an abort() over here: https://github.com/dechamps/FlexASIO/blob/57e14e8963b24f6f43f5d28f975b4b0f680e9346/src/flexasio/FlexASIOUtil/portaudio.cpp#L43. Seems like something is already initialized that shouldn't be, but I can't wrap my head around what the root cause is. Any clue as to what may be wrong here? See stacktrace below. This is for a .NET/winforms app, but I doubt if that's the problem.

    ucrtbase.dll!00007ffc980a286e() Unknown
>   FlexASIO.dll!flexasio::PortAudioDebugRedirector::PortAudioDebugRedirector(std::function<void __cdecl(std::basic_string_view<char,std::char_traits<char>>)> write) Line 43   C++
    FlexASIO.dll!flexasio::FlexASIO::FlexASIO(void * sysHandle) Line 322    C++
    [Inline Frame] FlexASIO.dll!std::_Construct_in_place(flexasio::FlexASIO &) Line 142 C++
    [Inline Frame] FlexASIO.dll!std::_Optional_construct_base<flexasio::FlexASIO>::_Construct(void * &) Line 125    C++
    [Inline Frame] FlexASIO.dll!std::optional<flexasio::FlexASIO>::emplace(void * & <_Args_0>) Line 311 C++
    [Inline Frame] FlexASIO.dll!flexasio::`anonymous-namespace'::CFlexASIO::init::__l2::<lambda>() Line 55  C++
    FlexASIO.dll!flexasio::`anonymous namespace'::CFlexASIO::Enter<void <lambda>(void)>(std::basic_string_view<char,std::char_traits<char>> context, flexasio::`anonymous-namespace'::CFlexASIO::init::__l2::void <lambda>(void) functor) Line 152  C++
    FlexASIO.dll!flexasio::`anonymous namespace'::CFlexASIO::init(void * sysHandle) Line 53 C++
    xt-audio.dll!AsioService::OpenDevice(const char * id, XtDevice * * device) Line 44  C++
    xt-audio.dll!XtServiceOpenDevice(const XtService * s, const char * id, XtDevice * * device) Line 19 C++
    [Managed to Native Transition]  
    Xt.Audio.dll!Xt.XtService.OpenDevice(string id) Line 44 C#
    Xt.Gui.exe!Xt.XtGui.GetDeviceInfo(Xt.XtService service, Xt.XtDeviceList list, int index, string defaultId) Line 205 C#
    Xt.Gui.exe!Xt.XtGui.GetDeviceInfos(Xt.XtService service, Xt.XtDeviceList list, string defaultId) Line 220   C#
    Xt.Gui.exe!Xt.XtGui.OnSystemChanged(object sender, System.EventArgs e) Line 244 C#
    System.Windows.Forms.dll!System.Windows.Forms.ComboBox.OnSelectedIndexChanged(System.EventArgs e)   Unknown
    System.Windows.Forms.dll!System.Windows.Forms.ComboBox.SelectedIndex.set(int value) Unknown
    System.Windows.Forms.dll!System.Windows.Forms.ComboBox.RefreshItems()   Unknown
    System.Windows.Forms.dll!System.Windows.Forms.ListControl.SetDataConnection(object newDataSource, System.Windows.Forms.BindingMemberInfo newDisplayMember, bool force)  Unknown
    System.Windows.Forms.dll!System.Windows.Forms.ListControl.DataSource.set(object value)  Unknown
    Xt.Gui.exe!Xt.XtGui.OnShown(System.EventArgs e) Line 126    C#
    System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(object obj) Unknown
    mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)   Unknown
    mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)   Unknown
    mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Unknown
    System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallback(System.Windows.Forms.Control.ThreadMethodEntry tme)   Unknown
    System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbacks()    Unknown
    System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m)   Unknown
    System.Windows.Forms.dll!System.Windows.Forms.Form.WndProc(ref System.Windows.Forms.Message m)  Unknown
    System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Callback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam)    Unknown
    [Native to Managed Transition]  
    user32.dll!00007ffc98ffef75()   Unknown
    user32.dll!00007ffc98ffe69d()   Unknown
    System.Windows.Forms.ni.dll!00007ffbdeeea339()  Unknown
    [Managed to Native Transition]  
    System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason, int pvLoopData)  Unknown
    System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason, System.Windows.Forms.ApplicationContext context)    Unknown
    System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) Unknown
    Xt.Gui.exe!Xt.XtGui.Main(string[] args) Line 30 C#
    [Native to Managed Transition]  
    mscoreei.dll!00007ffc7a7fd6ea() Unknown
    mscoree.dll!00007ffc7aaaac42()  Unknown
    kernel32.dll!00007ffc9a237344() Unknown
    ntdll.dll!00007ffc9a4a26b1()    Unknown
dechamps commented 1 month ago

This looks like an attempt to create two separate instances of the FlexASIO driver class in the same process, i.e. initializing FlexASIO twice in a row (without destroying the first instance first).

FlexASIO doesn't support that - it only supports up to 1 driver instance at any given time in a given process.

This is just a quick guess just from looking at the stack trace though - I haven't taken a close look at the user code. Do you think that's what may be happening in the offending app?

It may be useful to gather a log to shed more light into what may be going on - if FlexASIO is being initialized twice, that will definitely be visible in the log.

sjoerdvankreel commented 1 month ago

That's exactly what it was, thanks! Any particular reason for this design decision? From a user perspective i think it'd be nice to be able to CoCreateInstance<IASIO> multiple times and use all the canDoXyz() functions. You can still fail the second call to IASIO->createBuffers for WASAPI exclusive backend.

dechamps commented 1 month ago

Any particular reason for this design decision?

It's not really a "design decision", per se, just a consequence of FlexASIO using global state in a few places, mostly because PortAudio itself uses global state. Which is sad, but that's the way it is.

That being said, IMHO from the perspective of the driver this behavior is perfectly reasonable and cannot be considered a driver bug. Indeed, the ASIO SDK does not allow more than one driver instance to exist at any given time - it uses a global variable to store the instance and the client API is very strongly designed to revolve around a single global instance (as evidenced by the fact that ASIO client API functions do not even take a driver instance as a parameter).

The ASIO SDK defines the ASIO spec. Therefore, if the ASIO SDK makes it impossible to have more than one driver instance at any given time, then it logically follows that a spec-compliant ASIO driver is allowed to assume it will not be instantiated more than once at any given time in a given process. For this reason, I do not see this as a bug in FlexASIO - it is strictly abiding by the ASIO spec.

The reason why you're seeing this problem is because your code does not go through the ASIO SDK client library code - it bypasses it and interacts with the driver directly. If you were going through the ASIO SDK, this issue would be impossible by construction.

Now, if you want to bypass the ASIO SDK and talk to the driver directly, then that's fine, but drivers will still expect you to abide by ASIO SDK rules, so you need to make sure that your code never interacts with an ASIO driver in a way that contradicts the ASIO SDK.

I could consider a request to make FlexASIO go "above and beyond" the ASIO spec and tolerate this particular usage pattern, but to be clear that would be considered a feature request, not a bugfix. (It may also not be a trivial one - in particular I'd be worried about the thread safety implications, given PortAudio itself uses global state and is infamously thread-hostile.)

But that wouldn't really "fix" the broader problem, which is that your code makes unwarranted assumptions about what ASIO drivers will tolerate. I can "fix" FlexASIO, but that would still leave open the possibility that other ASIO drivers have the same "issue", so you may still face compatibility issues with other drivers. For this reason, a better approach would be for you to fix it on your side, i.e. make it so that your library will never attempt to create multiple concurrent instances of an ASIO driver. This way, you can guarantee compatibility with ASIO-compliant drivers, as opposed to hoping they will accept non-compliant usage.

sjoerdvankreel commented 1 month ago

Just to be clear, I'm with you on this. You play by the rules, and I don't. I am very aware of ASIO's (IMHO stupid) decision to allow only 1 driver instance per-process.

Which is exactly the reason I bypass the SDK and go for IASIO directly. Not to load multiple instances of the same driver in a single process, but it's sure nice to be able to use my e-mu card with dedicated ASIO drivers alongside f.e. my onboard realtek card with a generic ASIO driver. And as a side effect of that, yes my library also allows to instantiate multiple instances of the same asio driver.

<< as evidenced by the fact that ASIO client API functions do not even take a driver instance as a parameter It's even worse than that. The audio callback function does not even take a context parameter. I had to use runtime code generation to make the multiple-concurrent-drivers setup work, see https://github.com/sjoerdvankreel/xt-audio/blob/d34abe36d3b789bdc3f30ce92179afa33d32d382/src/core/xt/xt/backend/asio/Asio.cpp#L192.

<< I could consider a request to make FlexASIO go "above and beyond" the ASIO spec I will not make that request. Like I said, this is absolutely not a bug in FlexASIO. It's just me playing fast and loose. But it works for most asio drivers, and it's sure nice to be able to run different drivers side by side. Especially for a generic audio I/O library like xt-audio, it's nice to be able to use all your soundcards at once.

<< For this reason, a better approach would be for you to fix it on your side I hope I have convinced you by now that I will not do that :) I have put a lot of thought and time into the current design. It has obvious benefits compared to going with the SDK directly, even if it does not strictly adhere to the spec. I never envisioned this design to kick off multiple of the same ASIO drivers though, it's really there to allow concurrent access to different drivers.

Thanks for your very detailed response, and I think this is a fun discussion really :) Just to re-iterate, FlexASIO is very much within it's rights to operate as it currently does, and I have my reasons to deviate from the spec. You make no changes, I make no changes, everyone's good.

And to be very clear, xt-audio does not require to instantiate multiple IASIO drivers simultaneously. Users can very much code against it while still adhering to the asio spec. Just open one driver at a time. It's just the gui sample application that wants to open multiple of the same drivers (https://sjoerdvankreel.github.io/xt-audio/img/gui.png). The xt-audio library itself is fine (depending on usage pattern of course).

Anyway, all of the multi-instance stuff turned out to be a red herring. I wrote up a sample program over here https://github.com/sjoerdvankreel/xt-audio/issues/26 that uses FlexASIO no problem (single instance of course). For me, at least. User of my library takes a STATUS_STACK_BUFFER_OVERRUN on that sample code. I'm hoping for them to run a debug build of xt-audio to narrow down the problem.

I'm also trying to get my hands on a debug FlexASIO build using git clone --recursive, run the cmake build, compile with visual studio. But no luck, see below. Any clue?

Severity    Code    Description Project File    Line    Suppression State   Details
Error   MSB8066 Custom build for 'C:\repos\FlexASIO\build\dechamps_ASIOUtil-prefix\src\dechamps_ASIOUtil-build\_deps\asiosdk-subbuild\CMakeFiles\4d6f3f8a9f0a0bf8b184db2bbae875f5\asiosdk-populate-download.rule;C:\repos\FlexASIO\build\dechamps_ASIOUtil-prefix\src\dechamps_ASIOUtil-build\_deps\asiosdk-subbuild\CMakeFiles\4d6f3f8a9f0a0bf8b184db2bbae875f5\asiosdk-populate-update.rule;C:\repos\FlexASIO\build\dechamps_ASIOUtil-prefix\src\dechamps_ASIOUtil-build\_deps\asiosdk-subbuild\CMakeFiles\4d6f3f8a9f0a0bf8b184db2bbae875f5\asiosdk-populate-patch.rule;C:\repos\FlexASIO\build\dechamps_ASIOUtil-prefix\src\dechamps_ASIOUtil-build\_deps\asiosdk-subbuild\CMakeFiles\4d6f3f8a9f0a0bf8b184db2bbae875f5\asiosdk-populate-configure.rule;C:\repos\FlexASIO\build\dechamps_ASIOUtil-prefix\src\dechamps_ASIOUtil-build\_deps\asiosdk-subbuild\CMakeFiles\4d6f3f8a9f0a0bf8b184db2bbae875f5\asiosdk-populate-build.rule;C:\repos\FlexASIO\build\dechamps_ASIOUtil-prefix\src\dechamps_ASIOUtil-build\_deps\asiosdk-subbuild\CMakeFiles\4d6f3f8a9f0a0bf8b184db2bbae875f5\asiosdk-populate-install.rule;C:\repos\FlexASIO\build\dechamps_ASIOUtil-prefix\src\dechamps_ASIOUtil-build\_deps\asiosdk-subbuild\CMakeFiles\4d6f3f8a9f0a0bf8b184db2bbae875f5\asiosdk-populate-test.rule;C:\repos\FlexASIO\build\dechamps_ASIOUtil-prefix\src\dechamps_ASIOUtil-build\_deps\asiosdk-subbuild\CMakeFiles\0e09e38d8b0d0a56c0c94f638f655b63\asiosdk-populate-complete.rule;C:\repos\FlexASIO\build\dechamps_ASIOUtil-prefix\src\dechamps_ASIOUtil-build\_deps\asiosdk-subbuild\CMakeFiles\9a4e685319a02d73f1b5980fc27c4ae9\asiosdk-populate.rule' exited with code 1. [C:\repos\FlexASIO\build\dechamps_ASIOUtil-prefix\src\dechamps_ASIOUtil-build\_deps\asiosdk-subbuild\asiosdk-populate.vcxproj] dechamps_ASIOUtil   C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\Microsoft.CppCommon.targets   254     
dechamps commented 1 month ago

ASIO's (IMHO stupid) decision to allow only 1 driver instance per-process

For the record, I will definitely agree that many aspects of ASIO can be… problematic. This reminds me of this fun little experience I had when writing the first version of the driver:

https://github.com/dechamps/FlexASIO/blob/57e14e8963b24f6f43f5d28f975b4b0f680e9346/src/flexasio/FlexASIO/cflexasio.cpp#L22-L25

There is also the fact that the ASIO SDK docs around buffer management are so confusing that I ended up having to write my own doc to clarify what I think they meant. (You may find that doc of interest to you, by the way. Feedback is welcome.)

it's sure nice to be able to use my e-mu card with dedicated ASIO drivers alongside f.e. my onboard realtek card with a generic ASIO driver

Just FYI, loading multiple ASIO drivers at the same time can also cause its own problems - drivers can interfere with each other, especially if multiple drivers try to use the same hardware at the same time (e.g. a universal audio driver such as FlexASIO or ASIO4ALL interfering with a device's own bespoke ASIO driver). See also #86.

That being said, if the set of ASIO drivers being loaded is under the control of the end user, then I guess it's up to them to make sure they don't select a problematic combination of drivers.

I had to use runtime code generation to make the multiple-concurrent-drivers setup work, see https://github.com/sjoerdvankreel/xt-audio/blob/d34abe36d3b789bdc3f30ce92179afa33d32d382/src/core/xt/xt/backend/asio/Asio.cpp#L192.

Oh wow that's… impressive, I'll give you that!

I hope I have convinced you by now that I will not do that :) I have put a lot of thought and time into the current design. It has obvious benefits compared to going with the SDK directly, even if it does not strictly adhere to the spec. I never envisioned this design to kick off multiple of the same ASIO drivers though, it's really there to allow concurrent access to different drivers.

My suggestion would be to still allow your users to instantiate different drivers at the same time (as that can indeed be very useful, and is unlikely to cause problems that the end user wouldn't know how to fix), but maybe return an error if the user attempts to instantiate the same driver multiple times? This would compel your users to structure their code in such a way that this doesn't happen. Of course, it may also make sense to simply keep things as is and just document the issue so that your users are aware.

I can think of only one "clean" way to have multiple concurrent instances of one ASIO driver in a single process: by somehow tricking the Windows DLL loader to load the same DLL multiple times at different points of the memory address space, such that the various "instances" of the ASIO driver DLL wouldn't share the same global state. (FlexASIO would definitely work in that case.) I suspect this may be possible, perhaps using activation contexts, or somehow loading the same DLL under different file names, or something like that. I thought maybe you'd like to give it a try, given you seem to have a penchant for impressively clever hacks…

By the way, another reason to refrain from initializing ASIO drivers multiple times (concurrently or otherwise) is that some of them can actually take a while to initialize. Sadly that includes FlexASIO, which can take a while because PortAudio itself can take a while, especially if there are many audio devices to enumerate.

I'm also trying to get my hands on a debug FlexASIO build using git clone --recursive, run the cmake build, compile with visual studio. But no luck, see below. Any clue?

I don't recognize that error. I get the impression you are trying to generate a Visual Studio project from CMake? That's never going to work - the project must be built using Visual Studio's native CMake support (i.e. "Open folder", then build using CMake inside VS). You can also take inspiration from the CI workflow:

https://github.com/dechamps/FlexASIO/blob/63760946c72a499939b61bc83c7d6e5cb76d4874/.github/workflows/continuous-integration.yml#L30-L32

That being said, it's been a while since I've worked on FlexASIO, I haven't tried to build it recently. I just gave it a go and the only real breakage right now is the broken ASIO SDK download hash in ASIOUtil (dechamps/ASIOUtil@b445f3f6768260dad7bd396b80f7f8564e537e25), which I just fixed on the dev branch. Make sure to build off that branch; I just pushed 5facc5439c5a4e0edd16cbc70baa0d9e4c5aabec and I can build that just fine. And indeed CI is able to build dev (although it seems to be stuck on some subsequent test infra setup step for some reason).

sjoerdvankreel commented 1 month ago

<< This reminds me of this fun little experience I had when writing the first version of the driver:

Oh man that's nasty. Glad I never got to experience that, being "on the other side". Although, lately I've been toying around with the VST3 SDK a lot, fighting some of the same issues in Steinberg's homebrew COM implementation. Including diamond inheritence, nonetheless. Great fun debugging their QueryInterface() equivalent.

<< There is also the fact that the ASIO SDK docs around buffer management are so confusing that I ended up having to write my own doc to clarify what I think they meant. (You may find that doc of interest to you, by the way. Feedback is welcome.)

Doc seems good to me. Reminds me of me fighting ALSA's hw-direct memory-mapped buffer mode. Eventually i got a good enough understanding of it to help someone out, see https://stackoverflow.com/questions/36906981/is-mmap-what-i-need-from-alsa-to-play-simultaneous-immediate-sounds-in-my-game/37309825#37309825. But honestly I don't expect any modern day ASIO driver to come at you with directProcess=false. That's for really low IRQ levels in case you cannot process the buffer in-place. I mean I do understand their API design but you gotta remember this is 1990-s level stuff. Honestly I cannot envision any present day ASIO driver doing that. Everything these days is threaded. In fact I don't even support the !directProcess case in xt-audio, never had a complaint about it. See https://github.com/sjoerdvankreel/xt-audio/blob/d34abe36d3b789bdc3f30ce92179afa33d32d382/src/core/xt/xt/backend/asio/Stream.cpp#L70.

<< Just FYI, loading multiple ASIO drivers at the same time can also cause its own problems - drivers can interfere with each other, especially if multiple drivers try to use the same hardware at the same time This is true, but the same problem will arise with a user trying to open the same device with wasapi exclusive twice. Although it may manifest a bit more "friendly" with some error code instead of a crash.

<< Oh wow that's… impressive, I'll give you that! I sense this is a bit tongue-in-cheeck but i'll take it as a compliment anyway ;). Obviously nobody wants to write that kind of code, it's just that i don't see any alternative for making multiple (different) drivers work concurrently.

<< My suggestion would be to still allow your users to instantiate different drivers at the same time (as that can indeed be very useful, and is unlikely to cause problems that the end user wouldn't know how to fix), but maybe return an error if the user attempts to instantiate the same driver multiple times?

This makes sense, but it's also not a panacea. For example my old e-mu card came with a dedicated asio driver with a full-blown mixer GUI allowing to route various in-out with per-channel volume controls etc. And it worked perfectly with multiple processes hitting the same asio driver, so i see no reason why it wouldn't work with a single process hitting that same driver. I'm 100% convinced it has an internal audio mixer and only hits the device after that.

<< Of course, it may also make sense to simply keep things as is and just document the issue so that your users are aware.

This is my most likely course of action :) This problem is really not confined to ASIO. "Once upon a time", DirectSound was hardware-direct. Same for ASIO. Same for Linux ALSA. Any modern distro, the default ALSA driver feeds back into the PulseAudio sound mixer server which in turn feeds back into the first ALSA hardware device. That's even counting out ALSA's own distinction between HW:xyz and DMix:xyz. And WASAPI's exclusive/shared distinction. Today you cannot really tell what driver will "take over the device" just by looking at the API being used.

<< I can think of only one "clean" way to have multiple concurrent instances of one ASIO driver in a single process

I think, stuff is complicated enough already as it is :) Mind you, apart from the XtGui sample (in which I just dropped the ball, honestly), there's no real need to fire up the same driver twice.

<< By the way, another reason to refrain from initializing ASIO drivers multiple times (concurrently or otherwise) is that some of them can actually take a while to initialize

This is not limited to ASIO. WASAPI exhibits the same problem. Especially with USB drivers.

All in all, I don't see an immediate course of action for xt-audio here. Audio API landscape has changed quite a bit since i wrote that library. For one, i cannot assume that ASIO=exclusive mode. And for that reason I also cannot assume 1 ASIO driver per process. It would benefit some use cases and hurt some others. And like you, "That being said, it's been a while since I've worked on FlexASIO", same goes for xt-audio.

<< I get the impression you are trying to generate a Visual Studio project from CMake? That's never going to work - the project must be built using Visual Studio's native CMake support

Again spot-on, that's exactly what I did. I'll try to give it another go sometime soon.

Also please note like I said above, this whole multi-instance thing is NOT the root cause of my user's issue! I thinks it's fun and all to talk about it, but please don't feel obliged in any way. The current issue is some sort of memory corruption. Can be either xt-audio of FlexASIO. Once I figure it out, I'll get back to you (or not, in case it's xt-audio).

dechamps commented 1 month ago

This is my most likely course of action :) This problem is really not confined to ASIO. "Once upon a time", DirectSound was hardware-direct. Same for ASIO. Same for Linux ALSA. Any modern distro, the default ALSA driver feeds back into the PulseAudio sound mixer server which in turn feeds back into the first ALSA hardware device. That's even counting out ALSA's own distinction between HW:xyz and DMix:xyz. And WASAPI's exclusive/shared distinction. Today you cannot really tell what driver will "take over the device" just by looking at the API being used.

Well, sure, but the consequences are different. Typically, if you try to open a device that is already opened in exclusive mode somewhere else, you'll almost certainly get a clean error code back. In contrast, if you create multiple instances of the same ASIO driver in the same process, technically that's undefined behavior, so the driver is allowed to do anything in response, including crashing the entire process (which is what FlexASIO does).

I think I'll actually take an action item here, and that would be to at least make FlexASIO return a clean error with a nice error message on init() when more than one instance is being created. This won't make this use case actually work, but at least it won't crash the entire process and it will be much easier for users to troubleshoot the issue.

Also please note like I said above, this whole multi-instance thing is NOT the root cause of my user's issue!

Understood. We can look at the other issue separately, and feel free to file another ticket if it turns out the root cause is in FlexASIO.

sjoerdvankreel commented 1 month ago

<< Well, sure, but the consequences are different. Typically, if you try to open a device that is already opened in exclusive mode somewhere else, you'll almost certainly get a clean error code back. In contrast, if you create multiple instances of the same ASIO driver in the same process, technically that's undefined behavior, so the driver is allowed to do anything in response, including crashing the entire process (which is what FlexASIO does).

Agreed. Actually, if you open multiple instances of a different driver in the same process, that's already allowed to crash per-the-spec.

But as I said, multiple different drivers is my main use case. Although I'm still not sure if i should actively prohibit multiple of the same drivers. Some can handle it, some don't. It's a "nice to have" when the driver let's you do that. OTOH, it leads to github tickets like these when it doesn't. To be honest, I probably won't do anything about it untill I take a github issue that's actually caused by this. But this one ain't it (meaning I don't find the gui sample demo important enough to go fixing stuff, seeing as the library proper can be used perfectly fine, within bounds).

Closing this issue, if the other one turns out to be in FlexASIO, i'll open a new one. And congrats on this nice piece of work, btw. I'm very much supportive of anything audio-related :)

dechamps commented 1 month ago

Closing this issue

Let me just keep this open - I'm using this issue to remind me to make FlexASIO degrade more gracefully when the host attempts to instantiate multiple times (i.e. return an error instead of crashing the process).

dechamps commented 1 month ago

So, this is hilarious, but while doing some cleaning up of old issues, I stumbled upon dechamps/FlexASIO#183 which is exactly the same issue (multiple FlexASIO instances being created)… and I even already wrote the code to fix it on the dev branch (not released yet): c91fe1ef4ccc0b5820d7b2ef82cf4af435062d5d It was a year ago and I completely forgot!

@sjoerdvankreel You'll be pleased to know that the new code actually makes FlexASIO support multiple instances - as in, it actually works, as opposed to what I wrote earlier when I said I was planning to make it return an error on initialization. Looks like I took a closer look back then and concluded that it was actually fine to allow multiple instances. I still wouldn't recommend doing that though, especially in concurrent code (see #185).

dechamps commented 1 month ago

This is fixed in FlexASIO 1.10, which now allows multiple FlexASIO instances to exist simultaneously in the same process.