wasapi: use InterlockedXXX APIs to secure inter-thread r/w access to variables to avoid possible deadlocks or logic corruption

This commit is contained in:
dmitrykos 2016-08-24 22:04:17 +03:00
commit 46bce3c39f

View file

@ -543,7 +543,7 @@ typedef struct PaWasapiStream
// must be volatile to avoid race condition on user query while // must be volatile to avoid race condition on user query while
// thread is being started // thread is being started
volatile BOOL running; volatile LONG running;
PA_THREAD_ID dwThreadId; PA_THREAD_ID dwThreadId;
HANDLE hThread; HANDLE hThread;
@ -1237,8 +1237,8 @@ static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format, EMixerDir d
typedef struct PaActivateAudioInterfaceCompletionHandler typedef struct PaActivateAudioInterfaceCompletionHandler
{ {
IActivateAudioInterfaceCompletionHandler parent; IActivateAudioInterfaceCompletionHandler parent;
ULONG refs; volatile LONG refs;
volatile BOOL done; volatile LONG done;
struct struct
{ {
HRESULT hr; HRESULT hr;
@ -1256,7 +1256,8 @@ static HRESULT (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_Quer
// From MSDN: // From MSDN:
// "The IAgileObject interface is a marker interface that indicates that an object // "The IAgileObject interface is a marker interface that indicates that an object
// is free threaded and can be called from any apartment." // is free threaded and can be called from any apartment."
if (IsEqualIID(riid, &IID_IAgileObject)) if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IAgileObject))
{ {
handler->parent.lpVtbl->AddRef((IActivateAudioInterfaceCompletionHandler *)handler); handler->parent.lpVtbl->AddRef((IActivateAudioInterfaceCompletionHandler *)handler);
(*ppvObject) = handler; (*ppvObject) = handler;
@ -1270,20 +1271,23 @@ static ULONG (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_AddRef
IActivateAudioInterfaceCompletionHandler *This) IActivateAudioInterfaceCompletionHandler *This)
{ {
PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This; PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This;
return ++ handler->refs;
return InterlockedIncrement(&handler->refs);
} }
static ULONG (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_Release)( static ULONG (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_Release)(
IActivateAudioInterfaceCompletionHandler *This) IActivateAudioInterfaceCompletionHandler *This)
{ {
PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This; PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This;
if (handler->refs == 0) ULONG refs;
if ((refs = InterlockedDecrement(&handler->refs)) == 0)
{ {
PaUtil_FreeMemory(handler->parent.lpVtbl); PaUtil_FreeMemory(handler->parent.lpVtbl);
PaUtil_FreeMemory(handler); PaUtil_FreeMemory(handler);
return 0;
} }
return -- handler->refs;
return refs;
} }
static HRESULT (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_ActivateCompleted)( static HRESULT (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_ActivateCompleted)(
@ -1311,7 +1315,9 @@ static HRESULT (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_Acti
else else
handler->out.hr = hr; handler->out.hr = hr;
handler->done = TRUE; // Got client object, stop busy waiting in ActivateAudioInterface_WINRT
InterlockedExchange(&handler->done, TRUE);
return hr; return hr;
} }
@ -1324,6 +1330,7 @@ static IActivateAudioInterfaceCompletionHandler *CreateActivateAudioInterfaceCom
handler->parent.lpVtbl->AddRef = &PaActivateAudioInterfaceCompletionHandler_AddRef; handler->parent.lpVtbl->AddRef = &PaActivateAudioInterfaceCompletionHandler_AddRef;
handler->parent.lpVtbl->Release = &PaActivateAudioInterfaceCompletionHandler_Release; handler->parent.lpVtbl->Release = &PaActivateAudioInterfaceCompletionHandler_Release;
handler->parent.lpVtbl->ActivateCompleted = &PaActivateAudioInterfaceCompletionHandler_ActivateCompleted; handler->parent.lpVtbl->ActivateCompleted = &PaActivateAudioInterfaceCompletionHandler_ActivateCompleted;
handler->refs = 1;
return (IActivateAudioInterfaceCompletionHandler *)handler; return (IActivateAudioInterfaceCompletionHandler *)handler;
} }
#endif #endif
@ -1360,7 +1367,7 @@ static HRESULT ActivateAudioInterface_WINRT(const PaWasapiDeviceInfo *deviceInfo
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error); IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
// Wait in busy loop for async operation to complete // Wait in busy loop for async operation to complete
while (SUCCEEDED(hr) && !handlerImpl->done) while (SUCCEEDED(hr) && !InterlockedCompareExchange(&handlerImpl->done, FALSE, FALSE))
{ {
Sleep(1); Sleep(1);
} }
@ -3849,7 +3856,7 @@ static PaError StartStream( PaStream *s )
stream->out.clientProc = stream->out.clientParent; stream->out.clientProc = stream->out.clientParent;
// Signal: stream running. // Signal: stream running.
stream->running = TRUE; InterlockedExchange(&stream->running, TRUE);
} }
return result; return result;
@ -3891,7 +3898,8 @@ void _StreamFinish(PaWasapiStream *stream)
// Cleanup handles // Cleanup handles
_StreamCleanup(stream); _StreamCleanup(stream);
stream->running = FALSE; // Notify: not running
InterlockedExchange(&stream->running, FALSE);
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
@ -3906,6 +3914,18 @@ void _StreamCleanup(PaWasapiStream *stream)
SAFE_CLOSE(stream->hBlockingOpStreamWR); SAFE_CLOSE(stream->hBlockingOpStreamWR);
} }
// ------------------------------------------------------------------------------------------
static BOOL _IsStreamRunning(PaWasapiStream *stream)
{
LONG retv;
do
{
retv = stream->running;
}
while (InterlockedCompareExchange(&stream->running, FALSE, FALSE) != retv);
return retv;
}
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
static PaError StopStream( PaStream *s ) static PaError StopStream( PaStream *s )
{ {
@ -3925,13 +3945,13 @@ static PaError AbortStream( PaStream *s )
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
static PaError IsStreamStopped( PaStream *s ) static PaError IsStreamStopped( PaStream *s )
{ {
return !((PaWasapiStream *)s)->running; return !_IsStreamRunning((PaWasapiStream *)s);
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
static PaError IsStreamActive( PaStream *s ) static PaError IsStreamActive( PaStream *s )
{ {
return ((PaWasapiStream *)s)->running; return _IsStreamRunning((PaWasapiStream *)s);
} }
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
@ -3965,7 +3985,7 @@ static PaError ReadStream( PaStream* s, void *_buffer, unsigned long frames )
ThreadIdleScheduler sched; ThreadIdleScheduler sched;
// validate // validate
if (!stream->running) if (!_IsStreamRunning(stream))
return paStreamIsStopped; return paStreamIsStopped;
if (stream->captureClient == NULL) if (stream->captureClient == NULL)
return paBadStreamPtr; return paBadStreamPtr;
@ -4144,7 +4164,7 @@ static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long fram
ThreadIdleScheduler sched; ThreadIdleScheduler sched;
// validate // validate
if (!stream->running) if (!_IsStreamRunning(stream))
return paStreamIsStopped; return paStreamIsStopped;
if (stream->renderClient == NULL) if (stream->renderClient == NULL)
return paBadStreamPtr; return paBadStreamPtr;
@ -4258,7 +4278,7 @@ static signed long GetStreamReadAvailable( PaStream* s )
UINT32 available = 0; UINT32 available = 0;
// validate // validate
if (!stream->running) if (!_IsStreamRunning(stream))
return paStreamIsStopped; return paStreamIsStopped;
if (stream->captureClient == NULL) if (stream->captureClient == NULL)
return paBadStreamPtr; return paBadStreamPtr;
@ -4284,7 +4304,7 @@ static signed long GetStreamWriteAvailable( PaStream* s )
UINT32 available = 0; UINT32 available = 0;
// validate // validate
if (!stream->running) if (!_IsStreamRunning(stream))
return paStreamIsStopped; return paStreamIsStopped;
if (stream->renderClient == NULL) if (stream->renderClient == NULL)
return paBadStreamPtr; return paBadStreamPtr;
@ -5074,7 +5094,7 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
} }
// Signal: stream running // Signal: stream running
stream->running = TRUE; InterlockedExchange(&stream->running, TRUE);
// Notify: thread started // Notify: thread started
SetEvent(stream->hThreadStart); SetEvent(stream->hThreadStart);
@ -5141,7 +5161,7 @@ thread_end:
CoUninitialize(); CoUninitialize();
// Notify: not running // Notify: not running
stream->running = FALSE; InterlockedExchange(&stream->running, FALSE);
// Notify: thread exited // Notify: thread exited
SetEvent(stream->hThreadExit); SetEvent(stream->hThreadExit);
@ -5308,7 +5328,7 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
} }
// Signal: stream running // Signal: stream running
stream->running = TRUE; InterlockedExchange(&stream->running, TRUE);
// Notify: thread started // Notify: thread started
SetEvent(stream->hThreadStart); SetEvent(stream->hThreadStart);
@ -5663,7 +5683,7 @@ thread_end:
CoUninitialize(); CoUninitialize();
// Notify: not running // Notify: not running
stream->running = FALSE; InterlockedExchange(&stream->running, FALSE);
// Notify: thread exited // Notify: thread exited
SetEvent(stream->hThreadExit); SetEvent(stream->hThreadExit);