Search code examples
c++concurrencymutex

How can I elegantly synchronize access to an arbitrary buffer?


I have a buffer that is currently accessed via a pointer to the beginning of the buffer. I'm trying to come up with a way to guarantee nobody can access the buffer unless they hold a lock on it. For example:

class Buffer {
  friend class BufferGuard;
  std::mutex mutex;
  void* buffer;
}

struct BufferGuard {
  BufferGuard(Buffer& parent) { parent.mutex.lock() }
  ~BufferGuard() { parent.mutex.unlock(); }

  void* Get() { return parent.buffer; }
}

However this is easily defeated by saving the result of BufferGuard::Get. This solution, as well as any others I can think of, rely on users of my library being well behaved. Am looking for a solution where misbehaving isn't an option.


Solution

  • You cannot guarantee that people cannot break your guard while giving them access to a trivially copyable object like void*. However, you can make it more explicitly obvious that the user is doing something wrong:

    struct BufferGuard {
      private:
        Buffer& parent;
      public:
        BufferGuard(Buffer& parent) : parent{parent} { parent.mutex.lock() }
        ~BufferGuard() { parent.mutex.unlock(); }
        BufferGuard(BufferGuard const&) = delete;
        BufferGuard(BufferGuard&&) = delete;
        BufferGuard& operator=(BufferGuard const&) = delete;
        BufferGuard& operator=(BufferGuard&&) = delete;
    
      template <std::regular_invocable<void*> Op>
      void operate(Op&& operation) {
        std::forward<Op>(operation)(parent.buffer);
      }
    }
    
    void usecase(Buffer& buffer) {
      BufferGuard guard{buffer};
      guard.operate([](void* buf) { new(buf) int{7}; })
    }
    

    This means, you don't directly return the pointer to use freely; Your user can only do something with the pointer as long as your guard is around. And as long as your guard is around, your lock is alive. Of course, a nifty user will find a way to hoist the pointer value out of the callback, but it takes intent to misuse the interface.