patterncppMinor
Ensure only a single instance of static template class member
Viewed 0 times
templateensureinstancemembersingleclassonlystatic
Problem
I am interacting with a C library, whose documentation states that certain functions are not thread safe. Since its functions deal heterogeneously with multiple types, I have written a template wrapper to use it in a homogeneous manner from C++. However, I now need to ensure serialized access to certain functions, serialized across all instantiated template wrappers.
I believe the following approach should work; what I would like to know is, are there any caveats to the following approach, and if so, what are they and how can I get around them?
I believe the following approach should work; what I would like to know is, are there any caveats to the following approach, and if so, what are they and how can I get around them?
struct LibMutexPolicy
{
static std::mutex & getMutexInstance()
{
static std::mutex inst;
return inst;
}
};
template
struct A : private LibMutexPolicy
{
void foo()
{
std::lock_guard lock(getMutexInstance());
// Call non thread-safe library code
}
};Solution
I believe the following approach should work; what I would like to know is, are there any caveats to the following approach, and if so, what are they and how can I get around them?
Consider a decorator implementation, with a visitor function for method access:
Client code:
Thread access:
The code is incomplete (you'd need a specialization of sync for const access and so on) but it should be enough to give you a picture.
Advantages:
-
this is a non-intrusive decorator that you can use over virtually any type
-
separates concerns nicely and allows you to implement and test them separately (e.g. test A, then test the synchronization code)
Minor disadvantage:
Consider a decorator implementation, with a visitor function for method access:
template
class threadsafe_reference // decorator
{
public:
using reference_type = V&;
threadsafe_reference(reference_type v, std::mutex& m): value_(v), mutex_(m) {}
// synchronized visitor access
void sync( std::function functor )
{
std::lock_guard lock{ mutex_ };
functor( value_ );
}
// you may wish to provide unsynchronized reference access here
private:
reference_type value_;
std::mutex& mutex_;
};
template
threadsafe_reference make_threadsafe(V& value, std::mutex& m)
{
return threadsafe_reference{ value, m };
}Client code:
struct A // agnostic to threading
{
void do_stuff_n_things( int i ) {};
};
A a; // need to pass this on two threads
mutex m;
auto tsa = make_threadsafe(a, m); // proxy accessorThread access:
tsa.sync( []( A& self ) { self.do_things_n_stuff(10); } );The code is incomplete (you'd need a specialization of sync for const access and so on) but it should be enough to give you a picture.
Advantages:
-
this is a non-intrusive decorator that you can use over virtually any type
-
separates concerns nicely and allows you to implement and test them separately (e.g. test A, then test the synchronization code)
Minor disadvantage:
- the access to A's methods requires lambdas/external functions
Code Snippets
template<typename V>
class threadsafe_reference // decorator
{
public:
using reference_type = V&;
threadsafe_reference(reference_type v, std::mutex& m): value_(v), mutex_(m) {}
// synchronized visitor access
void sync( std::function<void(value_type&)> functor )
{
std::lock_guard<std::mutex> lock{ mutex_ };
functor( value_ );
}
// you may wish to provide unsynchronized reference access here
private:
reference_type value_;
std::mutex& mutex_;
};
template<typename V>
threadsafe_reference<V> make_threadsafe(V& value, std::mutex& m)
{
return threadsafe_reference<V>{ value, m };
}struct A // agnostic to threading
{
void do_stuff_n_things( int i ) {};
};
A a; // need to pass this on two threads
mutex m;
auto tsa = make_threadsafe(a, m); // proxy accessortsa.sync( []( A& self ) { self.do_things_n_stuff(10); } );Context
StackExchange Code Review Q#71956, answer score: 4
Revisions (0)
No revisions yet.