Home | History | Annotate | Download | only in tests
      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:
     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 
     54 private:
     55   volatile LONG m_value;
     56 };
     57 
     58 class Mutex
     59 {
     60 public:
     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 
     70 private:
     71   CRITICAL_SECTION m_mutex;
     72 };
     73 
     74 class Thread
     75 {
     76 public:
     77   Thread() : m_thread(INVALID_HANDLE_VALUE) { }
     78   ~Thread() { }
     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   }
     85   void Join()
     86   { WaitForSingleObject(m_thread, INFINITE); }
     87 
     88 private:
     89   struct WrapperArgs
     90   {
     91     WrapperArgs(void* (*pf)(void*), void* arg) : m_pf(pf), m_arg(arg) { }
     92 
     93     void* (*m_pf)(void*);
     94     void* m_arg;
     95   };
     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:
    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); }
    115 private:
    116   volatile int m_value;
    117 };
    118 
    119 class Mutex
    120 {
    121 public:
    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 
    131 private:
    132   pthread_mutex_t m_mutex;
    133 };
    134 
    135 class Thread
    136 {
    137 public:
    138   Thread() : m_tid() { }
    139   ~Thread() { }
    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   }
    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 
    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 
    231 private:
    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:
    264   counter()
    265     : m_mutex(), m_count()
    266   { }
    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   }
    275   int get() const
    276   {
    277     int result;
    278     m_mutex.Lock();
    279     result = m_count;
    280     m_mutex.Unlock();
    281     return result;
    282   }
    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 
    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 
    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