I have a class which contains a mutex and an object, each time I need to access the contained object, a method is called to lock the mutex and return te contained object, let's see the code:
template <typename MUTEX, typename RESOURCE>
class LockedResource
{
using mutex_t = MUTEX;
using resource_t = RESOURCE;
mutex_t m_mutex;
resource_t m_resource;
public:
template <typename ... ARGS>
LockedResource(ARGS &&... args) :
m_resource(std::forward<ARGS>(args) ...)
{}
class Handler
{
std::unique_lock<mutex_t> m_lock; // unique lock
resource_t &m_resource; // Ref to resource
friend class LockedResource;
Handler(mutex_t &a_mutex, resource_t &a_resource) :
m_lock(a_mutex), // mutex automatically locked
m_resource(a_resource)
{ std::cout << "Resource locked\n"; }
public:
Handler(Handler &&a_handler) :
m_lock(std::move(a_handler.m_lock)),
m_resource(a_handler.m_resource)
{ std::cout << "Moved\n"; }
~Handler() // mutex automatically unlocked
{ std::cout << "Resource unlocked\n"; }
RESOURCE *operator->()
{ return &m_resource; }
};
Handler get()
{ return {m_mutex, m_resource}; }
};
template <typename T> using Resource = LockedResource<std::mutex, T>;
The idea behind this code is to wrap an object and protect it from multiple access from multiple threads; the wrapped object have private visibility and the only way to access it is through the internal class Handler, the expected usage is the following:
LockedResource<std::mutex, Foo> locked_foo;
void f()
{
auto handler = locked_foo.get(); // this will lock the locked_foo.m_mutex;
handler->some_foo_method();
// going out of the scope will call the handler dtor and
// unlock the locked_foo.m_mutex;
}
So, if I'm not mistaken, calling the LockedResource::get method creates a LockedResource::Handle value which locks the LockedResource::m_mutex for the entire lifetime of the Handle... but I must be mistaken because the code below doesn't cause a deadlock:
LockedResource<std::mutex, std::vector<int>> locked_vector{10, 10};
int main()
{
/*1*/ auto vec = locked_vector.get(); // vec = Resource<vector>::Handler
/*2*/ std::cout << locked_vector.get()->size() << '\n';
/*3*/ std::cout << vec->size() << '\n';
return 0;
}
I was expecting the line /*1*/ to lock the locked_vector.m_mutex and then the line /*2*/ try to lock the same already locked mutex causing deadlock, but the output is the following:
Resource locked Resource locked 10 Resource unlocked 10 Resource unlocked
- Shouldn't the second
::get()lead to a deadlock? - I'm accessing the wrapped resource through the same lock or I am misunderstanding something?
Here is the example code.
Aucun commentaire:
Enregistrer un commentaire