/* * * Copyright (c) 1999 * Dr John Maddock * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Dr John Maddock makes no representations * about the suitability of this software for any purpose. * It is provided "as is" without express or implied warranty. * * * FILE lazy_eval.hpp * VERSION 0.95 * * Lazy Evaluation (of which reference counting is a special case) presents * problems in multi-threaded environments, where such techniques can make * it effectively impossible to make classes thread safe by applying external * locking. This file defines the types needed to make classes which use lazy * evaluation techniques as thread safe as any normal C++ class that does not use these * techniques. * * The types are: * boost::jm::counted_t - an atomic reference counter. * boost::jm::lock_type - a thread synchronisation lock. * boost::jm::guard_type - exception safe access to lock_type. * boost::jm::reference_helper - uses counted_t * to make type "Data" intrinsically * reference counted, and allocated via * an instance of "Allocator". * * The default behaviour of this file are that all these types are regular * C++ types - with no platform specific services used - ie not thread safe. * However you can define either BOOST_WIN32_THREADS or BOOST_PTHREADS to * enable thread safety for either win32 or POSIX threads respectively, other * platforms need to be added. * * */ #ifndef BOOST_LAZY_EVAL_HPP #define BOOST_LAZY_EVAL_HPP #ifdef BOOST_WIN32_THREADS #include namespace boost{ namespace _jm { class counted_t { public: counted_t(LONG init = 0) : value(init){} LONG operator--()volatile { return InterlockedDecrement(&value); } LONG operator++()volatile { return InterlockedIncrement(&value); } bool operator==(LONG l)const volatile { return value == l; } // is there a better way? bool operator!=(LONG l)const volatile { return value != l; } // is there a better way? private: LONG value; }; class guard_type; class lock_type { public: lock_type() { InitializeCriticalSection(&hmutex); } ~lock_type() { DeleteCriticalSection(&hmutex); } private: void acquire(bool aq) { if(aq) EnterCriticalSection(&hmutex); else LeaveCriticalSection(&hmutex); } CRITICAL_SECTION hmutex; friend class guard_type; }; class guard_type { public: guard_type(lock_type& m, bool aq = true) : mut(m), owned(false){ acquire(aq); } ~guard_type() { acquire(false); } void acquire(bool aq = true) { if(aq && !owned) { mut.acquire(true); owned = true; } else if(!aq && owned) { mut.acquire(false); owned = false; } } private: lock_type& mut; bool owned; }; } // namespace _jm } // namespace boost #elif defined(BOOST_PTHREADS) #include #include namespace boost{ namespace _jm{ typedef sig_atomic_t counted_t; // is this safe?????? class guard_type; class lock_type { public: lock_type() { pthread_mutex_init(&hmutex, NULL); } ~lock_type() { pthread_mutex_destroy(&hmutex); } private: void acquire(bool aq) { if(aq) pthread_mutex_lock(&hmutex); else pthread_mutex_unlock(&hmutex); } pthread_mutex_t hmutex; friend class guard_type; }; class guard_type { public: guard_type(lock_type& m, bool aq = true) : mut(m), owned(false){ acquire(aq); } ~guard_type() { acquire(false); } void acquire(bool aq = true) { if(aq && !owned) { mut.acquire(true); owned = true; } else if(!aq && owned) { mut.acquire(false); owned = false; } } private: lock_type& mut; bool owned; }; } // namespace _jm } // namespace boost #else // // regular C++ - no threading support: // namespace boost{ namespace _jm{ typedef int counted_t; struct lock_type{}; struct guard_type { guard_type(lock_type& m, bool aq = true){} void acquire(bool aq = true){} }; } // namespace _jm } // namespace boost #endif // // now define reference counted helper class: // namespace boost{ namespace _jm{ template class reference_helper : public Data { #ifndef BOOST_NO_MEMBER_TEMPLATES typedef typename Allocator::template rebind >::other self_alloc; #else typedef boost::_jm::alloc_binder, Allocator> self_alloc; #endif struct d: public self_alloc { counted_t count; d(const Allocator& a) : self_alloc(a), count(1) {} } a; public: reference_helper(const Allocator& a) : Data(a), a(a){} void addref() { ++(a.count); } void release() { if(0 == --(a.count)) { self_alloc al(a); al.destroy(this); al.deallocate(this, 1); } } static reference_helper* create(const Allocator& a) { self_alloc al(a); reference_helper* p = al.allocate(1); new (p) reference_helper(a); return p; }; bool is_shared() { return !(a.count == 1); } }; } // namespace _jm } // namespace boost #endif s