//$$CDS-header$$
-
#ifndef CDSLIB_SYNC_MONITOR_H
#define CDSLIB_SYNC_MONITOR_H
-
#include <cds/details/defs.h>
-
namespace cds { namespace sync {
-
/**
@page cds_sync_monitor Synchronization monitor
-
A <a href="http://en.wikipedia.org/wiki/Monitor_%28synchronization%29">monitor</a> is synchronization construct
that allows threads to have both mutual exclusion and the ability to wait (block) for a certain condition to become true.
-
Some blocking data structure algoritms like the trees require per-node locking.
For huge trees containing millions of nodes it can be very inefficient to inject
the lock object into each node. Moreover, some operating systems may not support
lock object for the node if needed, and to lock the node.
The monitor strategy can significantly reduce the number of system objects
required for data structure.
-
<b>Implemetations</b>
-
\p libcds contains several monitor implementations:
- \p sync::injecting_monitor injects the lock object into each node.
That mock monitor is designed for user-space locking primitive like
for a node from the pool when needed. When the node is unlocked
the lock assigned to it is given back to the pool if no thread
references to that node.
-
<b>How to use</b>
-
If you use a container from \p libcds that requires a monitor, you should just
- specify required monitor type in container's traits. Usually, the monitor
+ specify required monitor type in container's traits. Usually, the monitor
is specified by \p traits::sync_monitor typedef, or by \p cds::opt::sync_monitor
option for container's \p make_traits metafunction.
-
- If you're developing a new container algorithm, you should know internal monitor
+ If you're developing a new container algorithm, you should know internal monitor
interface:
\code
class Monitor {
struct node_injection: public Node
{
// Monitor data to inject into container's node
- // ...
+ // ...
};
-
- // Locks the node
+ // Locks the node
template <typename Node>
void lock( Node& node );
-
// Unlocks the node
template <typename Node>
void unlock( Node& node );
-
// Scoped lock applyes RAII to Monitor
template <typename Node>
using scoped_lock = monitor_scoped_lock< pool_monitor, Node >;
};
\endcode
-
Monitor's data must be inject into container's node as \p m_SyncMonitorInjection data member:
\code
template <typename SyncMonitor>
typename SyncMonitor::node_injection m_SyncMonitorInjection;
};
\endcode
-
The monitor must be a member of your container:
\code
template <typename GC, typename T, typename Traits>
};
\endcode
*/
-
/// Monitor scoped lock (RAII)
/**
Template arguments:
public:
typedef Monitor monitor_type; ///< Monitor type
typedef Node node_type; ///< Node type
-
private:
//@cond
monitor_type& m_Monitor; ///< Monitor
node_type const& m_Node; ///< Our locked node
//@endcond
-
public:
/// Makes exclusive access to the node \p p by \p monitor
monitor_scoped_lock( monitor_type& monitor, node_type const& p )
{
monitor.lock( p );
}
-
/// Unlocks the node
~monitor_scoped_lock()
{
m_Monitor.unlock( m_Node );
}
};
-
}} // namespace cds::sync
-
#endif // #ifndef CDSLIB_SYNC_MONITOR_H
event_counter m_nLockCount; ///< Number of monitor \p lock() call
event_counter m_nUnlockCount; ///< Number of monitor \p unlock call
+ event_counter m_nMaxLocked; ///< Max number of simuntaneously locked mutexes
event_counter m_nLockContention; ///< Number of \p lock() contenton
event_counter m_nUnlockContention; ///< Number of \p unlock() contention
event_counter m_nLockAllocation; ///< Number of the lock allocation from the pool
event_counter m_nLockDeallocation; ///< Number of the lock deallocation
+ event_counter m_nMaxAllocated; ///< Max number of sumultanouusly allocated mutexes
//@cond
- void onLock() { ++m_nLockCount; }
+ void onLock()
+ {
+ ++m_nLockCount;
+ int nDiff = static_cast<int>( m_nLockCount.get() - m_nUnlockCount.get());
+ if ( nDiff > 0 && m_nMaxLocked.get() < static_cast<typename event_counter::value_type>( nDiff ))
+ m_nMaxLocked = static_cast<typename event_counter::value_type>( nDiff );
+ }
void onUnlock() { ++m_nUnlockCount; }
void onLockContention() { ++m_nLockContention; }
void onUnlockContention() { ++m_nUnlockContention;}
- void onLockAllocation() { ++m_nLockAllocation; }
+ void onLockAllocation()
+ {
+ ++m_nLockAllocation;
+ int nDiff = static_cast<int>( m_nLockAllocation.get() - m_nLockDeallocation.get());
+ if ( nDiff > 0 && m_nMaxAllocated.get() < static_cast<typename event_counter::value_type>( nDiff ) )
+ m_nMaxAllocated = static_cast<typename event_counter::value_type>( nDiff );
+ }
void onLockDeallocation() { ++m_nLockDeallocation;}
//@endcond
};
Template arguments:
- \p LockPool - the @ref cds_memory_pool "pool type". The pool must maintain
the objects of type \p std::mutex or similar. The access to the pool is not synchronized.
- - \p BackOff - back-off strategy for spinning, default is \p cds::backoff::LockDefault
+ - \p BackOff - back-off strategy for spinning, default is \p cds::backoff::yield
- \p Stat - enable (\p true) or disable (\p false, the default) monitor's internal statistics.
<b>How to use</b>
typedef cds::sync::pool_monitor< pool_type > sync_monitor;
\endcode
*/
- template <class LockPool, typename BackOff = cds::backoff::LockDefault, bool Stat = false >
+ template <class LockPool, typename BackOff = cds::backoff::yield, bool Stat = false >
class pool_monitor
{
public:
typedef typename pool_type::value_type lock_type; ///< node lock type
typedef typename std::conditional<
std::is_same< BackOff, cds::opt::none >::value,
- cds::backoff::LockDefault,
+ cds::backoff::yield,
BackOff
>::type back_off; ///< back-off strategy for spinning
typedef uint32_t refspin_type; ///< Reference counter + spin-lock bit
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_shb_cmp_stat) \
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_cmp_stat) \
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_shb_less_pool_simple) \
- CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_simple) \
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_shb_less_pool_simple_stat) \
+ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_simple) \
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_simple_stat) \
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_shb_less_pool_lazy) \
- CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_lazy) \
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_shb_less_pool_lazy_stat) \
+ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_lazy) \
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_lazy_stat) \
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_shb_less_pool_bounded) \
- CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_bounded) \
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_shb_less_pool_bounded_stat) \
+ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_bounded) \
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_sht_less_pool_bounded_stat) \
#else
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less)\
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less)\
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpi_cmp_stat)\
- CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_cmp_stat)\
- CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_cmp_stat)\
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpi_less_pool_simple)\
+ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_cmp_stat)\
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_simple)\
+ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_cmp_stat)\
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_simple)\
- CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpi_less_pool_simple_stat)\
- CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_simple_stat)\
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_simple_stat)\
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpi_less_pool_lazy)\
- CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_lazy)\
- CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_lazy)\
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpi_less_pool_lazy_stat)\
+ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_lazy)\
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_lazy_stat)\
+ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_lazy)\
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_lazy_stat)\
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpi_less_pool_bounded)\
- CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_bounded)\
- CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_bounded)\
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpi_less_pool_bounded_stat)\
+ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_bounded)\
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpb_less_pool_bounded_stat)\
+ CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_bounded)\
CPPUNIT_TEST(BronsonAVLTreeMap_rcu_gpt_less_pool_bounded_stat)\
CDSUNIT_TEST_BronsonAVLTreeMap_RCU_signal
return o << "cds::sync::pool_monitor statistics:\n"
<< "\t\t m_nLockCount: " << s.m_nLockCount.get() << "\n"
<< "\t\t m_nUnlockCount: " << s.m_nUnlockCount.get() << "\n"
+ << "\t\t m_nMaxLocked: " << s.m_nMaxLocked.get() << "\n"
<< "\t\t m_nLockContention: " << s.m_nLockContention.get() << "\n"
<< "\t\t m_nUnlockContention: " << s.m_nUnlockContention.get() << "\n"
<< "\t\t m_nLockAllocation: " << s.m_nLockAllocation.get() << "\n"
- << "\t\t m_nLockDeallocation: " << s.m_nLockDeallocation.get() << "\n";
+ << "\t\t m_nLockDeallocation: " << s.m_nLockDeallocation.get() << "\n"
+ << "\t\t m_nMaxAllocated: " << s.m_nMaxAllocated.get() << "\n";
}
}
#endif