Files
rippled/modules/beast_basics/threads/beast_ConcurrentState.h
2013-06-17 07:09:33 -07:00

312 lines
8.8 KiB
C++

/*============================================================================*/
/*
VFLib: https://github.com/vinniefalco/VFLib
Copyright (C) 2008 by Vinnie Falco <vinnie.falco@gmail.com>
This library contains portions of other open source products covered by
separate licenses. Please see the corresponding source files for specific
terms.
VFLib is provided under the terms of The MIT License (MIT):
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE 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 <SharedData> 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 <AudioSampleBuffer*> m_array;
};
// Construct SharedData with one parameter
ConcurrentState <SharedData> 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 Object>
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 <class T1>
explicit ConcurrentState (T1 t1)
: m_obj (t1) { }
template <class T1, class T2>
ConcurrentState (T1 t1, T2 t2)
: m_obj (t1, t2) { }
template <class T1, class T2, class T3>
ConcurrentState (T1 t1, T2 t2, T3 t3)
: m_obj (t1, t2, t3) { }
template <class T1, class T2, class T3, class T4>
ConcurrentState (T1 t1, T2 t2, T3 t3, T4 t4)
: m_obj (t1, t2, t3, t4) { }
template <class T1, class T2, class T3, class T4, class T5>
ConcurrentState (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
: m_obj (t1, t2, t3, t4, t5) { }
template <class T1, class T2, class T3, class T4, class T5, class T6>
ConcurrentState (T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
: m_obj (t1, t2, t3, t4, t5, t6) { }
template <class T1, class T2, class T3, class T4, class T5, class T6, class T7>
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 <class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
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 Object>
class ConcurrentState <Object>::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 Object>
class ConcurrentState <Object>::ReadAccess : Uncopyable
{
public:
/** Create a ReadAccess from the specified ConcurrentState */
explicit ReadAccess (ConcurrentState const volatile& state)
: m_state (const_cast <ConcurrentState const&> (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 Object>
class ConcurrentState <Object>::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