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