• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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
30 #include <iostream>    // std::cerr
31 #include <stdlib.h>    // atoi()
32 #ifdef _WIN32
33 #include <process.h>   // _beginthreadex()
34 #include <windows.h>   // CRITICAL_SECTION
35 #else
36 #include <pthread.h>   // pthread_mutex_t
37 #endif
38 #include "unified_annotations.h"
39 
40 
41 static bool s_enable_annotations;
42 
43 
44 #ifdef _WIN32
45 
46 class AtomicInt32
47 {
48 public:
AtomicInt32(const int value=0)49   AtomicInt32(const int value = 0) : m_value(value) { }
~AtomicInt32()50   ~AtomicInt32() { }
operator ++()51   LONG operator++() { return InterlockedIncrement(&m_value); }
operator --()52   LONG operator--() { return InterlockedDecrement(&m_value); }
53 
54 private:
55   volatile LONG m_value;
56 };
57 
58 class Mutex
59 {
60 public:
Mutex()61   Mutex() : m_mutex()
62   { InitializeCriticalSection(&m_mutex); }
~Mutex()63   ~Mutex()
64   { DeleteCriticalSection(&m_mutex); }
Lock()65   void Lock()
66   { EnterCriticalSection(&m_mutex); }
Unlock()67   void Unlock()
68   { LeaveCriticalSection(&m_mutex); }
69 
70 private:
71   CRITICAL_SECTION m_mutex;
72 };
73 
74 class Thread
75 {
76 public:
Thread()77   Thread() : m_thread(INVALID_HANDLE_VALUE) { }
~Thread()78   ~Thread() { }
Create(void * (* pf)(void *),void * arg)79   void Create(void* (*pf)(void*), void* arg)
80   {
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));
84   }
Join()85   void Join()
86   { WaitForSingleObject(m_thread, INFINITE); }
87 
88 private:
89   struct WrapperArgs
90   {
WrapperArgsThread::WrapperArgs91     WrapperArgs(void* (*pf)(void*), void* arg) : m_pf(pf), m_arg(arg) { }
92 
93     void* (*m_pf)(void*);
94     void* m_arg;
95   };
wrapper(void * arg)96   static unsigned int __stdcall wrapper(void* arg)
97   {
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));
102   }
103   HANDLE m_thread;
104 };
105 
106 #else // _WIN32
107 
108 class AtomicInt32
109 {
110 public:
AtomicInt32(const int value=0)111   AtomicInt32(const int value = 0) : m_value(value) { }
~AtomicInt32()112   ~AtomicInt32() { }
operator ++()113   int operator++() { return __sync_add_and_fetch(&m_value, 1); }
operator --()114   int operator--() { return __sync_sub_and_fetch(&m_value, 1); }
115 private:
116   volatile int m_value;
117 };
118 
119 class Mutex
120 {
121 public:
Mutex()122   Mutex() : m_mutex()
123   { pthread_mutex_init(&m_mutex, NULL); }
~Mutex()124   ~Mutex()
125   { pthread_mutex_destroy(&m_mutex); }
Lock()126   void Lock()
127   { pthread_mutex_lock(&m_mutex); }
Unlock()128   void Unlock()
129   { pthread_mutex_unlock(&m_mutex); }
130 
131 private:
132   pthread_mutex_t m_mutex;
133 };
134 
135 class Thread
136 {
137 public:
Thread()138   Thread() : m_tid() { }
~Thread()139   ~Thread() { }
Create(void * (* pf)(void *),void * arg)140   void Create(void* (*pf)(void*), void* arg)
141   {
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   }
Join()148   void Join()
149   { pthread_join(m_tid, NULL); }
150 private:
151   pthread_t m_tid;
152 };
153 
154 #endif // !defined(_WIN32)
155 
156 
157 template<class T>
158 class smart_ptr
159 {
160 public:
161   typedef AtomicInt32 counter_t;
162 
163   template <typename Q> friend class smart_ptr;
164 
smart_ptr()165   explicit smart_ptr()
166     : m_ptr(NULL), m_count_ptr(NULL)
167   { }
168 
smart_ptr(T * const pT)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>
smart_ptr(Q * const 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 
~smart_ptr()182   ~smart_ptr()
183   {
184     set(NULL, NULL);
185   }
186 
smart_ptr(const smart_ptr<T> & sp)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>
smart_ptr(const smart_ptr<Q> & sp)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 
operator =(const smart_ptr<T> & sp)200   smart_ptr& operator=(const smart_ptr<T>& sp)
201   {
202     set(sp.m_ptr, sp.m_count_ptr);
203     return *this;
204   }
205 
operator =(T * const p)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>
operator =(Q * const q)213   smart_ptr& operator=(Q* const q)
214   {
215     set(q, q ? new counter_t(0) : NULL);
216     return *this;
217   }
218 
operator ->() const219   T* operator->() const
220   {
221     assert(m_ptr);
222     return m_ptr;
223   }
224 
operator *() const225   T& operator*() const
226   {
227     assert(m_ptr);
228     return *m_ptr;
229   }
230 
231 private:
set(T * const pT,counter_t * const count_ptr)232   void set(T* const pT, counter_t* const count_ptr)
233   {
234     if (m_ptr != pT)
235     {
236       if (m_count_ptr)
237       {
238 	if (s_enable_annotations)
239 	  U_ANNOTATE_HAPPENS_BEFORE(m_count_ptr);
240 	if (--(*m_count_ptr) == 0)
241 	{
242 	  if (s_enable_annotations)
243 	    U_ANNOTATE_HAPPENS_AFTER(m_count_ptr);
244 	  delete m_ptr;
245 	  m_ptr = NULL;
246 	  delete m_count_ptr;
247 	  m_count_ptr = NULL;
248 	}
249       }
250       m_ptr = pT;
251       m_count_ptr = count_ptr;
252       if (count_ptr)
253 	++(*m_count_ptr);
254     }
255   }
256 
257   T*         m_ptr;
258   counter_t* m_count_ptr;
259 };
260 
261 class counter
262 {
263 public:
counter()264   counter()
265     : m_mutex(), m_count()
266   { }
~counter()267   ~counter()
268   {
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   }
get() const275   int get() const
276   {
277     int result;
278     m_mutex.Lock();
279     result = m_count;
280     m_mutex.Unlock();
281     return result;
282   }
post_increment()283   int post_increment()
284   {
285     int result;
286     m_mutex.Lock();
287     result = m_count++;
288     m_mutex.Unlock();
289     return result;
290   }
291 
292 private:
293   mutable Mutex m_mutex;
294   int           m_count;
295 };
296 
thread_func(void * arg)297 static void* thread_func(void* arg)
298 {
299   smart_ptr<counter>* pp = reinterpret_cast<smart_ptr<counter>*>(arg);
300   (*pp)->post_increment();
301   *pp = NULL;
302   delete pp;
303   return NULL;
304 }
305 
main(int argc,char ** argv)306 int main(int argc, char** argv)
307 {
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;
311 
312   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));
320     {
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     }
326     p = NULL;
327     for (int i = 0; i < nthreads; ++i)
328       T[i].Join();
329   }
330   std::cerr << "Done.\n";
331   return 0;
332 }
333 
334 // Local variables:
335 // c-basic-offset: 2
336 // End:
337