Click here to Skip to main content
15,878,945 members
Articles / Programming Languages / C

Thread-safe Smart Pointer

Rate me:
Please Sign up or sign in to vote.
3.46/5 (8 votes)
14 Jul 2009CPOL4 min read 49.9K   364   36  
With this thread-safe smart pointer, you can use an object of any type in a multithreaded environment.
//////////////////////////////////////////////////////////////////////////
//
// Author - Oleg Fedchenko
//
// www.MulticoreWinsoft.com
//
//////////////////////////////////////////////////////////////////////////
#pragma once
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>
//////////////////////////////////////////////////////////////////////////
template<typename T>
class CThreadPtr
{
public:
    class CLocker : public boost::noncopyable
    {
    public:
        CLocker(const CThreadPtr& threadPtr)
        :m_threadPtr( threadPtr)
        {
			::WaitForSingleObject( m_threadPtr.m_mutexInternal, INFINITE);
			if (m_threadPtr.m_mutexObj)
				::WaitForSingleObject( m_threadPtr.m_mutexObj, INFINITE);
        }
		~CLocker()
		{
			if (m_threadPtr.m_mutexObj)
				::ReleaseMutex( m_threadPtr.m_mutexObj);
			::ReleaseMutex( m_threadPtr.m_mutexInternal);
		}
        T& operator*() const
        { 
            return *m_threadPtr.m_obj;
        }
        T* operator->() const
        { 
            return &*m_threadPtr.m_obj;
        }
    private:
        const CThreadPtr<T>& m_threadPtr;
    };
public:
    CThreadPtr()       : m_mutexInternal(::CreateMutex(NULL, FALSE, NULL)),
						 m_mutexObj(0),
						 m_obj(){}
    CThreadPtr(T* obj) : m_mutexInternal(::CreateMutex(NULL, FALSE, NULL)),
						 m_mutexObj(::CreateMutex(NULL, FALSE, NULL)),
						 m_obj(obj){}

    CThreadPtr(const CThreadPtr& rh) :	m_mutexInternal(::CreateMutex(NULL, FALSE, NULL)),
										m_mutexObj(0),
										m_obj()
	{
		if (::WaitForSingleObject( rh.m_mutexInternal, INFINITE) == WAIT_OBJECT_0)
		{
			// copy if rh is not destroyed
			if (rh.m_mutexObj)
			{
				::WaitForSingleObject( rh.m_mutexObj, INFINITE);
				if (DuplicateHandle( rh.m_mutexObj, &m_mutexObj))
				{
					m_obj = rh.m_obj;
				}
				::ReleaseMutex( rh.m_mutexObj);
			}
			::ReleaseMutex( rh.m_mutexInternal);
		}
	}

	~CThreadPtr()
	{
		::WaitForSingleObject( m_mutexInternal, INFINITE);
		if (m_mutexObj)
		{
			::WaitForSingleObject( m_mutexObj, INFINITE);
			m_obj.reset();
			::CloseHandle(m_mutexObj);
			m_mutexObj = 0;
		}
		::CloseHandle(m_mutexInternal);
	}

    CThreadPtr& operator=(const CThreadPtr& rh)
	{
		// do not copy myself
		if (&rh == this)
			return *this;
		::WaitForSingleObject( m_mutexInternal, INFINITE);
		// clear my obj
		if (m_mutexObj)
		{
			::WaitForSingleObject( m_mutexObj, INFINITE);
			m_obj.reset();
			::CloseHandle(m_mutexObj);
			m_mutexObj = 0;
		}
		// copy if rh is not destroyed
		if (::WaitForSingleObject( rh.m_mutexInternal, INFINITE) == WAIT_OBJECT_0)
		{
			// copy if not null
			if (rh.m_mutexObj)
			{
				::WaitForSingleObject( rh.m_mutexObj, INFINITE);
				if (DuplicateHandle( rh.m_mutexObj, &m_mutexObj))
				{
					m_obj = rh.m_obj;
				}
				::ReleaseMutex( rh.m_mutexObj);
			}
			::ReleaseMutex( rh.m_mutexInternal);
		}
		::ReleaseMutex( m_mutexInternal);
		return *this;
	}

	CLocker operator->() const
    {
        return *this;
    }

    CLocker operator*() const
    {
        return *this;
    }

    void reset()
    {
		::WaitForSingleObject( m_mutexInternal, INFINITE);
		if (m_mutexObj)
		{
			::WaitForSingleObject( m_mutexObj, INFINITE);
			m_obj.reset();
			::CloseHandle(m_mutexObj);
			m_mutexObj = 0;
		}
		::ReleaseMutex( m_mutexInternal);
    }

    void reset(T* obj)
    {
		::WaitForSingleObject( m_mutexInternal, INFINITE);
		if (m_mutexObj)
			::WaitForSingleObject( m_mutexObj, INFINITE);
        m_obj.reset(obj);
		// new mutex for a new obj
		if (m_mutexObj)
			::CloseHandle(m_mutexObj);
		m_mutexObj = ::CreateMutex(NULL, FALSE, NULL);
		//
		::ReleaseMutex( m_mutexInternal);
    }
    operator bool () const
    {
		CLocker lock(*this);
        return (bool)m_obj;
    }
    bool operator! () const
    {
		CLocker lock(*this);
        return !m_obj;
    }
private:
	static bool DuplicateHandle( HANDLE src, HANDLE* dest)
	{
		HANDLE curProcess = ::GetCurrentProcess();
		return ::DuplicateHandle(
			curProcess,	//HANDLE hSourceProcessHandle,
			src,		//HANDLE hSourceHandle,
			curProcess,	//HANDLE hTargetProcessHandle,
			dest,		//LPHANDLE lpTargetHandle,
			0,						//DWORD dwDesiredAccess, // ignored
			FALSE,					//BOOL bInheritHandle,
			DUPLICATE_SAME_ACCESS	//DWORD dwOptions
			) == TRUE;
	}
private:
	HANDLE m_mutexInternal;
	HANDLE m_mutexObj;
    boost::shared_ptr<T>  m_obj;
};
//////////////////////////////////////////////////////////////////////////

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Oleg Fedchenko is a professional in the field of multicore optimization and multicore development for Windows.
Please, visit this Web site http://MulticoreWinSoft.com

Comments and Discussions