Solved

Smart Pointers and Multithreading

Posted on 1997-10-29
6
661 Views
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.
0
Comment
Question by:JHaack
  • 3
  • 2
6 Comments
 
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++).

0
 

Author Comment

by:JHaack
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.
0
 
LVL 2

Accepted Solution

by:
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.
0
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 2

Expert Comment

by:anichini
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!!)

 
0
 

Author Comment

by:JHaack
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?
0
 
LVL 2

Expert Comment

by:anichini
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
  while(1)
  {
   // 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
  while(1)
  {
    // 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.

0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Introduction: Ownerdraw of the grid button.  A singleton class implentation and usage. Continuing from the fifth article about sudoku.   Open the project in visual studio. Go to the class view – CGridButton should be visible as a class.  R…
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.
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…

747 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

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now