Are smart pointers threadsafe?

AJ
Total Points:
3,752
Status Points:
3,252
Brown Belt
May 8, 2008 9:29 AM PDT
Rate
 
#8 Reply to #7
Just as an FYI, this is what I use.  The code is available via SVN, and you are free to use it.

There are some improvements that can be made to this code, but here it is.


/*
    Copyright 2007-2008 Adrien Guillon.  All Rights Reserved.

    This file is part of TBB Community Code.

    TBB Community Code is free software; you can redistribute it
    and/or modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

    TBB Community Code is distributed in the hope that it will be
    useful, but WITHOUT ANY WARRANTY; without even the implied warranty
    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with TBB Community Code; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    As a special exception, you may use this file as part of a free software
    library without restriction.  Specifically, if other files instantiate
    templates or use macros or inline functions from this file, or you compile
    this file and link it with other files to produce an executable, this
    file does not by itself cause the resulting executable to be covered by
    the GNU General Public License.  This exception does not however
    invalidate any other reasons why the executable file might be covered by
    the GNU General Public License.
*/

#ifndef __TCC_ptr_vector_H
#define __TCC_ptr_vector_H

#include <algorithm>
#include <vector>

#include <tbb/spin_mutex.h>

#include <boost/checked_delete.hpp>


namespace tcc {

/**
 * A vector that stores pointers to "owned objects."  This container assumes
 * ownership of the pointers it holds, and will call delete on them when this
 * object is destructed, or appopriate member function calls are made.
 *
 * The goal of this class is to provide a method of storing objects in an
 * exception safe manner, without using smart pointers.
 *
 * Instantiate the template with the type of object it will contain, not
 * with pointers itself.
 *
 * The Mutex can be any TBB mutex type, including RW types.
 *
 * The deleter template argument is for a function object which
 * implements void operator()(T* x), and deletes the object.  By
 * default, normal C++ delete is called.
 */

template <typename T, typename Container = typename std::vector<T*>, typename Mutex = tbb::spin_mutex, typename Deleter = boost::checked_deleter<T> >
class ptr_vector
{
public:
    typedef Container container_type;
    typedef Mutex mutex_type;
    typedef typename Container::iterator iterator;
  ;   typedef T* element_type;


    /**
     * Construct an empty ptr_vector
     */
    ptr_vector() { }


    /**
     * The destructor calls delete on all contained object pointers.
     *
     * TODO: Should the destructor use the mutex?
     */
    ~ptr_vector()
    {
        if(!empty())
        {
            deleteAll();
        }
    }

    /**
     * Takes ownership of an object.  This container will now own the object,
     * and is responsible for the destruction of the object unless the user
     * destroys it themselves with delete_member(x) or steal_member(x).
     *
     * This function is thread safe.
     */
    T& giveOwnership(T* x)
    {
        { // BEGIN MUTEX
            typename Mutex::scoped_lock lock(_ownedObjectsMutex);
            _ownedObjects.push_back(x);
        } // END MUTEX

        return *x;
    }

    /**
     * Take ownership of an object away from this container.  The object
     * will no longer be owned by this container, and a pointer will be
     * returned to the user.
     *
     * This function is thread safe.
     */
    T* takeOwnership(T& x)
    {
        typename container_type::iterator i;
        T* objectToRemove;

        { // BEGIN MUTEX

             typename Mutex::scoped_lock lock(_ownedObjectsMutex);
             i = std::remove(_ownedObjects.begin(), _ownedObjects.end(), &x);
             objectToRemove = *i;
             _ownedObjects.erase( i, _ownedObjects.end() );
        } // END MUTEX

        return objectToRemove;
    }


    /**
     * Call delete on an object owned by this container.  The object deleted
     * is removed from ownership.
     *
     * This function is thread safe.
     */
    void deleteMember(T& x)
    {
   &nbs p;    T* objectToDelete;
        objectToDelete = takeOwnership(x);
        _deleter(objectToDelete);
    }

    /**
     * Call delete on all objects owned by this container.  All objects
     * deleted are removed from ownership.  A mutex is used to govern
     * access of internal data structures.  This is the same function which
     * is called by the destructor.
     *
     * This function is thread safe.
     */
    void deleteAll()
    {
        { // BEGIN MUTEX

            typename Mutex::scoped_lock lock(_ownedObjectsMutex);

            std::for_each(_ownedObjects.begin(), _ownedObjects.end(), _deleter);

            _ownedObjects.clear();

        } // END MUTEX
    }

    /**
     * Check if this container owns anything.
     *
     * This function is thread safe.
     *
     * @return true if there are no objects owned, false otherwise.
     */
    bool empty() const
    {
        return _ownedObjects.empty();
    }


    /**
     * Swaps the contents of this ptr_vector with a ptr_vector instance
     * provided as an argument.
     *
     * This function is thread safe, however it requires two mutexes,
     * one for this ptr_vector and one for the ptr_vector argument.
     */
    void swap(ptr_vector& x)
    {
        { // BEGIN MUTEX ON x
            typename Mutex::scoped_lock lock_x(x._ownedObjectsMutex);

            { // BEGIN MUTEX ON this
                typename Mutex::scoped_lock lock_this(_ownedObjectsMutex);

                _ownedObjects.swap(x);

            }

        }
    }

    iterator begin() { return _ownedObjects.begin(); }
    iterator end() { return _ownedObjects.end(); }

private:

    /**
     * Actual object container.
     */
    Container _ownedObjects;

    /**
     * Copying a ptr_vector is forbidden in case the user accidently
 & nbsp;   * does so.  If copying a ptr_vector were permitted, the contents
     * of the rvalue would have to be cleared, which might not be
     * what the user expected.  Instead, the user must call the function
     * swap().
     */
    ptr_vector(const ptr_vector& x);

    /**
     * This is illegal so that users can't accidently lose memory.  They
     * should instead use swap().
     */
    ptr_vector& operator=(const ptr_vector&);


    /**
     * Instantiate an actual instance of the deleter.
     */
    Deleter _deleter;

    /**
     * Mutex for the internal data structure.
     */
    Mutex _ownedObjectsMutex;
};


template <typename T>
class ptr_vector_default : public ptr_vector<T, std::vector<T*>, tbb::spin_mutex, boost::checked_deleter<T> >
{
};


} // END NAMESPACE tcc

#endif




Intel Software Network Forums Statistics

8491 users have contributed to 31629 threads and 100769 posts to date.
In the past 24 hours, we have 28 new thread(s) 129 new posts(s), and 184 new user(s).

In the past 3 days, the most popular thread for everyone has been Implicite multithreading ??? The most posts were made to Crash when loading skeleton The post with the most views is Dear Steve, excuse me for a d

Please welcome our newest member shadowwolf99