IUnknown :: QueryInterface () increases reference count?

If I have IUnknown *ptr , do I need to call Release() for every interface I get through ptr->QueryInterface() , in addition to calling ptr->Release() when I finished with ptr ?

I used to think the answer is "Yes", but this quote from MSDN confused me:

Sometimes you may need to get a weak reference to an object (that is, you may want to get a pointer to one of your interfaces without increasing the number of links), but this is not acceptable for this, calling QueryInterface then Release .

I don’t understand why this problem - if I call ptr->QueryInterface() and then call Release in the resulting pointer, should the object reference count still be positive? How does this lead to an invalid pointer?

+6
source share
4 answers

The documentation is correct. And you need to follow the rules of reference counting, which include calling Release on the interfaces obtained from QueryInterface , in addition to after creating the object.

To find out why you cannot make weak pointers with Release - there is a race condition when you call QueryInterface and then Release immediately after.

  • Thread1 creates an object - reference counter 1
  • Thread2 calls QueryInterface for a weak job - reference counter 2
  • Thread1 releases an object - reference counter 1
  • Thread2 calls Release for the weak link reference number 0. The object is destroyed.
  • Thread2 is trying to use the error object.

The warning warns against the above - some programmers presumably think that they can "call ptr->QueryInterface() , and then call Release on the resulting pointer", and then use the object ...

+6
source

IUnknown :: QueryInterface Method

Retrieves pointers to supported interfaces on an object.

This method calls IUnknown :: AddRef on the returned pointer.

Directly from the IUnknown :: QueryInterface link at http://msdn.microsoft.com/en-us/library/ms682521%28v=vs.85%29.aspx

+4
source

The flow is not the only scenario; I would go so far as to say that threads are not really the main scenario at all: these COM rules date back to Win16 before the preliminary multithreading is added first.

The key problem is that with respect to COM, reference counting is for each interface, not for every object. Implementing COM allows you to implement a reference counter for free, implementing it for each object - this is perhaps the easiest way to do this in C ++, especially when a COM object is mapped to one C ++ object, but this is nothing more than an implementation detail. and client COM code cannot rely on this.

There are many COM objects that can generate interfaces on the fly as needed, and then destroy them as soon as they are no longer needed. In those cases, if you call QI to get one of these interfaces, after you call Release, the memory for this interface can be canceled, so using this can lead to an error / crash / etc.

Generally speaking, you should consider any call to β†’ Release () as potentially freeing memory after the pointer.

(Also note that for COM, there is no concept of weak links for starters: there are (strong) links counted and what it is.)

+2
source

The suggestion to avoid weak links does not solve the race problem.

 T1 operator new, create object, references: 1 T1 passes interface object reference to T2, thinking it can "share" ownership T1 suspends T2 resumes T2 QueryInterface T2 suspends before InterlockedIncrement, references: 1 T1 resumes T1 Calls Release T1 suspends between InterlockedDecrement and operator delete, references: 0 T2 resumes, InterlockedIncrement occurs, references 1 T2 suspends T1 resumes, operator delete executes, references 1 !!! T1 suspends T2 resumes T2 Any reference to the interface is now invalid since it has been deleted with reference count 1. 

This is resolvable on the COM server. The COM client, however, should not depend on the server preventing this race condition. Therefore, COM clients MUST NOT pass interface objects between threads. The only thread that must be allowed to access the interface object is the ONE thread, which currently owns the interface object.

T1 should not call Release. Yes, it could call AddRef before passing the interface object to T2. But this may not solve the race, but only move it somewhere else. Best practice is to always support the concept of a single interface object, one owner.

If the COM server wants to support the concept that two (or more) interfaces can refer to some general state of the internal server, the COM server should advertise the contract by supplying the CreateCopyOfInstance method and managing the conflict internally. There are, of course, examples of servers that handle such a "splitter". Take a look at Microsoft's persistent storage interfaces. There the interfaces are NOT "bloated". Each interface must belong to one user (thread / process / independently), and the β€œfan” is controlled inside the server by the server using the methods provided by COM clients to control some aspects of conflicting issues. Therefore, Microsoft COM servers must therefore decide race conditions within their contracts to their customers.

0
source

Source: https://habr.com/ru/post/897069/


All Articles