0

Are IUnknown AddRef and Release interfaces are thread safe (atomic)? I know what they do are incrementing/decrementing reference counts, but I wonder how they do.

Particularly, IUnknown interface that inherits to Direct 3D components such as ID3D12DeviceChild. Version of Direct 3D is 12 if necessary. The reason why I'm mentioning Direct 3D is I've read a statement "DirectX APIs are not "true COM"". So it might differ from "true COM".

The reason why I got confused is due to failure in finding official document or proof that the interfaces are internally using whether Interlocked API or using simple increment/decrement operator.

Reference of simple increment/decrement operator implementation.

ULONG CMyObj::Release()
{
    if (--m_dwRef == 0)
    {
        delete this;
        return 0;
    }
    return m_dwRef;
}

References of Interlocked API (1), (2).

ULONG CMyMAPIObject::Release()
{
    // Decrement the object's internal counter.
    ULONG ulRefCount = InterlockedDecrement(m_cRef);
    if (0 == m_cRef)
    {
        delete this;
    }
    return ulRefCount;
}

Other related references :

I need to know whether they are thread safe because my project is multithreaded so that how I should treat them. If anyone can help, it will be really appreciated.

YoonSeok OH
  • 647
  • 2
  • 7
  • 15
  • 1
    `IUnknown` is just a pure virtual class in C++. The implementation is completely dependend on the components you actually use. – πάντα ῥεῖ Oct 16 '22 at 15:31
  • 5
    From the [answer](https://stackoverflow.com/a/64757457/1889329) you referred to: *"DirectX APIs only support `COINIT_MULTITHREADED`"*. In other words: DirectX COM objects can only live in the MTA, and it's pretty safe to expect that implementers of those interfaces know about this. In other words: You can assume that `AddRef` and `Release` use implementations that can be called from different threads concurrently. – IInspectable Oct 16 '22 at 15:36
  • @IInspectable You really would hope so, wouldn't you – Paul Sanders Oct 16 '22 at 15:37
  • In general, it depends. AddRef & Release are thread safe if their implementation is. For DirectX, if you respect what's written in the articles, you shouldn't need to know, and often, concurrent ops are not needed or depend on the driver (https://learn.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-render-multi-thread-create). That being said, if you disassemble DirectX assembly, you should always end up with `lock xadd ` x86/x64 instructions (~InterlockedIncrement API) because it cost almost nothing more than regular inc/dec. – Simon Mourier Oct 16 '22 at 17:10
  • @SimonMourier Older MSVC would produce `lock inc` instead, it works on the 386 unlike xadd. (Which is also why the return values of AddRef/Release are for debugging only) – Anders Oct 17 '22 at 12:06

1 Answers1

2

TL;DR: For Direct3D 11, Direct3D 12, and DXGI, all use of the IUnknown methods should be 'thread-safe'.

For Direct3D 11, the methods of ID3D11Device are all 'thread-safe' by design. The methods of ID3D11DeviceContext are not 'thread-safe'. That said, you can safely call AddRef and Release on all ID3D11DeviceChild-derived classes from any thread.

For Direct3D 12, the methods of ID3D12Device are also 'thread-safe'. Individual command-lists are not 'thread-safe' so you are expected to only use a command list interface from one thread at time. Keep in mind that in Direct3D 12, the application is responsible for handling all the CPU/GPU synchronization as well.

A key difference with Direct3D Device Child objects is that they are NOT immediately cleaned up when their reference counts hit 0. Since it uses 'deferred destruction' they get cleaned up at some later point, typically a Flush or Present.

Chuck Walbourn
  • 38,259
  • 2
  • 58
  • 81