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

Fix issues with DirectSound thread cleanup code #922

Merged
merged 3 commits into from
Oct 3, 2024

Conversation

dechamps
Copy link
Contributor

@dechamps dechamps commented Jun 2, 2024

This PR fixes a couple of issues with the way the DirectSound Host API cleans up its streaming thread, and also simplifies the code as a nice side effect.

Details can be found in the commit messages.

@philburk
Copy link
Collaborator

philburk commented Jun 7, 2024

This PR fixes a couple of issues

Thanks for offering the fix. Could you create an Issue, or two, so that we can discuss the bug?

@RossBencina
Copy link
Collaborator

I agree that the portaudio code is not consistent with the microsoft API docs at https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/endthread-endthreadex?view=msvc-170 I can see where the confusion comes from:

  • beginthread/endthread -> client MUST NOT call CloseHandle
  • beginthreadex/endthreadex -> client MUST call CloseHandle

It also looks like the old code was not consistently using the macros supplied for creating and closing the thread (the macros exist for the purpose of cygwin compatibility when beginthreadex/endthreadex is not used). Ideally I think the macros should be retained and fixed rather than eliminating them, since they clearly abstract the differences between the two runtimes.

dechamps added 2 commits June 9, 2024 10:50
The previous code refrained from calling CloseHandle on
_beingthreadex()-created threads, citing "documentation". Actually the
official docs suggest the exact opposite:

https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/endthread-endthreadex

> when you use _beginthreadex and _endthreadex, you must close the
> thread handle by calling the Win32 CloseHandle API

This means the previous code would leak the thread handle. This can be
confirmed empirically by looking at the process Win32 handle list in
e.g. Process Explorer.

Fixes PortAudio#925
The previous code would return from Pa_StopStream() when the streaming
thread signals that it is done. This is racy, because the streaming
thread is technically still running code by that point. If, for example,
the PortAudio user decides to unload the entire PortAudio DLL as soon as
Pa_StopStream() returns, hilarity could ensue.

Instead we can simply wait on the thread handle itself, which is both
safer and simpler.

Fixes PortAudio#926
@dechamps
Copy link
Contributor Author

dechamps commented Jun 9, 2024

Could you create an Issue, or two, so that we can discuss the bug?

Done in #925 and #926.

Ideally I think the macros should be retained and fixed rather than eliminating them, since they clearly abstract the differences between the two runtimes.

Done by preserving the CLOSE_THREAD_HANDLE macro.

(I would argue that these macros don't really make sense in practice and we should get rid of them, because they are abstracting away differences that don't really exist - the official docs of _beginthreadex() do not even try to hide the fact that it's really just a thin wrapper around CreateThread(), and that the return value is really just a Win32 thread HANDLE in disguise, as evidenced by the fact that we're being told to pass it to CloseHandle(). Really the only difference is which function is used to create the thread - CreateThread() vs. _beginthreadex() and everything else is the same. Given this, the macros just seem to obscure the code for no apparent benefit. But I guess I don't have strong feelings about this.)

Copy link
Collaborator

@RossBencina RossBencina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMPORTANT: DO NOT SQUASH when merging.

I have finally made time to review this properly. Overall it looks fine, although it would have been easier to deal with if it was two separate PRs.

7398279 (removal of event signaling thread completion) is 100% ok by me.

4a2fef4 (match _beginthreadex with CloseThread) appears correct to me. Not sure the fix is ideal, but ok.

@@ -3051,9 +3051,7 @@ static PaError StartStream( PaStream *s )
#ifndef PA_WIN_DS_USE_WMME_TIMER
if( stream->processingThread )
{
#ifdef CLOSE_THREAD_HANDLE
Copy link
Collaborator

@RossBencina RossBencina Sep 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose of #ifdef CLOSE_THREAD_HANDLE is to allow the macro to be a no-op. Given that there is now no situation where it would be a no-op I am not sure that the macro should be present at all -- but maybe it makes sense to keep it to provide some hint that it is dependent on the way the thread is created.

@RossBencina RossBencina merged commit 57aa393 into PortAudio:master Oct 3, 2024
11 checks passed
@RossBencina
Copy link
Collaborator

Merged. Thanks @dechamps

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

Successfully merging this pull request may close these issues.

3 participants