How can I create a smart pointer that locks and unlocks a mutex?

前端 未结 3 448
迷失自我
迷失自我 2021-01-01 19:51

I have a threaded class from which I would like to occasionally acquire a pointer an instance variable. I would like this access to be guarded by a mutex so that the thread

相关标签:
3条回答
  • 2021-01-01 20:19

    I'm not sure if there are any standard implementations, but since I like re-implementing stuff for no reason, here's a version that should work (assuming you don't want to be able to copy such pointers):

    template<class T>
    class locking_ptr
    {
    public:
      locking_ptr(T* ptr, mutex* lock)
        : m_ptr(ptr)
        , m_mutex(lock)
      {
        m_mutex->lock();
      }
      ~locking_ptr()
      {
        if (m_mutex)
          m_mutex->unlock();
      }
      locking_ptr(locking_ptr<T>&& ptr)
        : m_ptr(ptr.m_ptr)
        , m_mutex(ptr.m_mutex)
      {
        ptr.m_ptr = nullptr;
        ptr.m_mutex = nullptr;
      }
    
      T* operator ->()
      {
        return m_ptr;
      }
      T const* operator ->() const
      {
        return m_ptr;
      }
    private:
      // disallow copy/assignment
      locking_ptr(locking_ptr<T> const& ptr)
      {
      }
      locking_ptr& operator = (locking_ptr<T> const& ptr)
      {
        return *this;
      }
      T* m_ptr;
      mutex* m_mutex; // whatever implementation you use
    };
    
    0 讨论(0)
  • 2021-01-01 20:27

    There is another approach here. Far less flexible and less generic, but also far simpler. While it still seems to fit your exact scenario.

    shared_ptr (both standard and Boost) offers means to construct it while providing another shared_ptr instance which will be used for usage counter and some arbitrary pointer that will not be managed at all. On cppreference.com it is the 8th form (the aliasing constructor).

    Now, normally, this form is used for conversions - like providing a shared_ptr to base class object from derived class object. They share ownership and usage counter but (in general) have two different pointer values of different types. This form is also used to provide a shared_ptr to a member value based on shared_ptr to object that it is a member of.

    Here we can "abuse" the form to provide lock guard. Do it like this:

    auto A::getResource()
    {
        auto counter = std::make_shared<Lock>(&mMutex);
        std::shared_ptr<Resource> result{ counter, &mResource };
        return result;
    }
    

    The returned shared_ptr points to mResource and keeps mMutex locked for as long as it is used by anyone.

    The problem with this solution is that it is now your responsibility to ensure that the mResource remains valid (in particular - it doesn't get destroyed) for that long as well. If locking mMutex is enough for that, then you are fine.

    Otherwise, above solution must be adjusted to your particular needs. For example, you might want to have the counter a simple struct that keeps both the Lock and another shared_ptr to the A object owning the mResource.

    0 讨论(0)
  • 2021-01-01 20:32

    You're describing a variation of the EXECUTE AROUND POINTER pattern, described by Kevlin Henney in Executing Around Sequences.

    I have a prototype implementation at exec_around.h but I can't guarantee it works correctly in all cases as it's a work in progress. It includes a function mutex_around which creates an object and wraps it in a smart pointer that locks and unlocks a mutex when accessed.

    0 讨论(0)
提交回复
热议问题