//------------------------------------------------------------------------------ /* This file is part of Beast: https://github.com/vinniefalco/Beast Copyright 2013, Vinnie Falco Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ //============================================================================== #ifndef BEAST_CONCURRENTSTATE_BEASTHEADER #define BEAST_CONCURRENTSTATE_BEASTHEADER /*============================================================================*/ /** Structured access to a shared state. This template wraps an object containing members representing state information shared between multiple threads of execution, where any thread may need to read or write as needed. Synchronized access to the concurrent state is enforced at compile time through strongly typed accessor classes. This interface design facilitates source code pattern matching to find all areas where a concurrent state is accessed. There are three types of access: - ReadAccess Allows read access to the underlying object as `const`. ReadAccess may be granted to one or more threads simultaneously. If one or more threads have ReadAccess, requests to obtain WriteAccess are blocked. - WriteAccess Allows exclusive read/write access the underlying object. A WriteAccess request blocks until all existing ReadAccess and WriteAccess requests are released. While a WriteAccess exists, requests for ReadAccess will block. - UnlockedAccess Allows read access to the underlying object without using the lock. This can be helpful when designing concurrent structures through composition. It also makes it easier to search for places in code which use unlocked access. This code example demonstrates various forms of access to a ConcurrentState: @code struct SharedData { int value1; String value2; }; typedef ConcurrentState SharedState; SharedState sharedState; void readExample () { SharedState::ReadAccess state (sharedState); print (state->value1); // read access print (state->value2); // read access state->value1 = 42; // write disallowed: compile error } void writeExample () { SharedState::WriteAccess state (sharedState); state->value2 = "Label"; // write access } @endcode Forwarding constructors with up to eight parameters are provided. This lets you write constructors into the underlying data object. For example: @code struct SharedData { explicit SharedData (int numSlots) { m_array.reserve (numSlots); } std::vector m_array; }; // Construct SharedData with one parameter ConcurrentState sharedState (16); @endcode @param Object The type of object to encapsulate. @warning Recursive calls are not supported. It is generally not possible for a thread of execution to acquire write access while it already has read access. Such an attempt will result in undefined behavior. Calling into unknown code while holding a lock can cause deadlock. See @ref CallQueue::queue(). @ingroup beast_concurrent */ template class ConcurrentState : Uncopyable { public: class ReadAccess; class WriteAccess; class UnlockedAccess; /** Create a concurrent state. Up to 8 parameters can be specified in the constructor. These parameters are forwarded to the corresponding constructor in Object. If no constructor in Object matches the parameter list, a compile error is generated. */ /** @{ */ ConcurrentState () { } template explicit ConcurrentState (T1 t1) : m_obj (t1) { } template ConcurrentState (T1 t1, T2 t2) : m_obj (t1, t2) { } template ConcurrentState (T1 t1, T2 t2, T3 t3) : m_obj (t1, t2, t3) { } template ConcurrentState (T1 t1, T2 t2, T3 t3, T4 t4) : m_obj (t1, t2, t3, t4) { } template ConcurrentState (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) : m_obj (t1, t2, t3, t4, t5) { } template ConcurrentState (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6) : m_obj (t1, t2, t3, t4, t5, t6) { } template ConcurrentState (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7) : m_obj (t1, t2, t3, t4, t5, t6, t7) { } template ConcurrentState (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8) : m_obj (t1, t2, t3, t4, t5, t6, t7, t8) { } /** @} */ private: typedef ReadWriteMutex ReadWriteMutexType; Object m_obj; ReadWriteMutexType m_mutex; }; //------------------------------------------------------------------------------ /** Unlocked access to a ConcurrentState. Use sparingly. */ template class ConcurrentState ::UnlockedAccess : Uncopyable { public: explicit UnlockedAccess (ConcurrentState const& state) : m_state (state) { } Object const& getObject () const { return m_state.m_obj; } Object const& operator* () const { return getObject (); } Object const* operator-> () const { return &getObject (); } private: ConcurrentState const& m_state; }; //------------------------------------------------------------------------------ /** Read only access to a ConcurrentState */ template class ConcurrentState ::ReadAccess : Uncopyable { public: /** Create a ReadAccess from the specified ConcurrentState */ explicit ReadAccess (ConcurrentState const volatile& state) : m_state (const_cast (state)) , m_lock (m_state.m_mutex) { } /** Obtain a read only reference to Object */ Object const& getObject () const { return m_state.m_obj; } /** Obtain a read only reference to Object */ Object const& operator* () const { return getObject (); } /** Obtain a read only smart pointer to Object */ Object const* operator-> () const { return &getObject (); } private: ConcurrentState const& m_state; ReadWriteMutexType::ScopedReadLockType m_lock; }; //------------------------------------------------------------------------------ /** Read/write access to a ConcurrentState */ template class ConcurrentState ::WriteAccess : Uncopyable { public: explicit WriteAccess (ConcurrentState& state) : m_state (state) , m_lock (m_state.m_mutex) { } /** Obtain a read only reference to Object */ Object const* getObject () const { return m_state.m_obj; } /** Obtain a read only reference to Object */ Object const& operator* () const { return getObject (); } /** Obtain a read only smart pointer to Object */ Object const* operator-> () const { return &getObject (); } /** Obtain a read/write pointer to Object */ Object& getObject () { return m_state.m_obj; } /** Obtain a read/write reference to Object */ Object& operator* () { return getObject (); } /** Obtain a read/write smart pointer to Object */ Object* operator-> () { return &getObject (); } private: ConcurrentState& m_state; ReadWriteMutexType::ScopedWriteLockType m_lock; }; #endif