Skip to content

Instantly share code, notes, and snippets.

@anttirt
Created August 18, 2011 21:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anttirt/1155300 to your computer and use it in GitHub Desktop.
Save anttirt/1155300 to your computer and use it in GitHub Desktop.
class A {
public:
typedef std::lock_guard<std::mutex> lock_t;
// ISO-IEC 14882-2003, 12.8#2:
// A non-template constructor for class X is a copy constructor if
// its first parameter is of type X&, const X&, volatile X& or const volatile X&,
// and either there are no other parameters or else all other parameters have default arguments (8.3.6).
// => this qualifies as a copy constructor, and the compiler will *not* generate another one
// ISO-IEC 14882-2003, 12.2#5:
// A temporary bound to a reference parameter in a function call (5.2.2) persists
// until the completion of the full expression containing the call.
// => the lock object will not be destroyed until the constructor call returns or throws an exception
// See also: http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
A(A const& rhs, lock_t const& = lock_t(rhs.mtx))
: data(rhs.data)
{
}
// A subtle possibility for a deadlock arises with the canonical operator= implementation:
// A& operator=(A const& rhs) {
// A(rhs).swap(*this); // the lock is not released until the full expression (*including* the call to swap) is complete
// return *this;
// }
// If the object is being assigned to itself, both the copy constructor and swap will
// attempt to lock the same mutex, so unless the mutex is re-entrant, this implementation
// of operator= will deadlock on self-assignment.
// A small change will remedy that:
A& operator=(A const& rhs) {
A tmp(rhs); // the lock is released after this statement, before the call to swap
tmp.swap(*this);
return *this;
}
private:
T data;
mutable std::mutex mtx;
};
// This class has one theoretical issue; a programmer with a split personality
// might want to intentionally hurt themselves and actually pass an unrelated
// lock object as the second argument. This would require extraordinary effort,
// but nevertheless, with a small addition even that can be prevented:
class A {
private:
// this is not accessible outside A
struct no_you_dont {};
public:
typedef std::lock_guard<std::mutex> lock_t;
// to provide an alternative value for the third argument, the programmer
// would also have to provide one for the second argument, which is impossible
// because it is of a type private to A
A(A const& rhs, no_you_dont const& = no_you_dont(), lock_t const& = lock_t(rhs.mtx))
: data(rhs.data)
{
}
private:
T data;
mutable std::mutex mtx;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment