Author Topic: ASIO out thread suicide  (Read 17615 times)

wuschel

  • Posts: 9
ASIO out thread suicide
« on: 4 Aug '08 - 22:57 »
The ASIO output plugin crashes when used with the new ASIO4ALL 2.9 beta, as soon as you press "stop".

Investigation shows that the thread used to CoCreateInstance() the IASIO will commit suicide after successfully calling ASIOstart(). Audio is now playing, but the thread which owns the IASIO and has the duty to process Windows messages - at least for the lifetime of the COM object it created - that thread is gone.

If the user presses "Stop", this will result in ASIOstop() being called from another thread (the main thread, to be precise), which is bad enough already, and posting a message to the original IASIO owner thread will stall forever, because that one's long gone.

For reference, here is the relevant quote from scripture:

http://msdn.microsoft.com/en-us/library/ms680112(VS.85).aspx

Ian @ un4seen

  • Administrator
  • Posts: 20393
Re: ASIO out thread suicide
« Reply #1 on: 5 Aug '08 - 13:31 »
Here's an update to try...

   www.un4seen.com/stuff/xmp-asio.dll

wuschel

  • Posts: 9
Re: ASIO out thread suicide
« Reply #2 on: 5 Aug '08 - 14:40 »
Here's an update to try...

That went quick! Unfortunately, no luck yet! This time, the owner thread of the COM object seems to die even earlier and ASIOinit() is called with another ThreadID already. Previously, all would go well until after ASIOstart().

kakuyoshi

  • Posts: 30
Re: ASIO out thread suicide
« Reply #3 on: 5 Aug '08 - 15:15 »
Hi,all.
A pattern I can play it and is impossible.

There is not reaction even if I click a play button without I play it,
and it being started.I am in a condition that a process was left even
if I finish XMPlay.

An ASIO4ALL SYSTEM Tray icon is displayed when I click ASIO4ALL
displayed by Devices/drivers of Option and stuff → Output → ASIO.
If ASIO4ALL SYSTEM Tray icon is displayed by SYSTEM Tray, I seem
to be able to play it.

kakuyoshi

Ian @ un4seen

  • Administrator
  • Posts: 20393
Re: ASIO out thread suicide
« Reply #4 on: 5 Aug '08 - 16:23 »
This time, the owner thread of the COM object seems to die even earlier and ASIOinit() is called with another ThreadID already.

That's strange, the thread should stay alive for the duration of the ASIO output. Here's another little modification to try, with the "init" call moved to that thread...

   www.un4seen.com/stuff/xmp-asio.dll

If it's still no good, please try opening a file with another output enabled (eg. default), and then switch to the ASIO output afterwards.

wuschel

  • Posts: 9
Re: ASIO out thread suicide
« Reply #5 on: 5 Aug '08 - 17:11 »
That's strange, the thread should stay alive for the duration of the ASIO output. Here's another little modification to try, with the "init" call moved to that thread...

ASIOinit now runs in the proper thread, but not so anything thereafter:

Query Interface            Thread: 00000254
ASIOinit               Thread: 00000254
ASIOgetChannels            Thread: 00000924
ASIOsetSampleRate         Thread: 00000924

... and that was it, no further IASIO calls made. Stuck at this point.

Note that for Single Threaded Apartment, there must be only one thread accessing the interface!

Ian @ un4seen

  • Administrator
  • Posts: 20393
Re: ASIO out thread suicide
« Reply #6 on: 5 Aug '08 - 18:07 »
I'm not so sure that the thread from which the calls are being made is the issue. I think it may be that the "owner" thread doesn't have a message queue, so here's an update to try, in which it does...

   www.un4seen.com/stuff/xmp-asio.dll

wuschel

  • Posts: 9
Re: ASIO out thread suicide
« Reply #7 on: 5 Aug '08 - 18:55 »
I'm not so sure that the thread from which the calls are being made is the issue. I think it may be that the "owner" thread doesn't have a message queue, so here's an update to try, in which it does...

This certainly improves things a lot! The only remaining issue I found is that it still self-destructs in the course of a kASIOResetRequest message. (e.g, open control panel from tray, change buffer size -> gone)

The threading model violations are still there and may cause problems elsewhere, but do not appear to matter as far as ASIO4ALL.

Ian @ un4seen

  • Administrator
  • Posts: 20393
Re: ASIO out thread suicide
« Reply #8 on: 6 Aug '08 - 16:52 »
I gave the ASIO4ALL 2.9 beta a try, and did find that it locks-up when moving the buffer size slider. Is that what you're referring to? It seems to be caused by the "owner" thread being stuck in the ASIO4ALL control panel, ie. the thread can't process any further messages until the control panel is closed. So it can't respond when the plugin tries to re-initialize the driver (in response to the kASIOResetRequest message).

Do you have any idea why that is? Perhaps the ASIO4ALL control panel expects to be handled by the main thread?

wuschel

  • Posts: 9
Re: ASIO out thread suicide
« Reply #9 on: 7 Aug '08 - 01:13 »
I gave the ASIO4ALL 2.9 beta a try, and did find that it locks-up when moving the buffer size slider. Is that what you're referring to? It seems to be caused by the "owner" thread being stuck in the ASIO4ALL control panel, ie. the thread can't process any further messages until the control panel is closed. So it can't respond when the plugin tries to re-initialize the driver (in response to the kASIOResetRequest message).

Do you have any idea why that is? Perhaps the ASIO4ALL control panel expects to be handled by the main thread?

Hmm, its a Single Threaded Apartment, after all. The only "legit" context of the kASIOResetRequest message would be the "owner" thread. The ASIOmessage() callback is, of course, expected to return at some point, which leaves a compliant host with not too many options. Hence, a usual response to kASIOResetRequest would be

  • ASIOstop() (if running)
  • ASIOdisposeBuffers() (if "prepared")
  • ASIOinit()
  • ASIOget all the stuff()
  • ASIOcreateBuffers()
  • ASIOget and set some more stuff()
  • ASIOstart()

... and return to the caller afterwards. I can see that this can cause some headaches if more than one worker thread is involved.

Ian @ un4seen

  • Administrator
  • Posts: 20393
Re: ASIO out thread suicide
« Reply #10 on: 7 Aug '08 - 14:52 »
The plugin does basically follow that sequence in response to a kASIOResetRequest message, with the additional step of releasing the driver (eg. "ASIOExit") prior to "ASIOinit". One big difference though, is that the "asioMessage" function won't wait until afterwards before returning to the caller. The ASIO documentation states that the driver should not be re-initialized in the asioMessage function, so a new thread is created to carry out that task, and the thread that sent the kASIOResetRequest message is free to go about its business in the meantime.

I have tried modifying the plugin to send a message to the "owner" thread (instead of creating a new thread), but the problem is that the "owner" thread can't process any messages until the ASIO4ALL control panel is closed. Perhaps you could try to reproduce that in your own tests? I guess the fact that the control panel isn't owned by the main thread may have something to do with it, so you could throw that into the mix.

Btw, I have also tried moving all driver calls to the "owner" thread, but again that is foiled by the thread not being able to process messages until the ASIO4ALL control panel is closed.

wuschel

  • Posts: 9
Re: ASIO out thread suicide
« Reply #11 on: 7 Aug '08 - 19:15 »
The reason why you should not re-initialize the driver during a resetRequest message presumably comes from the possibility that it could have been issued by e.g. the driver owned audio thread.

There are not a lot of options as far as meeting this requirement and, at the same time, not violating the COM requirements. What works and what is actually used where it works is to PostMessage() the reset request (or any other ASIOmessage for that matter) to the message queue of the owner thread, where a WndProc will receive it and perform the reinitialization.

For messages issued by the control panel, this would be the equivalent of a SendMessage(), but trigger a thread switch otherwise.

Ian @ un4seen

  • Administrator
  • Posts: 20393
Re: ASIO out thread suicide
« Reply #12 on: 8 Aug '08 - 12:47 »
There are not a lot of options as far as meeting this requirement and, at the same time, not violating the COM requirements. What works and what is actually used where it works is to PostMessage() the reset request (or any other ASIOmessage for that matter) to the message queue of the owner thread, where a WndProc will receive it and perform the reinitialization.

But the "owner" thread is unable to process any messages because it is stuck in the ASIO4ALL control panel. This is the thread's call-stack once the ASIO4ALL control panel is opened (via the tray icon)...

   NTDLL! 7c90e4f4()
   USER32! 7e4249c4()
   USER32! 7e424a06()
   USER32! 7e43b190()
   ASIO4ALL! 053e2dcd()
   USER32! 7e418816()
   USER32! 7e428ea0()
   USER32! 7e428eec()
   NTDLL! 7c90e453()
   ThreadProc

The highlighted line is in a DialogBoxParam call (to show the control panel), which would explain things. Perhaps a modeless dialog should be used instead?

wuschel

  • Posts: 9
Re: ASIO out thread suicide
« Reply #13 on: 8 Aug '08 - 16:02 »
There are not a lot of options as far as meeting this requirement and, at the same time, not violating the COM requirements. What works and what is actually used where it works is to PostMessage() the reset request (or any other ASIOmessage for that matter) to the message queue of the owner thread, where a WndProc will receive it and perform the reinitialization.

But the "owner" thread is unable to process any messages because it is stuck in the ASIO4ALL control panel. This is the thread's call-stack once the ASIO4ALL control panel is opened (via the tray icon)...

   NTDLL! 7c90e4f4()
   USER32! 7e4249c4()
   USER32! 7e424a06()
   USER32! 7e43b190()
   ASIO4ALL! 053e2dcd()
   USER32! 7e418816()
   USER32! 7e428ea0()
   USER32! 7e428eec()
   NTDLL! 7c90e453()
   ThreadProc

The highlighted line is in a DialogBoxParam call (to show the control panel), which would explain things. Perhaps a modeless dialog should be used instead?

Even a modal dialog box contains a message loop and is able to dispatch a message anywhere to another window owned by the same thread. I wouldn't have mentioned the PostMessage() if I weren't absolutely, positively, certain that it works - not just for ASIO4ALL - and is implemented in numerous hosts where it works in exactly that way.

Quote from: http://msdn.microsoft.com/en-us/library/ms644994(VS.85).aspx
To process messages for the modal dialog box, the system starts its own message loop, taking temporary control of the message queue for the entire application. When the system retrieves a message that is not explicitly for the dialog box, it dispatches the message to the appropriate window. If it retrieves a WM_QUIT message, it posts the message back to the application message queue so that the application's main message loop can eventually retrieve the message.

where "Dispatch" = send directly to WndProc.

Thus, if your WndProc hasn't received the message, did you trace where it went?

Ian @ un4seen

  • Administrator
  • Posts: 20393
Re: ASIO out thread suicide
« Reply #14 on: 8 Aug '08 - 16:50 »
Note that the "owner" thread is not the main thread and has no windows attached, thus there is no WndProc, just a GetMessage loop. PostThreadMessage is used to send messages to the thread, and they remain in the thread's message queue until the ASIO4ALL control panel is closed (at which point the GetMessage loop can resume).

wuschel

  • Posts: 9
Re: ASIO out thread suicide
« Reply #15 on: 8 Aug '08 - 20:04 »
Note that the "owner" thread is not the main thread and has no windows attached, thus there is no WndProc,

Hmm, so why not create one, then? There is no guarantee for a message to be received/processed by any specific message loop. Message reception is only guaranteed for windows, not for message loops. Otherwise there would be no need for a DispatchMessage(), would there?

You will have the same problem when the control panel is opened from within the ASIO options dialog, BTW, and ASIO4ALL certainly is not the only ASIO driver in the world with a modal control panel dialog.

Ian @ un4seen

  • Administrator
  • Posts: 20393
Re: ASIO out thread suicide
« Reply #16 on: 11 Aug '08 - 14:10 »
Thread messages are guaranteed to arrive at their intended destination if there are no other message loops in the way, eg. modal dialogs ;)

Anyway, I figure it'd probably be best to host the driver in the main thread instead of a dedicated thread (particularly when it comes to the controlPanel function), so here's an update that does so...

   www.un4seen.com/stuff/xmp-asio.dll

I haven't fully looked into it yet, but it may have the potential for a dead-lock between the "track opening" thread and the main thread, so please report any of that (or other problems).

wuschel

  • Posts: 9
Re: ASIO out thread suicide
« Reply #17 on: 11 Aug '08 - 15:36 »
At a first glance, it is working great, actually. Thanks a lot!

You will note that I also changed ASIO4ALL to be a little more forgiving (reasonable timeouts here and there, that kind of stuff). It now also sort of works with the original ASIO out DLL, but not as well as this new version of yours, which allows the user to configure stuff on the fly (from the tray), even including the buffer size if they so chose. Well done!

kakuyoshi

  • Posts: 30
Re: ASIO out thread suicide
« Reply #18 on: 13 Sep '08 - 21:08 »
I do not get off with a question irrelevant to XMPlay.

May malfunction same as contents revised in xmp-asio.dll
happen in BASS ASIO? Because a similar phenomenon occurred
in Sound Player with BASS ASIO.
(But this may have a problem in Player side.)

kakuyoshi

Ian @ un4seen

  • Administrator
  • Posts: 20393
Re: ASIO out thread suicide
« Reply #19 on: 15 Sep '08 - 16:54 »
I am currently looking into making the same/similar changes in BASSASIO, but BASSASIO needs to be more flexible than the XMPlay plugin to accommodate different ways of doing things in different apps. For example, it can't really restrict ASIO calls to the main thread (as the XMPlay plugin update does) in case there is no message queue in the main thread.

kakuyoshi

  • Posts: 30
Re: ASIO out thread suicide
« Reply #20 on: 17 Sep '08 - 08:20 »
I am currently looking into making the same/similar changes in BASSASIO, but BASSASIO needs to be more flexible than the XMPlay plugin to accommodate different ways of doing things in different apps. For example, it can't really restrict ASIO calls to the main thread (as the XMPlay plugin update does) in case there is no message queue in the main thread.

I was relieved to hear that I examined correspondence.
It is a serious state, but prays for a problem being solved because I use Sound Player
for BASSASIO habitually as well as XMPlay.

kakuyoshi