std::unique_ptr with RAII for mutex? - c++

We have a lot of legacy C++98 code that we are slowly upgrading to c++11 and we have a RAII implementation for custom Mutex class:
class RaiiMutex
{
public:
RaiiMutex() = delete;
RaiiMutex(const RaiiMutex&) = delete;
RaiiMutex& operator= (const RaiiMutex&) = delete;
RaiiMutex(Mutex& mutex) : mMutex(mutex)
{
mMutex.Lock();
}
~RaiiMutex()
{
mMutex.Unlock();
}
private:
Mutex& mMutex;
};
Is it ok to make an std::unique_ptr of this object? We would still benefit from automatically calling the destructor when the object dies (thus unlocking) and would also gain the ability of unlocking before non-critical operations.
Example legacy code:
RaiiMutex raiiMutex(mutex);
if (!condition)
{
loggingfunction();
return false;
}
After:
auto raiiMutex = std::unique_ptr<RaiiMutex>(new RaiiMutex(mutex));
if (!condition)
{
raiiMutex = nullptr;
loggingfunction(); // log without locking the mutex
return false;
}
It would also remove the use of unnecessary brackets:
Example legacy code:
Data data;
{
RaiiMutex raiiMutex(mutex);
data = mQueue.front();
mQueue.pop_front();
}
data.foo();
After:
auto raiiMutex = std::unique_ptr<RaiiMutex>(new RaiiMutex(mutex));
Data data = mQueue.front();
mQueue.pop_front();
raiiMutex = nullptr;
data.foo();
Does it make sense?
Edit:
Cannot use unique_lock due to custom Mutex class:
class Mutex
{
public:
Mutex();
virtual ~Mutex();
void Unlock(bool yield = false);
void Lock();
bool TryLock();
bool TimedLock(uint64 pWaitIntervalUs);
private:
sem_t mMutex;
};

Add Mutex::lock(), Mutex::unlock() and Mutex::try_lock() methods to Mutex. They just forward to the Lock etc methods.
Then use std::unique_lock<Mutex>.
If you cannot modify Mutex, wrap it.
struct SaneMutex: Mutex {
void lock() { Lock(); }
// etc
using Mutex::Mutex;
};
A SaneMutex replaces a Mutex everywhere you can.
Where you can't:
struct MutexRef {
void lock() { m.Lock(); }
// etc
MutexRef( Mutex& m_in ):m(m_in) {}
private:
Mutex& m;
};
include an adapter.
these match the C++ standard lockable requirements. If you want timed lockable, you have to write a bit of glue code.
auto l = std::unique_lock<MutexRef>( mref );
or
auto l = std::unique_lock<SaneMutex>( m );
you now have std::lock, std::unique_lock, std::scoped_lock support.
And your code is one step closer to using std::mutex.
As for your unique_ptr solution, I wouldn't add the overhead of a memory allocation on every time you lock a mutex casually.

Related

How to implement scoped_lock functionality in c++11 using lock_guard

Looks like scoped_lock in c++17 gives the functionality I'm after however I'm presently tied to c++11.
At the moment I'm seeing deadlock issues with guard_lock when we call it with the same mutex more than once. Does scoped_lock protect against multiple calls (i.e. reenterent?)?
Is there a best practice for doing this in c++11 w/ lock_guard?
mutex lockingMutex;
void get(string s)
{
lock_guard<mutex> lock(lockingMutex);
if (isPresent(s))
{
//....
}
}
bool isPresent(string s)
{
bool ret = false;
lock_guard<mutex> lock(lockingMutex);
//....
return ret;
}
To be able to lock the same mutex multiple times one needs to use std::recursive_mutex. Recursive mutex is more expensive than a non-recursive one.
Best practise, though, is to design your code in such a way that a thread does not lock the same mutex multiple times. For example, have you public functions lock the mutex first and then invoke the implementation function that expects the mutex to have been locked already. Implementation functions must not call the public API functions that lock the mutex. E.g.:
class A {
std::mutex m_;
int state_ = 0;
private: // These expect the mutex to have been locked.
void foo_() {
++state_;
}
void bar_() {
this->foo_();
}
public: // Public functions lock the mutex first.
void foo() {
std::lock_guard<std::mutex> lock(m_);
this->foo_();
}
void bar() {
std::lock_guard<std::mutex> lock(m_);
this->bar_();
}
};
Scoped lock does not give the functionality you are looking for.
Scoped lock is just a variardic version of lock guard. It only exists due to some ABI issues with changing lock guard into a variardic template.
To have reentrant mutexes, you need to use a reentrant mutex. But this is both more expensive at runtime, and usually indicates a lack of care in your mutex state. While holding a mutex you should have complete and total understanding of all other synchronization actions you are performing.
Once you have complete understanding of all synchronizations actions you are performing, it is easy to avoid recursively locking.
There are two patterns you can consider here. First, split public locking API from a private non-locking API. Second, split synchronization from implementation.
private:
mutex lockingMutex;
bool isPresent(string s, lock_guard<mutex> const& lock) {
bool ret = false;
//....
return ret;
}
void get(string s, lock_guard<mutex> const& lock) {
if (isPresent(s, lock))
{
//....
}
}
public:
void get(string s) {
return get( std::move(s), lock_guard<mutex>(lockingMutex) );
}
bool isPresent(string s) {
return isPresent( std::move(s), lock_guard<mutex>(lockingMutex) );
}
};
here I use lock_guard<mutex> as "proof we have a lock".
An often better alternative is to write your class as non-thread-safe, then use a wrapper:
template<class T>
struct mutex_guarded {
template<class T0, class...Ts,
std::enable_if_t<!std::is_same<std::decay_t<T0>, mutex_guarded>{}, bool> =true
>
mutex_guarded(T0&&t0, Ts&&...ts):
t( std::forward<T0>(t0), std::forward<Ts>(ts)... )
{}
mutex_guarded()=default;
~mutex_guarded=default;
template<class F>
auto read( F&& f ) const {
auto l = lock();
return f(t);
}
template<class F>
auto write( F&& f ) {
auto l = lock();
return f(t);
}
private:
auto lock() { return std::unique_lock<std::mutex>(m); }
auto lock() const { return std::unique_lock<std::mutex>(m); }
mutable std::mutex m;
T t;
};
now we can use this like this:
mutex_guarded<Foo> foo;
foo.write([&](auto&&foo){ foo.get("hello"); } );
you can write mutex_gaurded, shared_mutex_guarded, not_mutex_guarded or even async_guarded (which returns futures and serializes actions in a worker thread).
So long as the class doesn't leave its own "zone of control" in methods this pattern makes writing mutex-guarded data much easier, and lets you compose related mutex guarded data into one bundle without having to rewrite them.

Is there a `shared_lock_guard` and if not, what would it look like?

I wanted to use a std::mutex in my class, and noticed that it isn't copyable. I'm at the bottom level of my library here, so it seems like a terrible idea to have this behaviour.
I used std::lock_guard on the std::mutex, but there doesn't seem to be a shared_lock_guard, which would be preferable to provide write-locks-exclusively behaviour. Is this an oversight or trivial to implement myself?
With C++14 You can use a std::shared_lock and a std::unique_lock to implement read/write locking:
class lockable
{
public:
using mutex_type = std::shared_timed_mutex;
using read_lock = std::shared_lock<mutex_type>;
using write_lock = std::unique_lock<mutex_type>;
private:
mutable mutex_type mtx;
int data = 0;
public:
// returns a scoped lock that allows multiple
// readers but excludes writers
read_lock lock_for_reading() { return read_lock(mtx); }
// returns a scoped lock that allows only
// one writer and no one else
write_lock lock_for_writing() { return write_lock(mtx); }
int read_data() const { return data; }
void write_data(int data) { this->data = data; }
};
int main()
{
lockable obj;
{
// reading here
auto lock = obj.lock_for_reading(); // scoped lock
std::cout << obj.read_data() << '\n';
}
{
// writing here
auto lock = obj.lock_for_writing(); // scoped lock
obj.write_data(7);
}
}
Note: If you have C++17, then you can use std::shared_mutex for mutex_type.
It’s not part of C++ standard yet, but you can find implementation example in boost.
template<typename SharedMutex>
class shared_lock_guard
{
private:
SharedMutex& m;
public:
typedef SharedMutex mutex_type;
explicit shared_lock_guard(SharedMutex& m_):
m(m_)
{
m.lock_shared();
}
shared_lock_guard(SharedMutex& m_,adopt_lock_t):
m(m_)
{}
~shared_lock_guard()
{
m.unlock_shared();
}
};
It requires mutex class conforming to SharedMutex concept though;
std::shared_mutex is part of proposed C++17 standard and boost had one already for some time: boost::shared_mutex.

RAII thread safe getter

Most of the times I see in the code some variant of this kind of implementation for a thread safe getter method:
class A
{
public:
inline Resource getResource() const
{
Lock lock(m_mutex);
return m_resource;
}
private:
Resource m_resource;
Mutex m_mutex;
};
Assuming that the class Resource can't be copied, or that the copy operation has a too high computational cost, is there a way in C++ to avoid the returning copy but still using a RAII style locking mechanism?
I haven't tried it, but something like this should work:
#include <iostream>
#include <mutex>
using namespace std;
typedef std::mutex Mutex;
typedef std::unique_lock<Mutex> Lock;
struct Resource {
void doSomething() {printf("Resource::doSomething()\n"); }
};
template<typename MutexType, typename ResourceType>
class LockedResource
{
public:
LockedResource(MutexType& mutex, ResourceType& resource) : m_mutexLocker(mutex), m_pResource(&resource) {}
LockedResource(MutexType& mutex, ResourceType* resource) : m_mutexLocker(mutex), m_pResource(resource) {}
LockedResource(LockedResource&&) = default;
LockedResource(const LockedResource&) = delete;
LockedResource& operator=(const LockedResource&) = delete;
ResourceType* operator->()
{
return m_pResource;
}
private:
Lock m_mutexLocker;
ResourceType* m_pResource;
};
class A
{
public:
inline LockedResource<Mutex, Resource> getResource()
{
return LockedResource<Mutex, Resource>(m_mutex, &m_resource);
}
private:
Resource m_resource;
Mutex m_mutex;
};
int main()
{
A a;
{ //Lock scope for multiple calls
auto r = a.getResource();
r->doSomething();
r->doSomething();
// The next line will block forever as the lock is still in use
//auto dead = a.getResource();
} // r will be destroyed here and unlock
a.getResource()->doSomething();
return 0;
}
But be careful, as the lifetime of the accessed Resource depends on the lifetime of the owner (A)
Example on Godbolt: Link
P1144 reduces the generated assembly quite nicely so that you can see where the lock is locked and unlocked.
How about returning an accessor object that provides a thread-safe interface to the Resource class and/or keeps some lock?
class ResourceGuard {
private:
Resource *resource;
public:
void thread_safe_method() {
resource->lock_and_do_stuff();
}
}
This will be cleared in a RAII fashion, releasing any locks if needed. If you need locking it should be done in the the Resource class.
Of course you have to take care of the lifespan of Resource. A very simple way would be to use a std::shard_ptr. A weak_ptr might fit as well.
another way to achieve the same thing. This is the implementation of a mutable version. the const accessor is just as trivial.
#include <iostream>
#include <mutex>
struct Resource
{
};
struct locked_resource_view
{
locked_resource_view(std::unique_lock<std::mutex> lck, Resource& r)
: _lock(std::move(lck))
, _resource(r)
{}
void unlock() {
_lock.unlock();
}
Resource& get() {
return _resource;
}
private:
std::unique_lock<std::mutex> _lock;
Resource& _resource;
};
class A
{
public:
inline locked_resource_view getResource()
{
return {
std::unique_lock<std::mutex>(m_mutex),
m_resource
};
}
private:
Resource m_resource;
mutable std::mutex m_mutex;
};
using namespace std;
auto main() -> int
{
A a;
auto r = a.getResource();
// do something with r.get()
return 0;
}

slim reader writer lock Raii

I have a windows server application that uses multiple threads to handle requests. I needed a reader-writer lock to guard access to a shared std::unordered_map; and I wanted to do this in a manner similar to a std::unique_lock (resource acquisition is initialization). So I came up with this SRWRaii class.
class SRWRaii
{
public:
SRWRaii(const SRWLOCK& lock, bool m_exclusive = false)
:m_lock(lock), m_exclusive(m_exclusive)
{
if (m_exclusive)
{
AcquireSRWLockExclusive(const_cast<SRWLOCK*>(&m_lock));
}
else
{
AcquireSRWLockShared(const_cast<SRWLOCK*>(&m_lock));
}
}
~SRWRaii()
{
if (m_exclusive)
{
ReleaseSRWLockExclusive(const_cast<SRWLOCK*>(&m_lock));
}
else
{
ReleaseSRWLockShared(const_cast<SRWLOCK*>(&m_lock));
}
}
private:
const SRWLOCK& m_lock;
bool m_exclusive;
};
Then I use this as follows
SRWLOCK g_mutex;
void Initialize()
{
InitializeSRWLock(&g_mutex);
}
void Reader()
{
SRWRaii lock(g_mutex);
// Read from unordered_map
}
void Writer()
{
SRWRaii lock(g_mutex, true); // exclusive
// add or delete from unordered_map
}
Given my noviceness to c++, I am a little suspect of this critical code. Are there issues with the above approach of implementing an Raii wrapper over SRWLOCK? What improvements can be done to the above code?

Assignment within RAII scope

Problem
How do you initialize an object inside a RAII scope, and use it outside of that scope?
Background
I have a global lock which can be called with lock() and unlock().
I have a type, LockedObject, which can only be initialized when the global lock is locked.
I have a function, use_locked(LockedObject &locked_object), which needs to be called with the global lock unlocked.
The usage scenario is
lock();
LockedObject locked_object;
unlock();
use_locked(locked_object);
RAII
For various reasons, I moved to a RAII encapsulation of the global lock. I would like to use this everywhere, primarily as creating LockedObject can fail with exceptions.
The problem is that
{
GlobalLock global_lock;
LockedObject locked_object;
}
use_locked(locked_object);
fails, as locked_object is created in the inner scope.
Examples
Set-up (mostly not important):
#include <assert.h>
#include <iostream>
bool locked = false;
void lock() {
assert(!locked);
locked = true;
}
void unlock() {
assert(locked);
locked = false;
}
class LockedObject {
public:
LockedObject(int i) {
assert(locked);
std::cout << "Initialized: " << i << std::endl;
}
};
void use_locked(LockedObject locked_object) {
assert(!locked);
}
class GlobalLock {
public:
GlobalLock() {
lock();
}
~GlobalLock() {
unlock();
}
};
Original, non RAII method:
void manual() {
lock();
LockedObject locked_object(123);
unlock();
use_locked(locked_object);
}
Broken RAII methods:
/*
void raii_broken_scoping() {
{
GlobalLock global_lock;
// Initialized in the wrong scope
LockedObject locked_object(123);
}
use_locked(locked_object);
}
*/
/*
void raii_broken_initialization() {
// No empty initialization
// Alternatively, empty initialization requires lock
LockedObject locked_object;
{
GlobalLock global_lock;
locked_object = LockedObject(123);
}
use_locked(locked_object);
}
*/
And a main function:
int main(int, char **) {
manual();
// raii_broken_scoping();
// raii_broken_initialization;
}
For what it's worth, in Python I would do:
with GlobalLock():
locked_object = LockedObject(123)
I want the equivalent of that. I mention my current solution in an answer, but it feels clumsy.
The specific (but simplified) code to be executed follows. With my current lambda-based call:
boost::python::api::object wrapped_object = [&c_object] () {
GIL lock_gil;
return boost::python::api::object(boost::ref(c_object));
} ();
auto thread = std::thread(use_wrapped_object, c_object);
with
class GIL {
public:
GIL();
~GIL();
private:
GIL(const GIL&);
PyGILState_STATE gilstate;
};
GIL::GIL() {
gilstate = PyGILState_Ensure();
}
GIL::~GIL() {
PyGILState_Release(gilstate);
}
boost::python::api::objects must be created with the GIL and the thread must be created without the GIL. The PyGILState struct and function calls are all given to me by CPython's C API, so I can only wrap them.
Allocate your object on the heap and use some pointers:
std::unique_ptr<LockedObject> locked_object;
{
GlobalLock global_lock;
locked_object.reset(new LockedObject());
}
use_locked(locked_object);
Here is a complete list of options from my perspective. optional would be what I would do:
The proposed post-C++1y optional would solve your problem, as it lets you construct data after declaration, as would heap based unique_ptr solutions. Roll your own, or steal ot from boost
A 'run at end of scope' RAII function storer (with 'commit') can also make this code less crazy, as can letting your locks be manually disengaged within their scope.
template<class F>
struct run_at_end_of_scope {
F f;
bool Skip;
void commit(){ if (!Skip) f(); Skip = true; }
void skip() { Skip = true; }
~run_at_end_of_scope(){commit();}
};
template<class F>
run_at_end_of_scope<F> at_end(F&&f){ return {std::forward<F>(f), false}; }
then:
auto later = at_end([&]{ /*code*/ });
and you can later.commit(); or later.skip(); to run the code earlier or skip running it.
Making your RAII locking classes have move constructors would let you do construction in another scope, and return via move (possibly elided).
LockedObject make_LockedObject(){
GlobalLock lock;
return {};
}
My current solution is to use an anonymous function:
void raii_return() {
LockedObject locked_object = [&] () {
GlobalLock global_lock;
return LockedObject(123);
} ();
use_locked(locked_object);
}
The advantage of this approach is that it avoids pointers and thanks to copy elision it should be quite fast.
One downside is that LockedObjects don't necessarily support copying (use_locked would in that case take a reference).