refactored COM initialization for dsound, asio and wasapi to new source file pa_win_coinitialize.c. fixes com uninitialisation bugs in error cases. resolves ticket #128

This commit is contained in:
rossb 2011-05-09 20:05:34 +00:00
commit 52ab9d6600
5 changed files with 301 additions and 90 deletions

View file

@ -101,6 +101,8 @@
#include "pa_debugprint.h"
#include "pa_ringbuffer.h"
#include "pa_win_coinitialize.h"
/* This version of pa_asio.cpp is currently only targetted at Win32,
It would require a few tweaks to work with pre-OS X Macintosh.
To make configuration easier, we define WIN32 here to make sure
@ -289,6 +291,8 @@ typedef struct
PaUtilAllocationGroup *allocations;
PaWinUtilComInitializationResult comInitializationResult;
AsioDrivers *asioDrivers;
void *systemSpecific;
@ -935,12 +939,10 @@ PaError PaAsio_GetAvailableBufferSizes( PaDeviceIndex device,
}
/* Unload whatever we loaded in LoadAsioDriver().
Also balance the call to CoInitialize(0).
*/
static void UnloadAsioDriver( void )
{
ASIOExit();
CoUninitialize();
}
/*
@ -956,23 +958,8 @@ static PaError LoadAsioDriver( PaAsioHostApiRepresentation *asioHostApi, const c
ASIOError asioError;
int asioIsInitialized = 0;
/*
ASIO uses CoCreateInstance() to load a driver. That requires that
CoInitialize(0) be called for every thread that loads a driver.
It is OK to call CoInitialize(0) multiple times form one thread as long
as it is balanced by a call to CoUninitialize(). See UnloadAsioDriver().
The V18 version called CoInitialize() starting on 2/19/02.
That was removed from PA V19 for unknown reasons.
Phil Burk added it back on 6/27/08 so that JSyn would work.
*/
CoInitialize( 0 );
if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(driverName) ) )
{
/* If this returns an error then it might be because CoInitialize(0) was removed.
It should be called right before this.
*/
result = paUnanticipatedHostError;
PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" );
goto error;
@ -1021,7 +1008,7 @@ error:
{
ASIOExit();
}
CoUninitialize();
return result;
}
@ -1053,6 +1040,24 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
goto error;
}
/*
We initialize COM ourselves here and uninitialize it in Terminate().
This should be the only COM initialization needed in this module.
The ASIO SDK may also initialize COM but since we want to reduce dependency
on the ASIO SDK we manage COM initialization ourselves.
There used to be code that initialized COM in other situations
such as when creating a Stream. This made PA work when calling Pa_CreateStream
from a non-main thread. However we currently consider initialization
of COM in non-main threads to be the caller's responsibility.
*/
result = PaWinUtil_CoInitialize( paASIO, &asioHostApi->comInitializationResult );
if( result != paNoError )
{
goto error;
}
asioHostApi->asioDrivers = 0; /* avoid surprises in our error handler below */
asioHostApi->allocations = PaUtil_CreateAllocationGroup();
@ -1065,7 +1070,7 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
/* Allocate the AsioDrivers() driver list (class from ASIO SDK) */
try
{
asioHostApi->asioDrivers = new AsioDrivers(); /* calls CoInitialize(0) */
asioHostApi->asioDrivers = new AsioDrivers(); /* invokes CoInitialize(0) in AsioDriverList::AsioDriverList */
}
catch (std::bad_alloc)
{
@ -1347,8 +1352,11 @@ error:
delete asioHostApi->asioDrivers;
asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
PaWinUtil_CoUninitialize( paASIO, &asioHostApi->comInitializationResult );
PaUtil_FreeMemory( asioHostApi );
}
return result;
}
@ -1368,9 +1376,11 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
}
delete asioHostApi->asioDrivers; /* calls CoUninitialize() */
delete asioHostApi->asioDrivers;
asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
PaWinUtil_CoUninitialize( paASIO, &asioHostApi->comInitializationResult );
PaUtil_FreeMemory( asioHostApi );
}
@ -3836,7 +3846,12 @@ PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific )
int asioIsInitialized = 0;
PaAsioHostApiRepresentation *asioHostApi;
PaAsioDeviceInfo *asioDeviceInfo;
PaWinUtilComInitializationResult comInitializationResult;
/* initialize COM again here, we might be in another thread */
result = PaWinUtil_CoInitialize( paASIO, &comInitializationResult );
if( result != paNoError )
return result;
result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
if( result != paNoError )
@ -3863,9 +3878,6 @@ PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific )
asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
/* See notes about CoInitialize(0) in LoadAsioDriver(). */
CoInitialize(0);
if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(asioDeviceInfo->commonDeviceInfo.name) ) )
{
result = paUnanticipatedHostError;
@ -3914,7 +3926,6 @@ PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErr
goto error;
}
CoUninitialize();
PA_DEBUG(("PaAsio_ShowControlPanel: ASIOExit(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
return result;
@ -3924,7 +3935,8 @@ error:
{
ASIOExit();
}
CoUninitialize();
PaWinUtil_CoUninitialize( paASIO, &comInitializationResult );
return result;
}

View file

@ -74,6 +74,7 @@
#include "pa_win_ds_dynlink.h"
#include "pa_win_waveformat.h"
#include "pa_win_wdmks_utils.h"
#include "pa_win_coinitialize.h"
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
#pragma comment( lib, "dsound.lib" )
@ -186,7 +187,7 @@ typedef struct
/* implementation specific data goes here */
char comWasInitialized;
PaWinUtilComInitializationResult comInitializationResult;
} PaWinDsHostApiRepresentation;
@ -1011,32 +1012,15 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
int i, deviceCount;
PaWinDsHostApiRepresentation *winDsHostApi;
DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
PaWinDsDeviceInfo *deviceInfoArray;
char comWasInitialized = 0;
/*
If COM is already initialized CoInitialize will either return
FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different
threading mode. In either case we shouldn't consider it an error
but we need to be careful to not call CoUninitialize() if
RPC_E_CHANGED_MODE was returned.
*/
HRESULT hr = CoInitialize(NULL);
if( FAILED(hr) && hr != RPC_E_CHANGED_MODE )
return paUnanticipatedHostError;
if( hr != RPC_E_CHANGED_MODE )
comWasInitialized = 1;
PaWinDs_InitializeDSoundEntryPoints();
/* initialise guid vectors so they can be safely deleted on error */
deviceNamesAndGUIDs.winDsHostApi = NULL;
deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
PaWinDs_InitializeDSoundEntryPoints();
winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
if( !winDsHostApi )
{
@ -1044,7 +1028,11 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
goto error;
}
winDsHostApi->comWasInitialized = comWasInitialized;
result = PaWinUtil_CoInitialize( paDirectSound, &winDsHostApi->comInitializationResult );
if( result != paNoError )
{
goto error;
}
winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
if( !winDsHostApi->allocations )
@ -1183,15 +1171,16 @@ error:
PaUtil_FreeAllAllocations( winDsHostApi->allocations );
PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
}
PaWinUtil_CoUninitialize( paDirectSound, &winDsHostApi->comInitializationResult );
PaUtil_FreeMemory( winDsHostApi );
}
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( comWasInitialized )
CoUninitialize();
PaWinDs_TerminateDSoundEntryPoints();
return result;
}
@ -1201,7 +1190,6 @@ error:
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
{
PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
char comWasInitialized = winDsHostApi->comWasInitialized;
/*
IMPLEMENT ME:
@ -1214,12 +1202,11 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
}
PaWinUtil_CoUninitialize( paDirectSound, &winDsHostApi->comInitializationResult );
PaUtil_FreeMemory( winDsHostApi );
PaWinDs_TerminateDSoundEntryPoints();
if( comWasInitialized )
CoUninitialize();
}

View file

@ -75,6 +75,8 @@
#include "pa_debugprint.h"
#include "pa_ringbuffer.h"
#include "pa_win_coinitialize.h"
#ifndef NTDDI_VERSION
#undef WINVER
@ -370,6 +372,8 @@ typedef struct
/* implementation specific data goes here */
PaWinUtilComInitializationResult comInitializationResult;
//in case we later need the synch
IMMDeviceEnumerator *enumerator;
@ -512,8 +516,6 @@ void *PaWasapi_ReallocateMemory(void *ptr, size_t size);
void PaWasapi_FreeMemory(void *ptr);
// Local statics
static volatile BOOL g_WasapiCOMInit = FALSE;
static volatile DWORD g_WasapiInitThread = 0;
// ------------------------------------------------------------------------------------------
#define LogHostError(HRES) __LogHostError(HRES, __FUNCTION__, __FILE__, __LINE__)
@ -1057,26 +1059,6 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
return paNoError;
}
/*
If COM is already initialized CoInitialize will either return
FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different
threading mode. In either case we shouldn't consider it an error
but we need to be careful to not call CoUninitialize() if
RPC_E_CHANGED_MODE was returned.
*/
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE))
{
PRINT(("WASAPI: failed CoInitialize"));
return paUnanticipatedHostError;
}
if (hr != RPC_E_CHANGED_MODE)
g_WasapiCOMInit = TRUE;
// Memorize calling thread id and report warning on Uninitialize if calling thread is different
// as CoInitialize must match CoUninitialize in the same thread.
g_WasapiInitThread = GetCurrentThreadId();
paWasapi = (PaWasapiHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaWasapiHostApiRepresentation) );
if (paWasapi == NULL)
{
@ -1084,6 +1066,12 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
goto error;
}
result = PaWinUtil_CoInitialize( paWASAPI, &paWasapi->comInitializationResult );
if( result != paNoError )
{
goto error;
}
paWasapi->allocations = PaUtil_CreateAllocationGroup();
if (paWasapi->allocations == NULL)
{
@ -1454,26 +1442,12 @@ static void Terminate( PaUtilHostApiRepresentation *hostApi )
PaUtil_DestroyAllocationGroup(paWasapi->allocations);
}
PaWinUtil_CoUninitialize( paWASAPI, &paWasapi->comInitializationResult );
PaUtil_FreeMemory(paWasapi);
// Close AVRT
CloseAVRT();
// Uninit COM (checking calling thread we won't unitialize user's COM if one is calling
// Pa_Unitialize by mistake from not initializing thread)
if (g_WasapiCOMInit)
{
DWORD calling_thread_id = GetCurrentThreadId();
if (g_WasapiInitThread != calling_thread_id)
{
PRINT(("WASAPI: failed CoUninitializes calling thread[%d] does not match initializing thread[%d]\n",
calling_thread_id, g_WasapiInitThread));
}
else
{
CoUninitialize();
}
}
}
// ------------------------------------------------------------------------------------------