-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvolatile.cpp
99 lines (79 loc) · 2.38 KB
/
volatile.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/***
====================================================
volatile: The Multithreaded Programmer's Best Friend
====================================================
from
http://www.drdobbs.com/cpp/volatile-the-multithreaded-programmers-b/184403766
Summary
When writing multithreaded programs, you can use volatile to your advantage.
You must stick to the following rules:
* Define all shared objects as volatile.
* Don't use volatile directly with primitive types.
* When defining shared classes, use volatile member functions to express
thread safety.
***/
#include <boost/thread.hpp>
#include <iostream>
using namespace boost;
template <typename T> class LockingPtr {
public:
// Constructors/destructors
LockingPtr(const volatile T& obj, const volatile mutex& mtx)
: pObj_(const_cast<T*>(&obj))
, pMtx_(const_cast<mutex*>(&mtx))
{
std::cout << BOOST_CURRENT_FUNCTION << " called" << std::endl;
pMtx_->lock();
}
~LockingPtr() { pMtx_->unlock(); }
// Pointer behavior
T& operator*() { return *pObj_; }
T* operator->() { return pObj_; }
private:
T* pObj_;
mutex* pMtx_;
// non copyable
LockingPtr(const LockingPtr&);
LockingPtr& operator=(const LockingPtr&);
};
/***
Notice the use of overloading.
Now Widget's user can invoke Operation using a uniform syntax either for
volatile objects and get thread safety, or for regular objects and get speed.
The user must be careful about defining the shared Widget objects as volatile.
***/
class Widget {
public:
Widget() {};
void Operation() const volatile;
// ...
protected:
void Operation() // NOLINT(readability-convert-member-functions-to-static)
{
std::cout << BOOST_CURRENT_FUNCTION << " called" << std::endl;
Helper();
};
static void Helper()
{
std::cout << BOOST_CURRENT_FUNCTION << " called" << std::endl;
}
private:
mutable mutex mtx_;
};
/***
When implementing a volatile member function, the first operation is usually
to lock this with a LockingPtr. Then the work is done by using the non-
volatile sibling:
***/
void Widget::Operation() const volatile
{
LockingPtr<Widget> lpThis(*this, mtx_);
assert(&(*lpThis) == const_cast<Widget*>(this));
lpThis->Operation(); // invokes the non-volatile function
}
int main()
{
volatile Widget wg; // thread save object
wg.Operation();
return 0;
}