diff --git a/src/dsoundoal.cpp b/src/dsoundoal.cpp index 80d4c20..f218d98 100644 --- a/src/dsoundoal.cpp +++ b/src/dsoundoal.cpp @@ -119,7 +119,7 @@ std::optional GetSpeakerConfig(IMMDevice *device, const GUID &devid) return speakerconf; } -ds::expected,HRESULT> CreateDeviceShare(GUID &guid) +ds::expected,HRESULT> CreateDeviceShare(const GUID &guid) { TRACE("CreateDeviceShare Creating shared device %s\n", GuidPrinter{guid}.c_str()); @@ -220,8 +220,7 @@ ds::expected,HRESULT> CreateDeviceShare(GUID &guid const DWORD maxHw{totalSources > MaxHwSources*2 ? MaxHwSources : (MaxHwSources/2)}; - auto shared = std::make_unique(); - shared->mId = guid; + auto shared = std::make_unique(guid); shared->mSpeakerConfig = speakerconf; shared->mMaxHwSources = maxHw; shared->mMaxSwSources = totalSources - maxHw; @@ -233,9 +232,30 @@ ds::expected,HRESULT> CreateDeviceShare(GUID &guid } // namespace +#define PREFIX "SharedDevice::" +std::mutex SharedDevice::sDeviceListMutex; +std::vector> SharedDevice::sDeviceList; -std::mutex DSound8OAL::sDeviceListMutex; -std::vector> DSound8OAL::sDeviceList; +auto SharedDevice::GetById(const GUID &deviceId) noexcept + -> ds::expected,HRESULT> +{ + auto find_id = [&deviceId](std::unique_ptr &device) + { return deviceId == device->mId; }; + + std::unique_lock listlock{sDeviceListMutex}; + auto sharediter = std::find_if(sDeviceList.begin(), sDeviceList.end(), find_id); + if(sharediter != sDeviceList.end()) + { + (*sharediter)->AddRef(); + return ComPtr{(*sharediter).get()}; + } + + auto shared = CreateDeviceShare(deviceId); + if(!shared) return ds::unexpected(shared.error()); + + sDeviceList.emplace_back(std::move(shared).value()); + return ComPtr{sDeviceList.back().get()}; +} SharedDevice::~SharedDevice() { @@ -249,6 +269,22 @@ SharedDevice::~SharedDevice() alcCloseDevice(mDevice.release()); } +void SharedDevice::dispose() noexcept +{ + std::lock_guard listlock{sDeviceListMutex}; + + auto find_shared = [this](std::unique_ptr &shared) + { return this == shared.get(); }; + + auto shared_iter = std::find_if(sDeviceList.begin(), sDeviceList.end(), find_shared); + if(shared_iter != sDeviceList.end()) + { + TRACE(PREFIX "dispose Freeing shared device %s\n", + GuidPrinter{(*shared_iter)->mId}.c_str()); + sDeviceList.erase(shared_iter); + } +} +#undef PREFIX BufferSubList::~BufferSubList() { @@ -277,27 +313,7 @@ DSound8OAL::DSound8OAL(bool is8) : mPrimaryBuffer{*this}, mIs8{is8} { } -DSound8OAL::~DSound8OAL() -{ - if(!mShared) - return; - - std::lock_guard listlock{sDeviceListMutex}; - - auto find_shared = [this](std::unique_ptr &shared) - { return mShared == shared.get(); }; - - auto shared_iter = std::find_if(sDeviceList.begin(), sDeviceList.end(), find_shared); - if(shared_iter == sDeviceList.end()) return; - - (*shared_iter)->mUseCount -= 1; - if((*shared_iter)->mUseCount == 0) - { - TRACE(PREFIX "~DSound8OAL Freeing shared device %s\n", - GuidPrinter{(*shared_iter)->mId}.c_str()); - sDeviceList.erase(shared_iter); - } -} +DSound8OAL::~DSound8OAL() = default; HRESULT STDMETHODCALLTYPE DSound8OAL::QueryInterface(REFIID riid, void** ppvObject) noexcept @@ -647,26 +663,9 @@ HRESULT STDMETHODCALLTYPE DSound8OAL::Initialize(const GUID *deviceId) noexcept HRESULT hr{GetDeviceID(*deviceId, devid)}; if(FAILED(hr)) return hr; - { - auto find_id = [devid](std::unique_ptr &device) - { return devid == device->mId; }; - - std::unique_lock listlock{sDeviceListMutex}; - auto sharediter = std::find_if(sDeviceList.begin(), sDeviceList.end(), find_id); - if(sharediter != sDeviceList.end()) - { - (*sharediter)->mUseCount += 1; - mShared = sharediter->get(); - } - else - { - auto shared = CreateDeviceShare(devid); - if(!shared) return shared.error(); - - sDeviceList.emplace_back(std::move(shared).value()); - mShared = sDeviceList.back().get(); - } - } + auto shared = SharedDevice::GetById(devid); + if(!shared) return shared.error(); + mShared = std::move(shared).value(); mPrimaryBuffer.setContext(mShared->mContext.get()); diff --git a/src/dsoundoal.h b/src/dsoundoal.h index 0f357d7..a580814 100644 --- a/src/dsoundoal.h +++ b/src/dsoundoal.h @@ -10,82 +10,84 @@ #include "AL/alc.h" #include "comptr.h" #include "dsoal.h" +#include "expected.h" #include "primarybuffer.h" class Buffer; struct SharedDevice { + static std::mutex sDeviceListMutex; + static std::vector> sDeviceList; + struct NoDeleter { template void operator()(T*) noexcept { } }; - SharedDevice() = default; + SharedDevice(const GUID &id) : mId{id} { } SharedDevice(const SharedDevice&) = delete; - SharedDevice(SharedDevice&& rhs) = default; ~SharedDevice(); SharedDevice& operator=(const SharedDevice&) = delete; - SharedDevice& operator=(SharedDevice&&) = delete; + + void dispose() noexcept; + + ULONG AddRef() noexcept { return mRef.fetch_add(1u, std::memory_order_relaxed)+1; } + ULONG Release() noexcept + { + const auto ret = mRef.fetch_sub(1u, std::memory_order_relaxed)-1; + if(ret == 0) dispose(); + return ret; + } DWORD mMaxHwSources{}; DWORD mMaxSwSources{}; - DWORD mCurrentHwSources{}; - DWORD mCurrentSwSources{}; + std::atomic mCurrentHwSources{}; + std::atomic mCurrentSwSources{}; - GUID mId{}; + const GUID mId; DWORD mSpeakerConfig{}; std::unique_ptr mDevice; std::unique_ptr mContext; - size_t mUseCount{1u}; + std::atomic mRef{1u}; DWORD getCurrentHwCount() const noexcept - { return static_cast(mCurrentHwSources); } + { return mCurrentHwSources.load(std::memory_order_relaxed); } DWORD getCurrentSwCount() const noexcept - { return static_cast(mCurrentSwSources); } + { return mCurrentSwSources.load(std::memory_order_relaxed); } /* Increment mCurrentHwSources up to mMaxHwSources. Returns false is the * current is already at max, or true if it was successfully incremented. */ bool incHwSources() noexcept { - /* Can't use standard atomics here since they break the move - * constructor. - */ - DWORD current; - DWORD previous{static_cast(mCurrentHwSources)}; + DWORD cur{mCurrentHwSources.load(std::memory_order_relaxed)}; do { - if(previous == mMaxHwSources) + if(cur == mMaxHwSources) return false; - current = previous; - previous = InterlockedCompareExchange(&mCurrentHwSources, current+1, current); - } while(previous != current); + } while(!mCurrentHwSources.compare_exchange_weak(cur, cur+1, std::memory_order_relaxed)); return true; } /* Increment mCurrentSwSources up to mMaxSwSources. */ bool incSwSources() noexcept { - /* Can't use standard atomics here since they break the move - * constructor. - */ - DWORD current; - DWORD previous{static_cast(mCurrentSwSources)}; + DWORD cur{mCurrentSwSources.load(std::memory_order_relaxed)}; do { - if(previous == mMaxSwSources) + if(cur == mMaxSwSources) return false; - current = previous; - previous = InterlockedCompareExchange(&mCurrentSwSources, current+1, current); - } while(previous != current); + } while(!mCurrentSwSources.compare_exchange_weak(cur, cur+1, std::memory_order_relaxed)); return true; } - void decHwSources() noexcept { InterlockedDecrement(&mCurrentHwSources); } - void decSwSources() noexcept { InterlockedDecrement(&mCurrentSwSources); } + void decHwSources() noexcept { mCurrentHwSources.fetch_sub(1u, std::memory_order_relaxed); } + void decSwSources() noexcept { mCurrentSwSources.fetch_sub(1u, std::memory_order_relaxed); } + + static auto GetById(const GUID &deviceId) noexcept -> ds::expected,HRESULT>; }; @@ -114,9 +116,6 @@ class DSound8OAL final : IDirectSound8 { DSound8OAL(bool is8); ~DSound8OAL(); - static std::mutex sDeviceListMutex; - static std::vector> sDeviceList; - class UnknownImpl final : IUnknown { DSound8OAL *impl_from_base() noexcept { @@ -145,7 +144,7 @@ class DSound8OAL final : IDirectSound8 { std::mutex mDsMutex; DWORD mPrioLevel{}; - SharedDevice *mShared{}; + ComPtr mShared; std::vector mSecondaryBuffers; PrimaryBuffer mPrimaryBuffer;