Am I doing something wrong here? Boost threads & intrusive_ptr crash

Hi, I'm using an event loop type system which I've implemented myself. This is due to specific needs. Event loop is fairly simple, it has several types of events: - Timer based events (relative or exact, repeating, etc) - IO based events - Notification based events Notifications are events which can come from any thread, and get processed on in the main loop. Here is the function which is used to post a notification. REF(t) is simply a macro for boost::intrusive_ptr<t>. void Loop::postNotification (REF(INotificationSource) note, bool urgent) { using namespace boost; typedef boost::mutex::scoped_lock scoped_lock; // Lock the event loop notification queue // Add note to the end of the queue // Interrupt event loop thread if urgent // We should probably lock around m_currentThread, it might not be set before we read it..? if (this_thread::get_id() == m_currentThread) { note->processEvents(this, NOTIFICATION); } else { { // Enqueue the notification to be processed scoped_lock lock(m_notifications.lock); m_notifications.sources->push(note); (*) } if (urgent) { // Interrupt event loop thread so that it processes notifications more quickly m_urgentNotificationPipe->notifyEventLoop(); } } } I'm experiencing a very infrequent crash at the line marked with (*). The note is a reference counted pointer (boost::intrusive_ptr), but by the time we get here, this pointer is invalid, even though there are still references to it in the system. The odd thing is, further up the chain (and on the same thread), I'm checking to ensure that note is valid, i.e. //... display update system, separate thread from event-loop. ensure(ctx->notificationSource); loop->postNotification(ctx->notificationSource); //... After writing this far in the email, I'm starting to see something where there could be an error. Is it possible that the object reference count is being messed up due to multi-threading issues? I.e. if the object is being passed around on two separate threads, having the reference count incremented and decremented at the same time? Actually, it seems obvious that this could be a problem... Are there any "patterns" for using reference counted objects across different threads? Any feedback is appreciated. Kind regards, Samuel

AMDG Space Ship Traveller wrote:
<snip>
I'm experiencing a very infrequent crash at the line marked with (*). The note is a reference counted pointer (boost::intrusive_ptr), but by the time we get here, this pointer is invalid, even though there are still references to it in the system. The odd thing is, further up the chain (and on the same thread), I'm checking to ensure that note is valid, i.e.
//... display update system, separate thread from event-loop. ensure(ctx->notificationSource); loop->postNotification(ctx->notificationSource); //...
After writing this far in the email, I'm starting to see something where there could be an error. Is it possible that the object reference count is being messed up due to multi-threading issues? I.e. if the object is being passed around on two separate threads, having the reference count incremented and decremented at the same time? Actually, it seems obvious that this could be a problem...
Are there any "patterns" for using reference counted objects across different threads?
shared_ptr's reference count is thread safe. For intrusive_ptr, it depends on how you define the reference count. In Christ, Steven Watanabe

Space Ship Traveller wrote:
Hi, [snip] Here is the function which is used to post a notification. REF(t) is simply a macro for boost::intrusive_ptr<t>.
Can you confirm which of the following REF(INotificationSource) maps to? boost::intrusive_ptr<INotificationSource> note or boost::intrusive_ptr<INotificationSource>& note More concretely, is the parameter: 1 - a reference to an already existing intrusive_ptr 2 - a new intrusive_ptr using a copy constructor 3 - a new intrusive_ptr accepting a raw pointer If it is the third one then there is nothing to stop some particular interleaving of instructions decrementing the ref count to zero between your check and the construction of the parameter. Whether or not this causes an obvious error when you attempt to create a new intrusive_ptr from it will depend a lot of things. For example, I have a test I just made and I can get this to fail hard and to simply create a new intrusive_ptr from an invalid pointer, depending on what I do....
void Loop::postNotification (REF(INotificationSource) note, bool urgent) { using namespace boost; typedef boost::mutex::scoped_lock scoped_lock;
// Lock the event loop notification queue // Add note to the end of the queue // Interrupt event loop thread if urgent
// We should probably lock around m_currentThread, it might not be set before we read it..? if (this_thread::get_id() == m_currentThread) { note->processEvents(this, NOTIFICATION); } else { { // Enqueue the notification to be processed scoped_lock lock(m_notifications.lock); m_notifications.sources->push(note); (*) }
if (urgent) { // Interrupt event loop thread so that it processes notifications more quickly m_urgentNotificationPipe->notifyEventLoop(); } } }
I'm experiencing a very infrequent crash at the line marked with (*). The note is a reference counted pointer (boost::intrusive_ptr), but by the time we get here, this pointer is invalid, even though there are still references to it in the system. The odd thing is, further up the chain (and on the same thread), I'm checking to ensure that note is valid, i.e.
//... display update system, separate thread from event-loop. ensure(ctx->notificationSource); loop->postNotification(ctx->notificationSource); //...
After writing this far in the email, I'm starting to see something where there could be an error. Is it possible that the object reference count is being messed up due to multi-threading issues? I.e. if the object is being passed around on two separate threads, having the reference count incremented and decremented at the same time? Actually, it seems obvious that this could be a problem...
Are there any "patterns" for using reference counted objects across different threads?
Any feedback is appreciated.
Kind regards, Samuel
------------------------------------------------------------------------
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users

On 12/06/2009, at 2:37 AM, Nigel Rantor wrote:
Space Ship Traveller wrote:
Hi, [snip] Here is the function which is used to post a notification. REF(t) is simply a macro for boost::intrusive_ptr<t>.
Can you confirm which of the following REF(INotificationSource) maps to?
boost::intrusive_ptr<INotificationSource> note
This one.
or
boost::intrusive_ptr<INotificationSource>& note
Sometimes I use this form, but not in this case.
More concretely, is the parameter:
1 - a reference to an already existing intrusive_ptr
2 - a new intrusive_ptr using a copy constructor
3 - a new intrusive_ptr accepting a raw pointer
If it is the third one then there is nothing to stop some particular interleaving of instructions decrementing the ref count to zero between your check and the construction of the parameter.
Whether or not this causes an obvious error when you attempt to create a new intrusive_ptr from it will depend a lot of things.
For example, I have a test I just made and I can get this to fail hard and to simply create a new intrusive_ptr from an invalid pointer, depending on what I do....
I appreciate your insight into the situation. I'm currently reviewing the code, but there is definitely some bug like this. I think it is a case of an increment and decrement interleaving in such a way that the increment never occurs, and thus the object is freed even thought there is still a reference to it. Kind regards, Samuel

Space Ship Traveller wrote:
On 12/06/2009, at 2:37 AM, Nigel Rantor wrote:
Space Ship Traveller wrote:
Hi, [snip] Here is the function which is used to post a notification. REF(t) is simply a macro for boost::intrusive_ptr<t>.
Can you confirm which of the following REF(INotificationSource) maps to?
boost::intrusive_ptr<INotificationSource> note
This one.
or
boost::intrusive_ptr<INotificationSource>& note
Sometimes I use this form, but not in this case.
More concretely, is the parameter:
1 - a reference to an already existing intrusive_ptr
2 - a new intrusive_ptr using a copy constructor
3 - a new intrusive_ptr accepting a raw pointer
If it is the third one then there is nothing to stop some particular interleaving of instructions decrementing the ref count to zero between your check and the construction of the parameter.
Whether or not this causes an obvious error when you attempt to create a new intrusive_ptr from it will depend a lot of things.
For example, I have a test I just made and I can get this to fail hard and to simply create a new intrusive_ptr from an invalid pointer, depending on what I do....
I appreciate your insight into the situation. I'm currently reviewing the code, but there is definitely some bug like this.
I think it is a case of an increment and decrement interleaving in such a way that the increment never occurs, and thus the object is freed even thought there is still a reference to it.
As Steven pointed out if the inc/dec code is not thread-safe then the case I pointed out is just one more way it can fail. Happy to look at more code, especially if you can create a minimal failing case... n
participants (3)
-
Nigel Rantor
-
Space Ship Traveller
-
Steven Watanabe