Board index » cppbuilder » "Smart Pointer" reference counting

"Smart Pointer" reference counting

I'm not sure why the "smart interface pointers" work the way they do. For
example, I have the following method  in one of my COM objects (CPPB 5.0
project):

STDMETHODIMP TMyObjectImpl::Method1(IMyObjectPtr OtherObject, TOLEBOOL*
Result)
{
  // OtherObject must have its reference count incremented because
  // it will automatically Release() when it goes out of scope.
  // Why did Borland do this????
  OtherObject->AddRef();

  // Do stuff...
  return S_OK;

Quote
};

If I don't call AddRef() as shown above, then 'OtherObject' will
automatically release itself, leading to all kinds of bad problems. My
desire is to use 'OtherObject' which is another "live" object. It seems as
though the ATL 'xxxPtr' types will automatically call Release() when they go
out of scope - true? I guess this is kind of good (it makes us have to write
one less line of code!), but its bad because the resulting code no longer
has matching calls to AddRef() and Release().

Assuming I was using a more basic type like LPUNKOWN, I assume I wouldn't
have this problem. Since I'm using Apartment Threading in my COM library,
should I still call OtherObject->AddRef() at the beginning of the method and
OtherObject->Release() at the end? My guess is: technically - no, conforming
to COM guidelines - yes.

I would appreciate any input on this
Thanks!

 

Re:"Smart Pointer" reference counting


The subject of BCB smart pointers has been covered at least once before in
this group - not that I'm blaming you for bringing it up again as I just
searched groups.google.com and couldn't find the posting I made last time!

To recap:

The right thing to do with an interface pointer passed as an input parameter
is to leave it alone or both AddRef and Release it. The COM Specification,
Chaper 2, Reference Counting, Rule 2a says:

"Rule 2a: In-parameters to functions. The copy of an interface pointer which
is passed as an actual parameter to a function has a lifetime which is
nested in that of the pointer used to initialize the value. The actual
parameter therefore need not be separately reference counted."

Although the rule says *need* not, which implies that an AddRef/Release pair
is technically allowed, it is logical that you would add an AddRef and a
Release in the server code AND/OR an AddRef and a Release in the client
code. What BCB actually does is, by using its smart pointers,
is add an AddRef in the client code and a Release in the server code, which
is the root of the problem. This is the reason BCB inserts a warning comment
into the server code for a function with a smart pointer input
parameter.

This smart pointer behaviour works if both client and server are written in
C++Builder but breaks down when one or other is coded in another development
tool (surely one of the main reasons for using COM!). (Incidentally, IMHO
this means Borland have broken the rules of COM, thus it IS a bug.)

My solution, inspired by the compiler generated comment, is to immediately
AddRef any smart pointers inside the BCB server code AND to Release smart
pointers after calling in the client code e.g.

void somefunc()
 {
    ISmartPtr p = Something;
    Foo->Bar(p);    // Copy ctor of smart pointer implicitly calls AddRef.
    p->Release();    // Balance smart pointer AddRef
 }

 // Builder's generated code:
 interface IFoo:
 {
     Bar(ISmartPtr p)
     {
         p->AddRef();    // Balance smart pointer Release.
         // Other code.
         // Dtor of smart pointer implicitly calls Release.
     }
 };

Now, if either the client or server code is replaced by non-BCB code, the
interface is still destroyed at the correct point.

The solution is quite {*word*193}, but it works (although see note below). What
would be nicer is a kind of TComInterfaceInParam type, which leaves the
reference count alone on construction and destruction, or, simpler but
without the other advantages of TComInterface, just passing a raw interface
pointer.

Note that for parameters that end up as pointers to TComInterface (e.g.
ISmartPtr*) - typically output parameters - no reference count manipulation
is needed (even though the compiler still spits out its warning comment into
the server code) since the copying of a such a parameter is just the copying
of a raw pointer which does not create a new smart pointer and thus does not
change the reference count.

NOTE: The only occassion it wouldn't work is when Bar returns a failure
HRESULT, which the smart pointer Foo converts into an exception. In that
case, execution will jump to an exception handler, the p.Release() will
never get called and p will leak. Dealing correctly with this situation
requires a catch or (yikes!) __finally block.

Hope this sheds some light.

--
Garry Lancaster
Codemill Ltd
mailto: glancas...@NOSPAMPLEASEcodemill.net
Visit our web site at http://www.codemill.net

Quote
David Bridges <brid...@advpubtech.com> wrote in message

news:3a8ed627$1_1@dnews...
Quote
> I'm not sure why the "smart interface pointers" work the way they do. For
> example, I have the following method  in one of my COM objects (CPPB 5.0
> project):

> STDMETHODIMP TMyObjectImpl::Method1(IMyObjectPtr OtherObject, TOLEBOOL*
> Result)
> {
>   // OtherObject must have its reference count incremented because
>   // it will automatically Release() when it goes out of scope.
>   // Why did Borland do this????
>   OtherObject->AddRef();

>   // Do stuff...
>   return S_OK;
> };

> If I don't call AddRef() as shown above, then 'OtherObject' will
> automatically release itself, leading to all kinds of bad problems. My
> desire is to use 'OtherObject' which is another "live" object. It seems as
> though the ATL 'xxxPtr' types will automatically call Release() when they
go
> out of scope - true? I guess this is kind of good (it makes us have to
write
> one less line of code!), but its bad because the resulting code no longer
> has matching calls to AddRef() and Release().

> Assuming I was using a more basic type like LPUNKOWN, I assume I wouldn't
> have this problem. Since I'm using Apartment Threading in my COM library,
> should I still call OtherObject->AddRef() at the beginning of the method
and
> OtherObject->Release() at the end? My guess is: technically - no,
conforming
> to COM guidelines - yes.

> I would appreciate any input on this
> Thanks!

Re:"Smart Pointer" reference counting


Salutation,

If you look at the definition of a borland smart pointer TComInterface (see
file utilcls.h in the include directory) you will notice those constructor

  TComInterface(IUnknown* p) : intf(0)
  {
    *this = p;
  }

  TComInterface(IDispatch* p) : intf(0)
  {
    *this = p;
  }

  TComInterface(TVariant var) : intf(0)
  {
    *this = var;
  }

  TComInterface(const TComInterface<T, piid>& src)
  {
    if ((intf = src.intf) != 0)
      intf->AddRef();
  }

You can see that any raw interface pointer (IUnknown or IDispatch) doesn't
get a addref increment when assigned to the smart pointer. Another smart
pointer from borland however will increase the reference count.

A real smart pointer (CComPtr<T>) will increase the reference count in all
those cases. Borland doesn't do that because they don't use raw interface
pointer in their COM method. This is actually a problem if your client is
non Borland (say VC or VB) since they will call your Com object with raw
interfaces only (since they only import in those format).

All smart pointers (borland included) release their interface once out of
scope.

I usually avoid creating Com object using Borland because of this issue.
There are workaround such as adding a reference if your object will be use
by non-borland client. You could also check the return value of the AddRef
method to balance the call.

// Force a reference to get the number of reference + 1
int RefCount = OtherObject->AddRef();

// We need only 2 in our situation
if( RefCount > 2 )
{
    // We expect 2 references at most since we will loose one
    // once the smart pointer is out of scope
    while( RefCount > 2 )
    {
        RefCount = OtherObject->Release();
    }

Quote
}

Ugly as hell but it will work with all clients.

Hope this helps,

Francois Belair

Re:"Smart Pointer" reference counting


Come to think of it, forget my solution about balancing the reference unless
you always know the number of reference on your client object and that's
impossible :-)

Why do you use a smart pointer as a parameter in the first place? Can't you
use this instead?

STDMETHODIMP TMyObjectImpl::Method1(IMyObject* pOtherObject, TOLEBOOL*
Result)
It will be implemented as this when you import your com object in BCB

STDMETHODIMP TMyObjectImpl::Method1(IMyObjectPtr OtherObject, TOLEBOOL*
Result)

In VC it will still remain as your definition (minus the TOLEBOOL which is
proprietary to borland)

but if you pass only raw interface in all your clients (VC, BCB, etc) i
think you should not have any problem.

So you call your object in BCB that way

IMyObject* pObject;
TOLEBOOL SomeResult
...
MyObject->Method1(pObject, &SomeResult);

Well i've made a VC com object and call it from borland that way and i don't
have memory issues with it. The return value of AddRef is always a good way
to check if you leaking or releasing too much memory.

Hope this helps,

Re:"Smart Pointer" reference counting


Hi David,
you are correct, the smart pointer arguments for Builder COM interface methods
is a particularly {*word*193} bug.

This has been acknowledged by Borland and a Borland representative has promised
it will be fixed no later than Builder 6, but we'll see...

Anyway, there was a long thread discussing this problem, you can find it at:
http://www.tamaracka.com/search.htm

Search for 'PLEASE READ. BUILDER COM SUPPORT BROKEN'

Phil.

On Sat, 17 Feb 2001 11:52:25 -0800, "David Bridges" <brid...@advpubtech.com>
wrote:

Quote
>I'm not sure why the "smart interface pointers" work the way they do. For
>example, I have the following method  in one of my COM objects (CPPB 5.0
>project):

Re:"Smart Pointer" reference counting


Actually, at least in BCB4, assignment of IUnknown*, IDispatch*, or
TVariant to an instance of the smart pointer, as happens in the line
  *this = p;
will result in a call to p->QueryInterface. Most QueryInterface
implementations will increment the reference count.

Paul

Quote
Francois Belair wrote:

> Salutation,

> If you look at the definition of a borland smart pointer TComInterface (see
> file utilcls.h in the include directory) you will notice those constructor

>   TComInterface(IUnknown* p) : intf(0)
>   {
>     *this = p;
>   }

>   TComInterface(IDispatch* p) : intf(0)
>   {
>     *this = p;
>   }

>   TComInterface(TVariant var) : intf(0)
>   {
>     *this = var;
>   }

>   TComInterface(const TComInterface<T, piid>& src)
>   {
>     if ((intf = src.intf) != 0)
>       intf->AddRef();
>   }

> You can see that any raw interface pointer (IUnknown or IDispatch) doesn't
> get a addref increment when assigned to the smart pointer. Another smart
> pointer from borland however will increase the reference count.

> A real smart pointer (CComPtr<T>) will increase the reference count in all
> those cases. Borland doesn't do that because they don't use raw interface
> pointer in their COM method. This is actually a problem if your client is
> non Borland (say VC or VB) since they will call your Com object with raw
> interfaces only (since they only import in those format).

> All smart pointers (borland included) release their interface once out of
> scope.

Other Threads