Smart Pointers and Multithreading

Posted on 1997-10-29
Last Modified: 2013-11-20
I have a SmartPtr class that implements reference counting and garbage collections. First of all, I am weary about it's completeness.  Is there a complete SmartPtr class that acts exactly (or nearly) like a regular pointer available.  Second, since threads each have their own stack, are smart pointers useful for multithreading?  It seems to me that since threads share resources (heap, GDI, etc), the smart pointer would be ideal for multithreading memory management.  Ultimately, I'm looking for a complete smart pointer and a good discussion (or book/article) on the various uses of smart pointers, including their applicability to multithreading.
Question by:JHaack
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 2
LVL 15

Expert Comment

by:Tommy Hui
ID: 1309028
One good reference I saw is in Scott Meyer's More Effective C++: Item 28 on page 159. Actually, his books are really good (Effective C++).


Author Comment

ID: 1309029
I'm looking for the application of smart pointers in multithreading.  I agree that "More Effective C++" is a good reference on smart pointers, but it doesn't really provide me with any information that I didn't already know.  As I indicated in the question, I have already implemented a smart pointer.  Beyond that, I'm looking for a commercial-level/standard smart pointer and, most importantly, a discussion on the application of smart pointers in multithreading.

Accepted Solution

anichini earned 170 total points
ID: 1309030
I've implemented smart pointer classes before for multithreaded environments. The only change you need to make for multithreading to the basic smart pointer idiom is wrap each increment and decrement/check with some sort of synchronization primitive. A good candidate on Win32 for these operations are InterlockedIncrement/InterlockedDecrement. (On non-Win32 platforms, you could use a semaphore for the ref count, or wrap your increments/decrements with a mutex.)

So your base reference-counted object would look something like this: (I've simplified it for brevity)

class CRefObject
{ public: void AddRef() { InterlockedIncrement(&m_refCount); }
          void Release() { if(InterlockedDecrement(&m_refCount) == 0) { DeleteThisObject(); }
  private: long m_refCount;

Then, assuming your existing smart pointer class already called the AddRef()/Release() routines, you really don't need any changes to your smart pointers unless you share them between threads. In that case, you must wrap the smart pointers copy constructors/assignment operators/anything that can change what the smart pointer points to with some sort of syncronization (a critical section in Win32 would do nicely).

Now I have to convince you why this is thread safe. The Interlocked* routines prevent this situation:

Object meef has a ref count of 2 because each thread has a smart pointer which references it. Both threads are simultaneously trying to create another reference to the object by copying their respective smart pointers into new smart pointers. Here is a time line of their actions through the copy constructors of the smart pointers (time goes down):

    Thread 1               Thread 2
    fetches ref count (temp= 2)
    increments it (temp++)        
    (task switch)
                           fetches ref count (temp = 2)
                           increments it (temp++)
                           stores ref count (meef.refCt = 3 now)
                           (task switch)
    stores ref count (meef.refCt = 3 now)

Note that the ref count should be 4, but its 3 because of inopportune task switching. The Interlocked* routines make the increments and decrements/checking atomic, so this can't happen.

Now, when I first started thinking about these issues I was convinced that there could be a case where one thread would decrement the ref count to zero, while at the same time another thread wants to increment the ref count by one, so the object gets deleted when it shouldn't. But further analysis shows that this should never happen - because the second thread should NOT have a smart pointer to the object in the first place, and thus can't increment its ref count at all (because remember, at the beginning of this whole situation, the ref count of the object would have to be at 1. Which means the first thread's smart pointer is the only reference to that object, and once that reference is released, the object should rightly be deleted).

Another interesting issue is when you have a smart pointer that is shared between multiple threads, i.e. you have a shared data area that contains a smart pointer. But I've decieded that this presents no problem - as long as the smart pointer is only created once and deleted once, it should be fine. The key is to remember is the reference is held with the smart pointer, not with the thread itself.

I also was able to get the syntax of using the smart pointer to be consistent with normal pointer syntax for every case except for casting to a derived class. Let me illustrate:

Normal method:  pMeef = (CMeef *) pFoo; // pFoo is a pointer to a base type of pMeef
Smart Pointer method pMeef = (CMeef *) (CFoo *) pFoo; // pFoo is a smart pointer to a base type of pMeef

I don't have the code handy but I could post it later if you want.
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!


Expert Comment

ID: 1309031
whoops, it screwed up my spacing on the timeline:

Thread 1: fetches ref count (temp= meef.m_nRefCount; // == 2)
Thread 1: increments it (temp++)
(task switch)
Thread 2: fetches ref count (temp = meef.m_nRefCount; // == 2)
Thread 2: increments it (temp++)
Thread 2: stores ref count (meef.m_nRefCount = temp; // == 3)
(task switch)
Thread 1: stores ref count (meef.m_nRefCount = temp; // == 3!!)


Author Comment

ID: 1309032
I'm pretty sure I can synchronize my smart pointer to be thread safe.  However, I am a little unclear about how to share smart pointers between threads.  I've always used handles to create shared pointers.  Isn't there a problem in passing pointers between threads?  Specifically, I want to allocate data on the heap using a smart pointer in the primary thread.  Secondary threads will then use the heap data, incrementing the refcount when they start and decrementing it when they finish - not worrying about whether the heap should be freed.  If you could provide a snippet of code that illustrates the concept, I would greatly appreciate it.  Otherwise, thank you for the excellent help that you've already provided.

P.S.  Do you know of any good articles/books on smart pointers and multithreading?

Expert Comment

ID: 1309033
I haven't run across any articles or books that deal with smart pointers in a multithreaded context.

generally, this is how I would go about the scenario you describe (this isn't garuanteed to be syntactically correct, but it is conceptually correct):

struct ThreadArgs
  MeefSmartPointer ptr; // MeefSmartPointer is a smart pointer to
                        // type CMeef

void thread1(void *pArg)
  ThreadArgs *p = (ThreadArgs *)pArg;
  MeefSmartPointer ptr = p->ptr; // meef ref count == 3
  delete p; // free up arguments // meef ref count == 2
   // do our thing with our reference to the shared data in ptr
  // when we exit, we decrement meef's ref count

#define STACK_SIZE (1<<14) // stack size of 16K
void main()
  MeefSmartPtr ptr = new CMeef; // increments ref count to 1

  ThreadArgs *p = new ThreadArgs;
  p->ptr = ptr; // ref count = 2 now
  _beginthread(thread1, STACK_SIZE, (void *)p);
  // worker thread deletes arguments
    // do our main thread thing
  // when we exit, we decrement the meef's ref count

This allows both threads to acces the data (the CMeef object) and keeps a reference count to it. As long as the ref counting is implemented as I described above, this is thread safe. Note that since we're never really sharing a smart pointer between two threads (i.e. the main thread and thread1 do not access the passed smart pointer itself at the same time, only the CMeef object), you don't need to worry about synchronizing the smart pointer's copy constructors/assignment operators.


Featured Post

On Demand Webinar: Networking for the Cloud Era

Did you know SD-WANs can improve network connectivity? Check out this webinar to learn how an SD-WAN simplified, one-click tool can help you migrate and manage data in the cloud.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Michael from AdRem Software outlines event notifications and Automatic Corrective Actions in network monitoring. Automatic Corrective Actions are scripts, which can automatically run upon discovery of a certain undesirable condition in your network.…

695 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question