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.
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.