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.
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.
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.
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.
28 #include <cassert> // assert()
29 #include <climits> // PTHREAD_STACK_MIN
30 #include <iostream> // std::cerr
31 #include <stdlib.h> // atoi()
33 #include <process.h> // _beginthreadex()
34 #include <windows.h> // CRITICAL_SECTION
36 #include <pthread.h> // pthread_mutex_t
38 #include "unified_annotations.h"
41 static bool s_enable_annotations = true;
49 AtomicInt32(const int value = 0) : m_value(value) { }
51 LONG operator++() { return InterlockedIncrement(&m_value); }
52 LONG operator--() { return InterlockedDecrement(&m_value); }
55 volatile LONG m_value;
62 { InitializeCriticalSection(&m_mutex); }
64 { DeleteCriticalSection(&m_mutex); }
66 { EnterCriticalSection(&m_mutex); }
68 { LeaveCriticalSection(&m_mutex); }
71 CRITICAL_SECTION m_mutex;
77 Thread() : m_thread(INVALID_HANDLE_VALUE) { }
79 void Create(void* (*pf)(void*), void* arg)
81 WrapperArgs* wrapper_arg_p = new WrapperArgs(pf, arg);
82 m_thread = reinterpret_cast<HANDLE>(_beginthreadex(NULL, 0, wrapper,
83 wrapper_arg_p, 0, NULL));
86 { WaitForSingleObject(m_thread, INFINITE); }
91 WrapperArgs(void* (*pf)(void*), void* arg) : m_pf(pf), m_arg(arg) { }
96 static unsigned int __stdcall wrapper(void* arg)
98 WrapperArgs* wrapper_arg_p = reinterpret_cast<WrapperArgs*>(arg);
99 WrapperArgs wa = *wrapper_arg_p;
100 delete wrapper_arg_p;
101 return reinterpret_cast<unsigned>((wa.m_pf)(wa.m_arg));
111 AtomicInt32(const int value = 0) : m_value(value) { }
113 int operator++() { return __sync_add_and_fetch(&m_value, 1); }
114 int operator--() { return __sync_sub_and_fetch(&m_value, 1); }
116 volatile int m_value;
123 { pthread_mutex_init(&m_mutex, NULL); }
125 { pthread_mutex_destroy(&m_mutex); }
127 { pthread_mutex_lock(&m_mutex); }
129 { pthread_mutex_unlock(&m_mutex); }
132 pthread_mutex_t m_mutex;
138 Thread() : m_tid() { }
140 void Create(void* (*pf)(void*), void* arg)
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);
149 { pthread_join(m_tid, NULL); }
154 #endif // !defined(_WIN32)
161 typedef AtomicInt32 counter_t;
163 template <typename Q> friend class smart_ptr;
166 : m_ptr(NULL), m_count_ptr(NULL)
169 explicit smart_ptr(T* const pT)
170 : m_ptr(NULL), m_count_ptr(NULL)
172 set(pT, pT ? new counter_t(0) : NULL);
175 template <typename Q>
176 explicit smart_ptr(Q* const q)
177 : m_ptr(NULL), m_count_ptr(NULL)
179 set(q, q ? new counter_t(0) : NULL);
187 smart_ptr(const smart_ptr<T>& sp)
188 : m_ptr(NULL), m_count_ptr(NULL)
190 set(sp.m_ptr, sp.m_count_ptr);
193 template <typename Q>
194 smart_ptr(const smart_ptr<Q>& sp)
195 : m_ptr(NULL), m_count_ptr(NULL)
197 set(sp.m_ptr, sp.m_count_ptr);
200 smart_ptr& operator=(const smart_ptr<T>& sp)
202 set(sp.m_ptr, sp.m_count_ptr);
206 smart_ptr& operator=(T* const p)
208 set(p, p ? new counter_t(0) : NULL);
212 template <typename Q>
213 smart_ptr& operator=(Q* const q)
215 set(q, q ? new counter_t(0) : NULL);
219 T* operator->() const
232 void set(T* const pT, counter_t* const count_ptr)
238 if (s_enable_annotations)
239 U_ANNOTATE_HAPPENS_BEFORE(m_count_ptr);
240 if (--(*m_count_ptr) == 0)
242 if (s_enable_annotations)
243 U_ANNOTATE_HAPPENS_AFTER(m_count_ptr);
251 m_count_ptr = count_ptr;
258 counter_t* m_count_ptr;
265 : m_mutex(), m_count()
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.
293 mutable Mutex m_mutex;
297 static void* thread_func(void* arg)
299 smart_ptr<counter>* pp = reinterpret_cast<smart_ptr<counter>*>(arg);
300 (*pp)->post_increment();
306 int main(int argc, char** argv)
308 const int nthreads = std::max(argc > 1 ? atoi(argv[1]) : 1, 1);
309 const int iterations = std::max(argc > 2 ? atoi(argv[2]) : 1, 1);
310 s_enable_annotations = argc > 3 ? !!atoi(argv[3]) : true;
312 for (int j = 0; j < iterations; ++j)
316 smart_ptr<counter> p(new counter);
318 for (int i = 0; i < nthreads; ++i)
319 T[i].Create(thread_func, new smart_ptr<counter>(p));
321 for (int i = 0; i < nthreads; ++i)
324 std::cerr << "Done.\n";