Having an object and a mutex as 2 different variables is kinda error prone
MyObject myObject;
std::mutex myObjectMutex;
I tend to forget to lock it sometimes.
On Rust, a shared object is required to be inside a mutex:
std::sync::Mutex<MyObject> mySharedObject
So I have to use like this:
mySharedObject.lock().unwrap().objectMethod()
What would be the less error prone way to simulate something like this in C++ so I don't forget to lock it?
I thought of an std::tuple<std::mutex, MyObject> but it's not very good and I can forget to lock.
One way to do it is have your Mutex<T> only allow access to the contained T via a lambda:
template <typename T>
class Mutex {
private:
T value;
std::mutex mutex;
public:
// Fill out some constructors, probably some kind of emplacement constructor too.
// For simplicity of the example, this should be okay:
explicit Mutex(T value)
: value(std::move(value))
{}
template <typename F>
auto locked(F&& fn) const& -> std::invoke_result_t<F&&, T const&> {
// Lock the mutex while invoking the function.
// scoped_lock automatically unlocks at the end of the scope
std::scoped_lock lock(mutex);
return std::invoke(std::forward<F>(fn), value);
}
template <typename F>
auto locked(F&& fn) & -> std::invoke_result_t<F&&, T&> {
std::scoped_lock lock(mutex);
return std::invoke(std::forward<F>(fn), value);
}
// Can be worth repeating for const&& and && as well
};
Usage:
mySharedObject.locked([] (MyObject& obj) { return obj.objectMethod(); });
It's still possible to defeat this by stashing a reference to the obj inside a .locked(...) call and using the reference outside of .locked(...), but that would almost require deliberately trying to do the wrong thing.
Also, be aware that it being in a lambda can be quite limiting, as regular control flow no longer works (return doesn't return from the outer scope, continue/break don't work, etc.)
I think the following is very near regardless of the lifetime parameter
#include <atomic>
template<class T>
class MutexGuard{
T& inner;
std::atomic_flag & locked;
public:
MutexGuard(T& inner, std::atomic_flag& locked): inner{inner}, locked{locked}{}
operator T&()&{
return inner;
}
~MutexGuard(){
locked.clear();
}
};
template<class T>
class Mutex{
std::atomic_flag locked{false};
T inner;
public:
Mutex(T&& inner):inner{std::move(inner)}{}
Mutex(const T&) = delete;
MutexGuard<T> lock(){
while(locked.test_and_set());
return MutexGuard<T>{inner, locked};
}
};
Demo
Related
Before anything: I'm not a developer and I might not understand some of your messages, and as English is not my native language my question could be hard to understand.
Considering :
class MyVector
{
std::vector<command> vec;
std::mutex vector_m;
public:
void MVpush_back(command t)
{
this->vector_m.lock();
this->vec.push_back(t);
this->vector_m.unlock();
}
};
command is a custom class (its content doesn't seem relevant here; copy constructor does exist).
Basically, as I have a lot of possible writer & readers, thus I want to force the use of the mutex to access to the vec parameter.
As I'll only use push_back(), erase() and find() I could redefine them, but I was wondering if there is a way not have to redefine all functions.
something like:
<template> safe(*function name*<template>)
{
this->vector_m.lock();
<template> retval = vec.*function name*<parameter>;
this->vector_m.unlock();
return retval;
}
where the function to call is a kind of parameter...
I thought it could be done using std::initializer_list<type> but the type requirement is blocking.
Is there a way to do such a thing?
Rephrased question: is there a way to push a function with parameter(1) as parameter of a function(2) and make function(2) call function(1) ?
If you don't mind sacrificing the use of the member access operator (.), you can wrap all the vector operations neatly into lockable operations.
class MyVector {
std::vector<command> vec;
std::mutex vector_m;
struct locker {
MyVector& _ref;
locker(MyVector& parent) : _ref(parent) {
_ref.vector_m.lock();
}
~locker() { _ref.vector_m.unlock(); }
std::vector<command>* operator->() && { return &_ref.vec; }
};
public:
locker operator->() { return {*this}; }
};
Now, every access to the underlying vector will lock and unlock the vector for the duration of the operation:
MyVector mv;
mv->push_back(/* ... */);
// This locks the mutex before doing the push back
// And unlocks it immediately after, even in the face of exceptions.
The magic is in operator-> acting in a transitive manner. It is applied to the return value of itself until a regular pointer is returned, which is then accessed as usual. But every temporary along the way is created and destroyed in LIFO order. So the temporary MyVector::locker object has a lifetime that is just the duration of the access more or less.
Here's a quick not particularly fantastic version of the suggestion I made in the comments; not compiled or tested; just something so that you can get the idea.
template<class T>
class OverkillProtector {
private:
T& d;
std::unique_lock<std::mutex>lock ;
public:
OverkillProtector(T& d_, std::mutex& m_) :
d(d_),
lock(m_)
{}
OverkillProtector(const OverkillProtector&) = delete;
OverkillProtector& operator =(const OverkillProtector&) = delete;
T& getValue() { return d; }
const T& getValue() const { return d; }
};
Note that in the (default) desturctor, the unique lock will be destroyed, which will release the mutex. Note that the lifetime of this object is required to be less than that of the mutex or the data you're wrapping.
You might do something like:
class MyVector
{
std::vector<command> vec;
std::mutex vector_m;
public:
template <typename F>
decltype(auto) Do(F&& f)
{
std::unique_lock<std::mutex> lock{vector_m};
return std::forward<F>(f)(vec);
}
};
With usage similar to:
MyVector myVector;
command myCommand;
myVector.Do([&](auto& vec) { vec.push_back(myCommand); });
A template approach might look like this:
class MyVector
{
std::vector<command> vec;
mutable std::mutex vector_m;
public:
template <typename R, typename ... T, typename ... P>
R safeCall(R (std::vector<command>::*f)(T ...), P&& ... p)
{
std::lock_guard<std::mutex> l(vector_m);
return (vec.*f)(std::forward<P>(p)...);
}
template <typename R, typename ... T, typename ... P>
R safeCall(R (std::vector<command>::*f)(T ...) const, P&& ... p) const
{
std::lock_guard<std::mutex> l(vector_m);
return (vec.*f)(std::forward<P>(p)...);
}
};
void test()
{
MyVector v;
v.safeCall(&std::vector<int>::push_back, 7);
MyVector const* vv = &v;
int n = vv->safeCall(&std::vector<int>::operator[], 0);
}
Well, you safe the work of re-implementing the interface, but usage gets rather ugly – typedef for the vector type gets it minimally shorter, but still... A macro?
#define safe_call(V, R, F, ...) V R safeCall(&std::vector<int>::F, ## __VA_ARGS__)
safe_call(v, ., push_back, 7);
safe_call(vv, ->, operator[], 1);
Or a little shorter:
#define safe_call(V, F, ...) V safeCall(&std::vector<int>::F, ## __VA_ARGS__)
safe_call(v., push_back, 7);
safe_call(vv->, operator[], 1);
Well, I won't comment further, decide yourself...
In the end, you might bite the bullet and really duplicate the interface for the sake of more convenient usage afterwards - a helper template might facilitate the task, though:
class MyVector
{
std::vector<command> vec;
mutable std::mutex vector_m;
template <typename R, typename ... T>
R safeCall(R (std::vector<command>::*f)(T...), T... t)
{
std::lock_guard<std::mutex> l(vector_m);
return (vec.*f)(t...);
}
// const variant, too
public:
// ...
};
void MyVector::push_back(Command t)
{
safeCall(&std::vector<Command>::push_back, t);
}
I'm trying to create a very simple, bare-bones C++ class to implement a thread-safe list, i.e. one which is automatically locked when you access it. Unfortunately, the compiler doesn't want to allow me to create and return a struct that contains a unique_lock. This is what I tried at first:
template<typename T>
struct LockedQueue {
private:
std::mutex mutex;
using lock_t = std::unique_lock<std::mutex>;
std::list<T> underlying_list;
public:
struct LockedListAccess {
private:
lock_t lock;
public:
std::list<T> &access;
};
LockedListAccess locked() {
return LockedListAccess{ lock_t{mutex}, underlying_list };
}
};
This fails with
no matching function for call to ‘LockedQueue<tcp::socket>::LockedListAccess::LockedListAccess(<brace-enclosed initializer list>)
I'm guessing this means that brace initializer lists/C++11 unified initialization for structs don't work with move-only types like std::unique_lock. So I tried creating an explicit constructor for my struct that takes the unique_lock as an rvalue reference, and moves it into the member:
template<typename T>
struct LockedQueue {
private:
std::mutex mutex;
using lock_t = std::unique_lock<std::mutex>;
std::list<T> underlying_list;
public:
struct LockedListAccess {
private:
lock_t lock;
public:
std::list<T> &access;
LockedListAccess(lock_t&& l, std::list<T>& a) :
lock(l), access(a) {};
};
LockedListAccess locked() {
return LockedListAccess{ std::move(lock_t{mutex}), underlying_list };
}
};
However, this also fails, giving me the error
error: use of deleted function ‘std::unique_lock<_Mutex>::unique_lock(const std::unique_lock<_Mutex>&) [with _Mutex = std::mutex]’
This compiler error is especially confusing, because it points at the line containing lock(l), access(a) as the one where I'm trying to use the deleted copy constructor of std::unique_lock. I declared l as a lock_t&&, so how could I possibly be calling the copy constructor?
Most resources I could find on the internet seem to indicate that you can move unique_locks around with std::move, though no one seems to address the question of how to construct an object that contains a unique_lock by using std::move. What could I be doing wrong here?
Problem:
In the context of the LockedListAccess constructor, lock_t&& l is actually an l-value. So you need to cast it back to an r-value using std::move. General rule of thumb: Always std::move r-value references and always std::forward forwarding references.
Solution:
LockedListAccess(lock_t&& l, std::list<T>& a)
: lock(std::move(l))
, access(a)
{}
Also you do not need to move the temporary lock object because the compiler will automatically bind a temp object to an r-value reference.
This:
LockedListAccess locked() {
return LockedListAccess{ std::move(lock_t{mutex}), underlying_list }
}
can become this:
LockedListAccess locked() {
return LockedListAccess{ lock_t{mutex}, underlying_list }
}
Ok, the question title is a bit hard to phrase. What I am trying to achieve is create a template class with get/set functions that can handle simple types and structures.
This is simple for types such as integers and char, etc... But when the template type 'T' is a struct then it gets harder.
For example, here is a template class, where I have omitted various parts of it (such as constructor, etc), but it shows the get/set function:
EDIT: Only this class is allowed to modify the data, so passing a reference outside is not allowed. The reason is that I want to do a mutex around the set/get. I will/have update the functions...
template <class T> class storage
{
private:
T m_var;
pthread_mutex_t m_mutex;
public:
void set(T value)
{
pthread_mutex_lock(&m_mutex);
m_var = value;
pthread_mutex_unlock(&m_mutex);
}
T get(void)
{
T tmp;
// Note: Can't return the value within the mutex otherwise we could get into a deadlock. So
// we have to first read the value into a temporary variable and then return that.
pthread_mutex_lock(&m_mutex);
tmp = m_var;
pthread_mutex_unlock(&m_mutex);
return tmp;
}
};
Then consider the following code:
struct shape_t
{
int numSides;
int x;
int y;
}
int main()
{
storage<int> intStore;
storage<shape_t> shapeStore;
// To set int value I can do:
intStore.set(2);
// To set shape_t value I can do:
shape_t tempShape;
tempShape.numSides = 2;
tempShape.x = 5;
tempShape.y = 4;
shapeStore.set(tempShape);
// To modify 'x' (and keep y and numSides the same) I have to do:
shape_t tempShape = shapeStore.get();
tempShape.x = 5;
shapeStore.set(tempShape);
}
What I want to be able to do, if its possible, is to set the members of shape_t individually via some means in the template class, something like:
shapeStore.set(T::numSides, 2);
shapeStore.set(T::x, 5);
shapeStore.set(T::y, 4);
And not have to use a temp var. Is this possible? how?
I looked at this answer, but it did not quite do what I wanted because it is for a specific structure type
Make your get() member return a reference:
T& get()
{
return m_var;
}
Then you could say
shapeStore.get().x = 42;
Note it is good practice to add a const overload:
const T& get() const
{
return m_var;
}
Also note that if your get and set methods really do nothing special, as in your example, you might consider making the data public and doing away with getters/setters:
template <class T> struct storage
{
T m_var;
};
Edit: If you want to allow synchronised changes to the member, an option is to have a method that takes a modifying function. The function is applied inside the class, in your case, protected by the mutex. For example,
template <class T> struct storage
{
storage() : m_var() {}
void do_stuff(std::function<void(T&)> f)
{
std::lock_guard<std::mutex> lock(m_mutex);
f(m_var);
}
private:
T m_var;
std::mutex_t m_mutex;
};
Then you can modify members in a synchronised manner:
storage<shape_t> shapeStore;
shapeStore.do_stuff([](shape_t& s)
{ s.x = 42;
s.y = 100; });
If you don't have C++11 you can pass a function instead:
void foo(shape_t& s) { s.x = 42; }
shapeStore.do_stuff(foo);
Your design is fairly workable for primitive types, but it requires you to replicate the entire interface of class types and quickly becomes unmanageable. Even in the case of primitive types, you might want to enable more complex atomic operations than simply get and set, e.g., increment or add or multiply. The key to simplifying the design is to realize that you don't actually want to interpose on every single operation the client code performs on the data object, you only need to interpose before and after the client code atomically performs a sequence of operations.
Anthony Williams wrote a great article in Doctor Dobb's Journal years ago about this exact problem using a design where the manager object provides a handle to the client code that the client uses to access the managed object. The manager interposes only on the handle creation and destruction allowing clients with a handle unfettered access to the managed object. (See the recent proposal for standardization for excruciating detail.)
You could apply the approach to your problem fairly easily. First, I'll replicate some parts of the C++11 threads library because they make it MUCH easier to write correct code in the presence of exceptions:
class mutex {
pthread_mutex_t m_mutex;
// Forbid copy/move
mutex(const mutex&); // C++11: = delete;
mutex& operator = (const mutex&); // C++11: = delete;
public:
mutex(pthread_mutex_) { pthread_mutex_init(&m_mutex, NULL); }
~mutex() { pthread_mutex_destroy(&m_mutex); }
void lock() { pthread_mutex_lock(&m_mutex); }
void unlock() { pthread_mutex_unlock(&m_mutex); }
bool try_lock() { return pthread_mutex_trylock(&m_mutex) == 0; }
};
class lock_guard {
mutex& mtx;
public:
lock_guard(mutex& mtx_) : mtx(mtx_) { mtx.lock(); }
~lock_guard() { mtx.unlock(); }
};
The class mutex wraps up a pthread_mutex_t concisely. It handles creation and destruction automatically, and saves our poor fingers some keystrokes. lock_guard is a handy RAII wrapper that automatically unlocks the mutex when it goes out of scope.
storage then becomes incredibly simple:
template <class> class handle;
template <class T> class storage
{
private:
T m_var;
mutex m_mutex;
public:
storage() : m_var() {}
storage(const T& var) : m_var(var) {}
friend class handle<T>;
};
It's simply a box with a T and a mutex inside. storage trusts the handle class to be friendly and allows it poke around its insides. It should be clear that storage does not directly provide any access to m_var, so the only way it could possibly be modified is via a handle.
handle is a bit more complex:
template <class T>
class handle {
T& m_data;
lock_guard m_lock;
public:
handle(storage<T>& s) : m_data(s.m_var), m_lock(s.m_mutex) {}
T& operator* () const {
return m_data;
}
T* operator -> () const {
return &m_data;
}
};
it keeps a reference to the data item and holds one of those handy automatic lock objects. The use of operator* and operator-> make handle objects behave like a pointer to T.
Since only way to access the object inside storage is through a handle, and a handle guarantees that the appropriate mutex is held during its lifetime, there's no way for client code to forget to lock the mutex, or to accidentally access the stored object without locking the mutex. It can't even forget to unlock the mutex, which is nice as well. Usage is simple (See it working live at Coliru):
storage<int> foo;
void example() {
{
handle<int> p(foo);
// We have exclusive access to the stored int.
*p = 42;
}
// other threads could access foo here.
{
handle<int> p(foo);
// We have exclusive access again.
*p *= 12;
// We can safely return with the mutex held,
// it will be unlocked for us in the handle destructor.
return ++*p;
}
}
You would code the program in the OP as:
struct shape_t
{
int numSides;
int x;
int y;
};
int main()
{
storage<int> intStore;
storage<shape_t> shapeStore;
// To set int value I can do:
*handle<int>(intStore) = 2;
{
// To set shape_t value I can do:
handle<shape_t> ptr(shapeStore);
ptr->numSides = 2;
ptr->x = 5;
ptr->y = 4;
}
// To modify 'x' (and keep y and numSides the same) I have to do:
handle<shape_t>(shapeStore)->x = 5;
}
I can propose you alternative solution.
When you need you can get special template class that allows managing containing object.
template < typename T >
class SafeContainer
{
public:
// Variadic template for constructor
template<typename ... ARGS>
SafeContainer(ARGS ...arguments)
: m_data(arguments ...)
{};
// RAII mutex
class Accessor
{
public:
// lock when created
Accessor(SafeContainer<T>* container)
:m_container(container)
{
m_container->m_mutex.lock();
}
// Unlock when destroyed
~Accessor()
{
m_container->m_mutex.unlock();
}
// Access methods
T* operator -> ()
{
return &m_container->m_data;
}
T& operator * ()
{
return m_container->data;
}
private:
SafeContainer<T> *m_container;
};
friend Accessor;
Accessor get()
{
return Accessor(this);
}
private:
T m_data;
// Should be using recursive mutex to avoid deadlocks
std::mutex m_mutex;
};
Example:
struct shape_t
{
int numSides;
int x;
int y;
};
int main()
{
SafeContainer<shape_t> shape;
auto shapeSafe = shape.get();
shapeSafe->numSides = 2;
shapeSafe->x = 2;
}
Is there a way to code a write-only reference to an object? For example, suppose there was a mutex class:
template <class T> class mutex {
protected:
T _data;
public:
mutex();
void lock(); //locks the mutex
void unlock(); //unlocks the mutex
T& data(); //returns a reference to the data, or throws an exception if lock is unowned
};
Is there a way to guarantee that one couldn't do this:
mutex<type> foo;
type& ref;
foo.lock();
foo.data().do_stuff();
ref = foo.data();
foo.unlock();
//I have a unguarded reference to foo now
On the other hand, is it even worth it? I know that some people assume that programmers won't deliberately clobber the system, but then, why do we have private variables in the first place, eh? It'd be nice to just say it's "Undefined Behavior", but that just seems a little bit too insecure.
EDIT: OK, i understand the idea of a setter routine, but how would this be accomplished?
mutex<vector<int> > foo;
foo.lock();
for (int i=0; i < 10; i++) {
foo.data().push_back(i);
}
foo.unlock();
Using a set routine would require a copy for each write:
mutex<vector<int> > foo;
foo.lock();
for (int i=0; i < 10; i++) {
vector<int> copy = foo.read();
copy.push_back(i);
foo.write(copy);
}
though you could trivially optimize in this specific case, if, say, several different threads are all pushing elements, and maybe even erasing a few, this can become quite a bit of excess memory copying (i.e. one per critical section).
Yes, you can create a wrapper class that becomes invalidated when unlock is called and return the wrapper, instead of returning the reference, and you can overload its assignment operator to assign to the reference. The trick is that you need to hang onto a reference to the wrapper's internal data, so that when unlock is called, prior to releasing the lock, you invalidate any wrappers that you have created.
The common way to differentiate between getters and setters is by the const-ness of the object:
template <class T> class mutex {
public:
mutex();
void lock();
void unlock();
T& data(); // cannot be invoked for const objects
const T& data() const; // can be invoked for const objects
protected:
T _data;
};
Now, if you want to have read-only access, make the mutex const:
void read_data(const mutex< std::vector<int> >& data)
{
// only const member functions can be called here
}
You can bind a non-const object to a const reference:
// ...
mutex< std::vector<int> > data;
data.lock();
read_data(data);
data.unlock();
// ...
Note that the lock() and unlock() functions are inherently unsafe in the face of exceptions:
void f(const mutex< std::vector<int> >& data)
{
data.lock();
data.data().push_back(42); // might throw exception
data.unlock(); // will never be reached in push_back() throws
}
The usual way to solve this is RAII (resource acquisition is initialization):
template <class T> class lock;
template <class T> class mutex {
public:
mutex();
protected:
T _data;
private:
friend class lock<T>;
T& data();
void lock();
void unlock();
};
template <class T> class lock {
public:
template <class T> {
lock(mutex<T>& m) m_(m) {m_.lock();}
~lock() {m_.unlock();}
T& data() {return m_.data();}
const T& data() const {return m_.data()}
private:
mutex<T>& m_;
};
Note that I have also moved the accessor functions to the lock class, so that there is no way to access unlocked data.
You can use this like this:
void f(const mutex< std::vector<int> >& data)
{
{
lock< std::vector<int> > lock_1(data);
std::cout << lock1.data()[0]; // fine, too
lock1.data().push_back(42); // fine
}
{
const lock< std::vector<int> > lock_2(data); // note the const
std::cout << lock1.data()[0]; // fine, too
// lock1.data().push_back(42); // compiler error
}
}
You could encapsulate the data as private and expose a write routine. Within that routine you could lock your mutex, giving you similar behavior to what you are shooting for.
You can use a member function as the following:
void set_data(const T& var);
This is how write-only access is applied in C++.
No. There is no way to guarantee anything about reading and writing memory in unsafe languages like C++, where all the memory is treated like one big array.
[Edit] Not sure why all the downvotes; this is correct and relevant.
In safe languages, such as Java or C#, you can certainly guarantee that, for instance, properly-implemented immutable types will stay immutable. Such a guarantee can never be made in C++.
The fear is not so much malicious users as it is accidental invalid-pointers; I have worked on C++ projects where immutable types have been mutated due to an invalid pointer in completely unrelated code, causing bugs that are extremely difficult to track down. This guarantee - which only safe languages can make - is both useful and important.
I need to work with array from several threads, so I use CRITICAL SECTION to give it an exclusive access to the data.
Here is my template:
#include "stdafx.h"
#ifndef SHAREDVECTOR_H
#define SHAREDVECTOR_H
#include <vector>
#include <windows.h>
template<class T>
class SharedVector {
std::vector<T> vect;
CRITICAL_SECTION cs;
SharedVector(const SharedVector<T>& rhs) {}
public:
SharedVector();
explicit SharedVector(const CRITICAL_SECTION& CS);
void PushBack(const T& value);
void PopBack();
unsigned int size() const;
T& operator[](int index);
virtual ~SharedVector();
};
template<class T>
SharedVector<T>::SharedVector() {
InitializeCriticalSection(&cs);
}
template<class T>
SharedVector<T>::SharedVector(const CRITICAL_SECTION& r): cs(r) {
InitializeCriticalSection(&cs);
}
template<class T>
void SharedVector<T>::PushBack(const T& value) {
EnterCriticalSection(&cs);
vect.push_back(value);
LeaveCriticalSection(&cs);
}
template<class T>
void SharedVector<T>::PopBack() {
EnterCriticalSection(&cs);
vect.pop_back();
LeaveCriticalSection(&cs);
}
template<class T>
unsigned int SharedVector<T>::size() const {
EnterCriticalSection(&cs);
unsigned int result = vect.size();
LeaveCriticalSection(&cs);
return result;
}
template<class T>
T& SharedVector<T>::operator[](int index) {
EnterCriticalSection(&cs);
T result = vect[index];
LeaveCriticalSection(&cs);
return result;
}
template<class T>
SharedVector<T>::~SharedVector() {
DeleteCriticalSection(&cs);
}
While compiling I have such a problem for calling EnterCriticalSection(&cs) and LeaveCriticalSection(&cs):
'EnterCriticalSection' : cannot convert parameter 1 from
'const CRITICAL_SECTION *' to 'LPCRITICAL_SECTION'
I do not know what is wrong. May be you can see. Just because I always used it this way and it was alright. windows.h is included
Just declare cs as:
mutable CRITICAL_SECTION cs;
or else remove the const clause on size()
Entering a critical section modifies the CRITICAL_SECTION, and leaving modifies it again. Since entering and leaving a critical section doesn't make the size() method call logically non-const, I'd say leave it declared const, and make cs mutable. This is the type of situation mutable was introduced for.
Also - take a look at Martin York's and Joe Mucchiello's suggestions - use RAII whenever possible to deal with any kind of resources that need to be cleaned up. This works just as well for critical sections as it does for pointers and file handles.
Also the code above is not Exception safe.
There is no guarantee that push_back() pop_back() will not throw. If they do they will leave your critical section permanently locked. You should create a locker class that calls EnterCriticalSection() on construction and LeaveCriticalSection() on destruction.
Also this makes your methods a lot easier to read. (see below)
class CriticalSectionLock
{
public:
CriticalSectionLock(CRITICAL_SECTION& cs)
: criticalSection(cs)
{
EnterCriticalSection(&criticalSection);
}
~CriticalSectionLock()
{
LeaveCriticalSection(&criticalSection);
}
private:
CRITICAL_SECTION& criticalSection;
};
// Usage
template
unsigned int SharedVector::size() const
{
CriticalSectionLock lock(cs);
return vect.size();
}
Another thing you should worry about. Is making sure that when you destroy the object you have ownership and that nobody else tries to take ownership during destruction. Hopefully your DestoryCriticalSection() takes care of this.
I prefer using a separate Acquisition object over your code. Your code if fragile when an exception occurs between the Enter and Leave calls:
class CS_Acquire {
CRITICAL_SECTION &cs;
public:
CS_Acquire(CRITICAL_SECTION& _cs) : cs(_cs) { EnterCriticalSection(cs); }
~CS_Acquire() { LeaveCriticalSection(cs); }
};
Then in your class methods you would code it as:
template <typename T>
void SharedVector::PushBack(const T& value) {
CS_Acquire acquire(&cs);
vect.push_back(value);
}
EnterCriticalSection doesn't take a const argument. That's a compilation error, BTW, not a linking error...
Also, are you sure you want to pass in a critical section into your ctor, and then have the ctor perform the InitializeCriticalSection call? If you want to share your critical section, I suppose you'd initialize it first, and then hand it out.
I see you have declared an empty copy constructor:
SharedVector(const SharedVector& rhs) {}
As I'm sure you are aware, this function does nothing, and it also leaves cs uninitialised. Because your class contains an instance of a CRITICAL_SECTION, you must be sure to disallow copy constructor and assignment operator calls unless you are going to implement them completely. You can do this by placing the following declarations in the private section of your class:
SharedVector(const SharedVector &);
SharedVector &operator=(const SharedVector &);
This prevents the compiler from auto-generating incorrect versions of these methods, and also prevents you from calling them in other code you write (because these are only declarations, but not definitions with {} code blocks).
Also, as Arnout mentioned, the constructor that takes a CRITICAL_SECTION& argument seems wrong. What your implementation does is copy the passed-in critical section to cs (which is not a valid thing to do with a CRITICAL_SECTION), then calls InitializeCriticalSection(&cs) which overwrites the copy you just did and creates a new critical section. To the caller who passed in a critical section, this seems to do the wrong thing by effectively ignoring whatever was passed in.
so, it`s something wrong with access rights.
I made size() method non-const and now it is ok.