#ifndef __ThreadSafeWrapper_h_ #define __ThreadSafeWrapper_h_ #include #include "MiscSupport.h" #include "ThreadSafeRefCount.h" /* The idea is simple. We do it all the time in Java and C#. You create * an object and fill it with data. You store a read only copy of that data * in a global variable. When you want to update the data, you create a new * object. AFTER it's completely initialized, you store the new object in * the global variable. The object is thread safe because it's read only. * The variable is thread safe because it's just a pointer. * * In C++ we get the first part but not the second part. ThreadSafeWrapper * gives you a thread safe pointer variable. Any attempt to update the * variable will automatically be wrapped in a mutex. Most attempts to * read from the variable will automatically be wrapped in the same mutex. * (In some cases that is not necessary.) Notice that ThreadSafeWrapper * gives you a copy of a TSRefCount. This keeps the object alive * even if the global variable is changed. */ template < class T > class ThreadSafeWrapper : NoCopy, NoAssign { public: typedef TSRefCount< T > SmartPointer; private: mutable pthread_mutex_t _mutex; SmartPointer _pointer; public: ThreadSafeWrapper(SmartPointer const &pointer = NULL) : _pointer(pointer) { pthread_mutex_init(&_mutex, NULL); } ThreadSafeWrapper(T *pointer) : _pointer(pointer) { pthread_mutex_init(&_mutex, NULL); } ~ThreadSafeWrapper() { pthread_mutex_destroy(&_mutex); } void set(SmartPointer const &pointer) { pthread_mutex_lock(&_mutex); _pointer = pointer; pthread_mutex_unlock(&_mutex); } SmartPointer get() const { SmartPointer result; pthread_mutex_lock(&_mutex); result = _pointer; pthread_mutex_unlock(&_mutex); return result; } // This will set the destination to a copy of the value in the variable. // This doesn't always access the mutex. You can compare two // TSRefCount objects without holding a mutex. The // TSRefCount is not copied, and the mutex is not acquired, unless // the pointers are different. void get(SmartPointer &destination) const { if (destination != _pointer) destination = get(); } }; /* This is a wrapper around ThreadSafeWrapper::get(SmartPointer &). This * allows you to make a single object that automatically mirrors a * ThreadSafeWrapper object. You can access this object using * and ->, * just like you would with a normal pointer or a TSRefCount. * This is somewhat efficient and doesn't use a mutex unless it has to. */ template < class T > class ThreadSafeCache : NoCopy, NoAssign { public: typedef ThreadSafeWrapper< T > Wrapper; typedef typename Wrapper::SmartPointer SmartPointer; private: Wrapper const &_source; mutable SmartPointer _cache; public: ThreadSafeCache(Wrapper const &source) : _source(source) { } T const &operator *() const { _source.get(_cache); return *_cache; } T const *operator ->() const { _source.get(_cache); return &*_cache; } }; /* Same as ThreadSafeWrapper but this is a wrapper around a non-const poiner. */ template < class T > class NCThreadSafeWrapper : NoCopy, NoAssign { public: typedef NCTSRefCount< T > SmartPointer; private: mutable pthread_mutex_t _mutex; SmartPointer _pointer; public: NCThreadSafeWrapper(SmartPointer const &pointer = NULL) : _pointer(pointer) { pthread_mutex_init(&_mutex, NULL); } NCThreadSafeWrapper(T *pointer) : _pointer(pointer) { pthread_mutex_init(&_mutex, NULL); } ~NCThreadSafeWrapper() { pthread_mutex_destroy(&_mutex); } void set(SmartPointer const &pointer) { pthread_mutex_lock(&_mutex); _pointer = pointer; pthread_mutex_unlock(&_mutex); } SmartPointer get() const { SmartPointer result; pthread_mutex_lock(&_mutex); result = _pointer; pthread_mutex_unlock(&_mutex); return result; } // This will set the destination to a copy of the value in the variable. // This doesn't always access the mutex. You can compare two // TSRefCount objects without holding a mutex. The // TSRefCount is not copied, and the mutex is not acquired, unless // the pointers are different. void get(SmartPointer &destination) const { if (destination != _pointer) destination = get(); } }; #endif