Once upon a time, if my memory doesn't betray me, I read that
IUnknown's AddRef and Release functions should not have any side-
effects, besides incrementing/decrementing the ref.count. This is
because COM proxy might not actually forward these calls to the stub,
or something like that...
In my case I have to override Release method to notify the object when
ref.count drops to 1. I can do this by overriding
CComObjectRootEx<>::InternalRelease(), as described here:
http://support.microsoft.com/kb/218982
but I'd like to be sure it's legitimate.
Thanks.
Well, Release has a mother of all side effects - it destroys the object
when the ref count reaches zero.
> This is
> because COM proxy might not actually forward these calls to the stub,
> or something like that...
The proxy never forwards these calls to the stub, except the very last
Release call when the proxy's own reference count reaches zero and it
destroys itself.
> In my case I have to override Release method to notify the object when
> ref.count drops to 1.
Why? What are you trying to achieve?
--
With best wishes,
Igor Tandetnik
With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925
Too bad... So I can't hook the moment when the reference count drops
to 1 or any other number >0, right?
> Why? What are you trying to achieve?
In my project I've got COM objects, whose sole purpose is to be
wrappers for the corresponding c++ objects: a) every COM wrapper
forwards calls to its implementation object; b) every COM wrapper
listens to some async. signals from the implementation object and
forwards them to COM client(s) through connection points.
The problem origins in (b): I have to ensure that COM wrapper outlives
any async. signal coming from the implementation. The best way to do
this is to bind the CComPtr to the slot functor:
wrapper::init()
{
impl_->listenToSignals(bind(&wrapper::handler, CComPtr<wrapper>
(this)));
}
This implies that the COM-object cannot "die" until it's explicitly
disconnected from the signal, so when the user of my library wants to
get rid of such an object, he must call one more function (say, obj-
>close()) before resetting his smart-ptr to the object -- which is not
just inconvenient, but also threatens the exception safety of user's
code.
I'd like to eliminate the need for this additional "finalization". The
only way I can see is to try and count those "internal" references,
and when the ref.count == internal_refcount, disconnect all the
signals.
Thanks!
Can't you disconnect the signals in wrapper's destructor, without
playing these pointless refcount games? The net effect of your current
scheme (assuming it works) is the same: all external clients release,
refcount reaches internal_refcount, whereupon you disconnect the
signals, which results in Release being called internal_refcount times;
on the last such Release call, refcount reaches zero and the object
destroys itself.
Who would call the destructor? If I pass self-CComPtr's to the
async.subsystem, the ref.count would never reach zero.
> The net effect of your current scheme (assuming it works) is the same: all external clients release,
> refcount reaches internal_refcount, whereupon you disconnect the signals, which results in Release being called internal_refcount times
No. I just didn't elaborate on how the signalling subsystem works.
Disconnecting "slot" (i.e. the callback functor) from "signal" doesn't
cause an immediate (synchronous) destruction of the slot - this
wouldn't be thread-safe. The async. callback may be already in
progress, because it might be started just before I disconnected the
signal, and it would end gracefully without crash, as the wrapper is
still afloat -- thanks to CComPtr bound to the slot. Now when the
async.callback ends - and the slot is already disconnected - the
signalling subsystem disposes the disconnected slot, and CComPtr bound
to it dies.
Well, this could be an elegant automatic lifetime management scheme,
if only I could track the ref.count :).
I think your scheme won't work, but for a different reason than the one
you are concerned about.
The whole proxy issue is a red herring. While it's true that you don't
see every single AddRef and Release call made on a proxy, you do know
that, as long as at least one proxy is alive, there is a strong
reference on your object; when all the proxies are destroyed, they'll
release all those references. So the count will be above
internal_refcount with the proxies (or, in fact, direct clients), and
will go down to internal_refcount once all clients disconnect.
But there's a different problem. As those CComPtr instances you use for
"internal" references are copied (at the very least, the temporary
instance you pass to bind() must be copied), there will be superfluous
AddRef/Release pairs of calls. This will briefly raise the refcount
above internal_refcount, then drop it back down. In your scheme, this
will prematurely trigger event disconnection logic.
Here's one way to work around this difficulty. Create two C++ classes -
one is what you have now sans slots, the other containing slots and a
trivial IUnknown implementation with its own refcount (let's call it
event sink). The sink would hold a regular C++ pointer to the main
class, and would AddRef it exactly once in its constructor and Release
exactly once when its own refcount reaches zero. You may also find it
convenient to forward actual events to the main class, so that all the
business logic is concentrated in one place. The main class may create
the sink class on the heap, or hold it as a data member. The main class
would disconnect events when its reference count drops to 1, and destroy
itself when it drops to 0.
Ok, I see now, the proxy certainly doesn't see my "internal" refs
anyway...
> Here's one way to work around this difficulty. Create two C++ classes -
> one is what you have now sans slots, the other containing slots and a
> trivial IUnknown implementation with its own refcount (let's call it
> event sink).
This is very nice idea! I think it can be even simplified a bit.
Actually, what I need is an additional "client" to my wrapper that
would hold exactly 1 reference - until all the slots are disconnected.
Once this's achieved, the wrapper itself can continue serving as the
slot implementation, without the need to split it. So probably the
following simple scheme would be sufficient (or do I miss
something?) :
class wrapper : // al the ATL stuff
{
public:
void init()
{
holder_.reset(take_iunknown_from_this..., mem_fn
(&IUnknown::Release)); // holder_ will be copied, but no AddRef's will
occur
asyncSubsys_->listenToSignal(bind(protect(bind(&wrapper::handle,
this)), holder_)); // bind holder_ as a superfluous param to every
async. event source
}
ULONG InternalRelease()
{
ULONG ref = __super::InternalRelease();
if (ref == 1)
{
holder_.reset();
disconnectSignals();
}
}
private:
shared_ptr<IUnknown> holder_;
};
Thanks a lot for your assistance!
..."take_iunknown_from_this" should AddRef() it of course.