[signals2] tracking intrusively ref-counted object
Hello, In my project I've got intrusively ref.counted objects (particularly, ATL COM-objects), which listen to signals of some asynchronous subsystem. Currently, when these objects connect to the signals, they bind their own intrusive smart-ptrs to the slot functor, like this: // pseudo code Object::init() { asyncSubsystem_.onSignal_.connect(bind(&Object::handleSignal, intrusive_from_this...)); // ensure the slot wouldn't bound to a dead object } Of course, this implies that the 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. Now I'd like to eliminate the need for this additional "finalization". For this purpose I have to: a) bind plain "this" to the slot functor -- so that the signal wouldn't be a "strong" client of Object b) eliminate race condition between slot invocation (coming from asyncSubsystem's thread) and the destruction of Object caused by resetting of user's intrusive ptr I tried to think of different ways to solve (b), but nothing seems to work so far... So my question is whether (b) is ever possible, and what would be the right solution for the whole issue. Thank you, Igor'.
On Saturday 05 September 2009, Igor R wrote:
Hello,
In my project I've got intrusively ref.counted objects (particularly, ATL COM-objects), which listen to signals of some asynchronous subsystem. Currently, when these objects connect to the signals, they bind their own intrusive smart-ptrs to the slot functor, like this: // pseudo code Object::init() { asyncSubsystem_.onSignal_.connect(bind(&Object::handleSignal, intrusive_from_this...)); // ensure the slot wouldn't bound to a dead object }
Of course, this implies that the 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.
If you can access the value of the reference count, you could have your slot check to see if the reference count has dropped to 1 (assuming the slot takes the reference-counted pointer by reference). If the use count is one, the slot could disconnect itself.
If you can access the value of the reference count, you could have your slot check to see if the reference count has dropped to 1
Actually, you mean I have to override object's release() method -- as this's the only piece of code that knows when ref.count drops to 1. I'm still not sure I understand how to make this solution thread-safe. Lets consider the following scheme: struct object { void init() { selfAncor_ = some_intrusive_ptr<object>(this); connection1_ = signal1_.connect(&handle, ref(selfAncor_)); connection2_ = signal2_.connect(&handle, ref(selfAncor_)); } int release() { do_release(); if (refcount_ == 1) { connection1_.disconnect(); connection2_.disconnect(); // the following line is unsafe, because some slot invocation might be in progress! selfAncor_.reset(); } } }; Thanks!
On Sunday 06 September 2009, Igor R wrote:
Actually, you mean I have to override object's release() method -- as this's the only piece of code that knows when ref.count drops to 1. I'm still not sure I understand how to make this solution thread-safe. Lets consider the following scheme:
struct object { void init() { selfAncor_ = some_intrusive_ptr<object>(this); connection1_ = signal1_.connect(&handle, ref(selfAncor_)); connection2_ = signal2_.connect(&handle, ref(selfAncor_));
No, you want to bind a copy of the smart pointer to the slot by value. Making the slot accept it by reference just avoids creating another temporary copy during invocation, which makes the reference counting more complicated.
}
int release() { do_release(); if (refcount_ == 1) { connection1_.disconnect(); connection2_.disconnect(); // the following line is unsafe, because some slot invocation might be in progress! selfAncor_.reset();
No, there's still a copy bound to the slot which won't be destroyed until the invocation is finished and the signal garbage collects the disconnected slot from its slot list.
} }
struct object { void init() { selfAncor_ = some_intrusive_ptr<object>(this); connection1_ = signal1_.connect(&handle, ref(selfAncor_)); connection2_ = signal2_.connect(&handle, ref(selfAncor_));
No, you want to bind a copy of the smart pointer to the slot by value. Making the slot accept it by reference just avoids creating another temporary copy during invocation
Excuse my ignorance, how can I make "the slot accept it by reference" if I bind "a copy of the smart pointer to the slot by value"?
On Sunday 06 September 2009, Igor R wrote:
struct object { void init() { selfAncor_ = some_intrusive_ptr<object>(this); connection1_ = signal1_.connect(&handle, ref(selfAncor_)); connection2_ = signal2_.connect(&handle, ref(selfAncor_));
No, you want to bind a copy of the smart pointer to the slot by value. Making the slot accept it by reference just avoids creating another temporary copy during invocation
Excuse my ignorance, how can I make "the slot accept it by reference" if I bind "a copy of the smart pointer to the slot by value"?
I mean something like: some_intrusive_ptr<object> selfAncor_; void handle(const some_intrusive_ptr<object> &this); // accept by reference signal1_.connect(&handle, selfAncor_); // bind value
I mean something like:
some_intrusive_ptr<object> selfAncor_; void handle(const some_intrusive_ptr<object> &this); // accept by reference signal1_.connect(&handle, selfAncor_); // bind value
Ok, I see now - you propose to define an additional formal parameter, while I bound selfAncor_ in place of "this"... Meanwhile, it seems that all this scheme is not feasible in my case, because of the nature of COM objects: even though one can override Release() method that decrements ref.count, it's still impossible to track an arbitrary value of ref.count :-(.
b) eliminate race condition between slot invocation (coming from asyncSubsystem's thread) and the destruction of Object caused by resetting of user's intrusive ptr
I tried to think of different ways to solve (b), but nothing seems to work so far... So my question is whether (b) is ever possible, and what would be the right solution for the whole issue.
Thank you,
Igor'.
Have the use of an object only go through an owning pointer. Resetting the pointer to drop the object will still allow pending calls to finish (so it has to watch its own pointers while stuff is changing around it), and the destructor won't be called until all calls are done. If your use is some more exotic situation, just make sure you follow the principle of the caller establishing ownership first. Resetting the data structure's pointer won't cause destruction (yet). --John TradeStation Group, Inc. is a publicly-traded holding company (NASDAQ GS: TRAD) of three operating subsidiaries, TradeStation Securities, Inc. (Member NYSE, FINRA, SIPC and NFA), TradeStation Technologies, Inc., a trading software and subscription company, and TradeStation Europe Limited, a United Kingdom, FSA-authorized introducing brokerage firm. None of these companies provides trading or investment advice, recommendations or endorsements of any kind. The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material. Any review, retransmission, dissemination or other use of, or taking of any action in reliance upon, this information by persons or entities other than the intended recipient is prohibited. If you received this in error, please contact the sender and delete the material from any computer.
participants (3)
-
Frank Mori Hess
-
Igor R
-
John Dlugosz