Contribution: ThreadOwnershipTracker


#1

In order to get hold of a serious bug in my current project, I needed to track the creation and destruction of objects to make sure they are deleted on the same thread that created them. I came up with the following little template that basically works the same way the Juce leak detectors work and is very easy to add to existing classes.

Please read the class comment below, why thread ownership is such an important thing for so many objects. You might be surprised what results you get if you add this to your project for a test.

Jules, please feel free to add it to the library and rename it as you see fit, if you like.

[code]#ifndef ThreadOwnershipTracker_h
#define ThreadOwnershipTracker_h

#include “JuceHeader.h”

/**
ThreadOwnershipTracker detects cases where an object is deleted on a thread
different from the one it was created on. Thread ownership tracking is enabled
and disabled using the JUCE_CHECK_MEMORY_LEAKS switch.

Proper thread ownership is vital for all objects that inherit from or 
include one or more members of class CriticalSection, Thread, AudioProcessor,
Component and all GUI objects in general. Deleting these objects on the wrong thread
is dangerous and may lead to undefined results that are very hard to debug.
It is a good idea to track all of your non-trivial objects at least for a
while until your system design has settled.

*/
template
class ThreadOwnershipTracker
{
public:

ThreadOwnershipTracker () throw()
    : tid (Thread::getCurrentThreadId())
{
}

~ThreadOwnershipTracker ()
{
    if (Thread::getCurrentThreadId() != tid)
    {
        DBG("*** WARNING: " << OwnerClass::getOwnerClassName() << " instance deleted on a foreign thread!");
        /** This means your object was not deleted on the thread it was 
            originally created on. See class comments for more details. */
        jassertfalse;
    }
}

private:

Thread::ThreadID tid;

};

#if (DOXYGEN || JUCE_CHECK_MEMORY_LEAKS)
/** This macro lets you embed a ThreadOwnershipTracker inside a class.
Thread ownership tracking is enabled and disabled automatically together
with the JUCE_CHECK_MEMORY_LEAKS switch.

  To use it, simply declare a JUCE_REQUIRE_THREAD_OWNERSHIP(YourClassName) 
  inside a private section of the class declaration. E.g.

  @code
  class MyClass
  {
  public:
      MyClass();
      void blahBlah();

  private:
      JUCE_REQUIRE_THREAD_OWNERSHIP (MyClass);
  };@endcode

/
#define JUCE_REQUIRE_THREAD_OWNERSHIP(OwnerClass)
friend class ThreadOwnershipTracker;
static const char
getOwnerClassName() throw() { return #OwnerClass; }
ThreadOwnershipTracker JUCE_JOIN_MACRO (threadOwnershipTracker, LINE);
#else
#define JUCE_REQUIRE_THREAD_OWNERSHIP(OwnerClass)
#endif

#endif // ThreadOwnershipTracker_h
[/code]


#2

Nice trick!

TBH I can’t think of many of the library classes where I could enforce that constraint without annoying someone… but certainly in an app it could be pretty handy!


#3

In fact I have created a thread in my app that is DEDICATED just to deleting other threads objects!

:lol:


#4

Sort of garbage collector? As long as these objects do not contain locks and UI objects, no problem.

If a complex object is deleted on a foreign thread, you are potentially running the risk of already beginning to tear down something that is still being built on another thread. You /could/ secure this with extra locks and some status tracking, but thread ownership is a lot easier to protect against this.

Native window peers, for instance, must be created and deleted on the message thread, or your app is doomed (sooner or later). I suspect that Juce Components also require this.

Of course you don’t need this for simple objects like messages, all sorts of plain data structures, etc.


#5

Yes exactly. I rolled my own ‘reference counted object’ that sends itself to another thread for deletion when the references drop to zero, so I dont bog down the UI or audio i/o callback.


#6

I haven’t thought of extending this to threads, but I tackled a similar problem with reference counted objects that manage OpenGL items. I use a referencecountedobject that holds the object I’m concerned with, and a pointer to a pool. When the references reach zero, it returns the object to the pool, which I can then manage on the correct thread.

This used to be more of a problem. The first threaded system I dealt with insisted all memory allocated on one thread be deleted on the same thread - it was a pain.

I might try the thread checker class. BTW, Jules - that point about it irritating people? I’m still gradually pinning down reams of leaked object assertions that happen when my app shuts down. Since they only happen on shutdown, I think they’re basically just a nuisance anyway. I’m just chasing them because you know better than me, and they’re irritating.

Bruce