blob: 862ecb0ef9ebc44d04cfe40e2175c3abf89d17a7 [file] [log] [blame]
barte32f8622010-03-06 10:54:36 +00001/*
2 * Test program that illustrates how to annotate a smart pointer
3 * implementation. In a multithreaded program the following is relevant when
4 * working with smart pointers:
5 * - whether or not the objects pointed at are shared over threads.
6 * - whether or not the methods of the objects pointed at are thread-safe.
7 * - whether or not the smart pointer objects are shared over threads.
8 * - whether or not the smart pointer object itself is thread-safe.
9 *
10 * Most smart pointer implemenations are not thread-safe
11 * (e.g. boost::shared_ptr<>, tr1::shared_ptr<> and the smart_ptr<>
12 * implementation below). This means that it is not safe to modify a shared
13 * pointer object that is shared over threads without proper synchronization.
14 *
15 * Even for non-thread-safe smart pointers it is possible to have different
16 * threads access the same object via smart pointers without triggering data
17 * races on the smart pointer objects.
18 *
19 * A smart pointer implementation guarantees that the destructor of the object
20 * pointed at is invoked after the last smart pointer that points to that
21 * object has been destroyed or reset. Data race detection tools cannot detect
22 * this ordering without explicit annotation for smart pointers that track
23 * references without invoking synchronization operations recognized by data
24 * race detection tools.
25 */
26
27
28#include <cassert> // assert()
29#include <climits> // PTHREAD_STACK_MIN
bart5530f5f2010-03-07 10:42:15 +000030#include <iostream> // std::cerr
barte32f8622010-03-06 10:54:36 +000031#include <stdlib.h> // atoi()
bartc710de62010-03-04 08:51:30 +000032#ifdef _WIN32
barte32f8622010-03-06 10:54:36 +000033#include <process.h> // _beginthreadex()
34#include <windows.h> // CRITICAL_SECTION
bartc710de62010-03-04 08:51:30 +000035#else
barte32f8622010-03-06 10:54:36 +000036#include <pthread.h> // pthread_mutex_t
bartc710de62010-03-04 08:51:30 +000037#endif
bart5530f5f2010-03-07 10:42:15 +000038#include "unified_annotations.h"
39
40
bart19aa3922011-07-13 10:43:05 +000041static bool s_enable_annotations;
barte32f8622010-03-06 10:54:36 +000042
bartc710de62010-03-04 08:51:30 +000043
44#ifdef _WIN32
barte32f8622010-03-06 10:54:36 +000045
bartc710de62010-03-04 08:51:30 +000046class AtomicInt32
47{
48public:
49 AtomicInt32(const int value = 0) : m_value(value) { }
50 ~AtomicInt32() { }
51 LONG operator++() { return InterlockedIncrement(&m_value); }
52 LONG operator--() { return InterlockedDecrement(&m_value); }
53
54private:
55 volatile LONG m_value;
56};
57
58class Mutex
59{
60public:
61 Mutex() : m_mutex()
62 { InitializeCriticalSection(&m_mutex); }
63 ~Mutex()
64 { DeleteCriticalSection(&m_mutex); }
65 void Lock()
66 { EnterCriticalSection(&m_mutex); }
67 void Unlock()
68 { LeaveCriticalSection(&m_mutex); }
69
70private:
71 CRITICAL_SECTION m_mutex;
72};
73
bartc710de62010-03-04 08:51:30 +000074class Thread
75{
76public:
77 Thread() : m_thread(INVALID_HANDLE_VALUE) { }
78 ~Thread() { }
79 void Create(void* (*pf)(void*), void* arg)
80 {
barte32f8622010-03-06 10:54:36 +000081 WrapperArgs* wrapper_arg_p = new WrapperArgs(pf, arg);
82 m_thread = reinterpret_cast<HANDLE>(_beginthreadex(NULL, 0, wrapper,
83 wrapper_arg_p, 0, NULL));
bartc710de62010-03-04 08:51:30 +000084 }
85 void Join()
86 { WaitForSingleObject(m_thread, INFINITE); }
87
88private:
barte32f8622010-03-06 10:54:36 +000089 struct WrapperArgs
bartc710de62010-03-04 08:51:30 +000090 {
barte32f8622010-03-06 10:54:36 +000091 WrapperArgs(void* (*pf)(void*), void* arg) : m_pf(pf), m_arg(arg) { }
bartc710de62010-03-04 08:51:30 +000092
93 void* (*m_pf)(void*);
94 void* m_arg;
95 };
96 static unsigned int __stdcall wrapper(void* arg)
97 {
barte32f8622010-03-06 10:54:36 +000098 WrapperArgs* wrapper_arg_p = reinterpret_cast<WrapperArgs*>(arg);
99 WrapperArgs wa = *wrapper_arg_p;
bartc710de62010-03-04 08:51:30 +0000100 delete wrapper_arg_p;
101 return reinterpret_cast<unsigned>((wa.m_pf)(wa.m_arg));
102 }
103 HANDLE m_thread;
104};
barte32f8622010-03-06 10:54:36 +0000105
bartc710de62010-03-04 08:51:30 +0000106#else // _WIN32
barte32f8622010-03-06 10:54:36 +0000107
bartc710de62010-03-04 08:51:30 +0000108class AtomicInt32
109{
110public:
111 AtomicInt32(const int value = 0) : m_value(value) { }
112 ~AtomicInt32() { }
113 int operator++() { return __sync_add_and_fetch(&m_value, 1); }
114 int operator--() { return __sync_sub_and_fetch(&m_value, 1); }
115private:
116 volatile int m_value;
117};
118
119class Mutex
120{
121public:
122 Mutex() : m_mutex()
123 { pthread_mutex_init(&m_mutex, NULL); }
124 ~Mutex()
125 { pthread_mutex_destroy(&m_mutex); }
126 void Lock()
127 { pthread_mutex_lock(&m_mutex); }
128 void Unlock()
129 { pthread_mutex_unlock(&m_mutex); }
130
131private:
132 pthread_mutex_t m_mutex;
133};
134
bartc710de62010-03-04 08:51:30 +0000135class Thread
136{
137public:
138 Thread() : m_tid() { }
139 ~Thread() { }
140 void Create(void* (*pf)(void*), void* arg)
barte32f8622010-03-06 10:54:36 +0000141 {
142 pthread_attr_t attr;
143 pthread_attr_init(&attr);
144 pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + 4096);
145 pthread_create(&m_tid, &attr, pf, arg);
146 pthread_attr_destroy(&attr);
147 }
bartc710de62010-03-04 08:51:30 +0000148 void Join()
149 { pthread_join(m_tid, NULL); }
150private:
151 pthread_t m_tid;
152};
barte32f8622010-03-06 10:54:36 +0000153
154#endif // !defined(_WIN32)
bartc710de62010-03-04 08:51:30 +0000155
bart21e49d72010-03-02 08:57:50 +0000156
157template<class T>
158class smart_ptr
159{
160public:
bartc710de62010-03-04 08:51:30 +0000161 typedef AtomicInt32 counter_t;
bart21e49d72010-03-02 08:57:50 +0000162
163 template <typename Q> friend class smart_ptr;
164
165 explicit smart_ptr()
166 : m_ptr(NULL), m_count_ptr(NULL)
167 { }
168
169 explicit smart_ptr(T* const pT)
170 : m_ptr(NULL), m_count_ptr(NULL)
171 {
172 set(pT, pT ? new counter_t(0) : NULL);
173 }
174
175 template <typename Q>
176 explicit smart_ptr(Q* const q)
177 : m_ptr(NULL), m_count_ptr(NULL)
178 {
179 set(q, q ? new counter_t(0) : NULL);
180 }
181
182 ~smart_ptr()
183 {
184 set(NULL, NULL);
185 }
186
187 smart_ptr(const smart_ptr<T>& sp)
188 : m_ptr(NULL), m_count_ptr(NULL)
189 {
190 set(sp.m_ptr, sp.m_count_ptr);
191 }
192
193 template <typename Q>
194 smart_ptr(const smart_ptr<Q>& sp)
195 : m_ptr(NULL), m_count_ptr(NULL)
196 {
197 set(sp.m_ptr, sp.m_count_ptr);
198 }
199
200 smart_ptr& operator=(const smart_ptr<T>& sp)
201 {
202 set(sp.m_ptr, sp.m_count_ptr);
203 return *this;
204 }
205
206 smart_ptr& operator=(T* const p)
207 {
208 set(p, p ? new counter_t(0) : NULL);
209 return *this;
210 }
211
212 template <typename Q>
213 smart_ptr& operator=(Q* const q)
214 {
215 set(q, q ? new counter_t(0) : NULL);
216 return *this;
217 }
218
219 T* operator->() const
220 {
221 assert(m_ptr);
222 return m_ptr;
223 }
224
225 T& operator*() const
226 {
227 assert(m_ptr);
228 return *m_ptr;
229 }
230
231private:
bartc710de62010-03-04 08:51:30 +0000232 void set(T* const pT, counter_t* const count_ptr)
bart21e49d72010-03-02 08:57:50 +0000233 {
234 if (m_ptr != pT)
235 {
barte32f8622010-03-06 10:54:36 +0000236 if (m_count_ptr)
bart21e49d72010-03-02 08:57:50 +0000237 {
bart5530f5f2010-03-07 10:42:15 +0000238 if (s_enable_annotations)
bart62049c42010-09-09 10:12:43 +0000239 U_ANNOTATE_HAPPENS_BEFORE(m_count_ptr);
barte32f8622010-03-06 10:54:36 +0000240 if (--(*m_count_ptr) == 0)
241 {
bart5530f5f2010-03-07 10:42:15 +0000242 if (s_enable_annotations)
bart62049c42010-09-09 10:12:43 +0000243 U_ANNOTATE_HAPPENS_AFTER(m_count_ptr);
barte32f8622010-03-06 10:54:36 +0000244 delete m_ptr;
245 m_ptr = NULL;
246 delete m_count_ptr;
247 m_count_ptr = NULL;
248 }
bart21e49d72010-03-02 08:57:50 +0000249 }
250 m_ptr = pT;
251 m_count_ptr = count_ptr;
252 if (count_ptr)
bartc710de62010-03-04 08:51:30 +0000253 ++(*m_count_ptr);
bart21e49d72010-03-02 08:57:50 +0000254 }
255 }
256
bartc710de62010-03-04 08:51:30 +0000257 T* m_ptr;
258 counter_t* m_count_ptr;
bart21e49d72010-03-02 08:57:50 +0000259};
260
261class counter
262{
263public:
264 counter()
265 : m_mutex(), m_count()
barte32f8622010-03-06 10:54:36 +0000266 { }
bart21e49d72010-03-02 08:57:50 +0000267 ~counter()
barte32f8622010-03-06 10:54:36 +0000268 {
269 // Data race detection tools that do not recognize the
270 // ANNOTATE_HAPPENS_BEFORE() / ANNOTATE_HAPPENS_AFTER() annotations in the
271 // smart_ptr<> implementation will report that the assignment below
272 // triggers a data race.
273 m_count = -1;
274 }
bart21e49d72010-03-02 08:57:50 +0000275 int get() const
276 {
277 int result;
bartc710de62010-03-04 08:51:30 +0000278 m_mutex.Lock();
bart21e49d72010-03-02 08:57:50 +0000279 result = m_count;
bartc710de62010-03-04 08:51:30 +0000280 m_mutex.Unlock();
bart21e49d72010-03-02 08:57:50 +0000281 return result;
282 }
283 int post_increment()
284 {
285 int result;
bartc710de62010-03-04 08:51:30 +0000286 m_mutex.Lock();
bart21e49d72010-03-02 08:57:50 +0000287 result = m_count++;
bartc710de62010-03-04 08:51:30 +0000288 m_mutex.Unlock();
bart21e49d72010-03-02 08:57:50 +0000289 return result;
290 }
291
292private:
bartc710de62010-03-04 08:51:30 +0000293 mutable Mutex m_mutex;
294 int m_count;
bart21e49d72010-03-02 08:57:50 +0000295};
296
bart21e49d72010-03-02 08:57:50 +0000297static void* thread_func(void* arg)
298{
barte32f8622010-03-06 10:54:36 +0000299 smart_ptr<counter>* pp = reinterpret_cast<smart_ptr<counter>*>(arg);
300 (*pp)->post_increment();
301 *pp = NULL;
302 delete pp;
bart21e49d72010-03-02 08:57:50 +0000303 return NULL;
304}
305
306int main(int argc, char** argv)
307{
barte32f8622010-03-06 10:54:36 +0000308 const int nthreads = std::max(argc > 1 ? atoi(argv[1]) : 1, 1);
bart5530f5f2010-03-07 10:42:15 +0000309 const int iterations = std::max(argc > 2 ? atoi(argv[2]) : 1, 1);
310 s_enable_annotations = argc > 3 ? !!atoi(argv[3]) : true;
bart21e49d72010-03-02 08:57:50 +0000311
bart5530f5f2010-03-07 10:42:15 +0000312 for (int j = 0; j < iterations; ++j)
313 {
314 Thread T[nthreads];
315
316 smart_ptr<counter> p(new counter);
317 p->post_increment();
318 for (int i = 0; i < nthreads; ++i)
319 T[i].Create(thread_func, new smart_ptr<counter>(p));
bart863f1eb2011-08-30 15:09:37 +0000320 {
321 // Avoid that counter.m_mutex introduces a false ordering on the
322 // counter.m_count accesses.
323 const timespec delay = { 0, 100 * 1000 * 1000 };
324 nanosleep(&delay, 0);
325 }
bart5530f5f2010-03-07 10:42:15 +0000326 p = NULL;
327 for (int i = 0; i < nthreads; ++i)
328 T[i].Join();
329 }
330 std::cerr << "Done.\n";
bart21e49d72010-03-02 08:57:50 +0000331 return 0;
332}
barte32f8622010-03-06 10:54:36 +0000333
334// Local variables:
335// c-basic-offset: 2
336// End: