Created
August 18, 2011 21:35
-
-
Save anttirt/1155300 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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