libfoedus-core
FOEDUS Core Library
foedus::soc::SharedCond Class Referencefinal

A conditional variable that can be placed in shared memory and used from multiple processes. More...

Detailed Description

A conditional variable that can be placed in shared memory and used from multiple processes.

Analogous to SharedMutex. This is for conditional variable. This class also avoids the pthread bug described in foedus::thread::ConditionVariable. This file does not assume C++11.

Definition at line 39 of file shared_cond.hpp.

#include <shared_cond.hpp>

Public Member Functions

 SharedCond ()
 
 ~SharedCond ()
 
 SharedCond (const SharedCond &)=delete
 
SharedCondoperator= (const SharedCond &)=delete
 
void initialize ()
 
void uninitialize ()
 
bool is_initialized () const
 
void wait (SharedMutexScope *scope)
 Unconditionally wait for the event. More...
 
bool timedwait (SharedMutexScope *scope, uint64_t timeout_nanosec)
 Wait for the event up to the given timeout. More...
 
void broadcast (SharedMutexScope *scope)
 Unblock all waiters. More...
 
void broadcast_nolock ()
 Unblock all waiters without a mutex held by the signaller. More...
 
void signal (SharedMutexScope *scope)
 Unblock one waiter. More...
 
SharedMutexget_mutex ()
 Returns the mutex that protects this condition variable. More...
 
bool exists_waiters () const
 A non-synchronized method to tell seemingly whether there is a waiter or not. More...
 

Constructor & Destructor Documentation

foedus::soc::SharedCond::SharedCond ( )
inline

Definition at line 41 of file shared_cond.hpp.

References initialize().

41 : initialized_(false), waiters_(0), notifiers_(0) { initialize(); }

Here is the call graph for this function:

foedus::soc::SharedCond::~SharedCond ( )
inline

Definition at line 42 of file shared_cond.hpp.

References uninitialize().

42 { uninitialize(); }

Here is the call graph for this function:

foedus::soc::SharedCond::SharedCond ( const SharedCond )
delete

Member Function Documentation

void foedus::soc::SharedCond::broadcast ( SharedMutexScope scope)

Unblock all waiters.

Parameters
[in,out]scopethe mutex scope that protects this conditional variable
Precondition
scope->is_locked_by_me() (the caller must have locked it)
scope->get_mutex() == &mutex_ (the scope should be protecting this variable)
Postcondition
scope is no longer locked

You should set the real condition variable itself after locking the mutex before calling this method to avoid lost signals.

Attention
Consider using broadcast_nolock(). We encountered a deadlock bug with a very high contention. We were not sure where the problem is; maybe the glibc's pthread_cond_broadcast() issue, simply our code's bug (lack or duplicated release etc), or a contention that causes repeated wakeup/broadcast loop. But, we did observe that the problem went away with broadcast_nolock().
Deprecated:
see above. But, not yet 100% sure why it happened... We should have a wiki entry to track this issue.

Definition at line 123 of file shared_cond.cpp.

References ASSERT_ND, foedus::assorted::memory_fence_acq_rel(), foedus::assorted::spinlock_yield(), foedus::soc::ugly_atomic_dec(), foedus::soc::ugly_atomic_inc(), and foedus::soc::SharedMutexScope::unlock().

123  {
124  common_assert(scope);
125 
126  ugly_atomic_inc(&notifiers_);
127 
128  // to avoid the glibc 2.18 pthread bug in broadcast, we use signal, one by one.
129  scope->unlock();
130  while (waiters_ > 0) {
131  // int ret = ::pthread_cond_broadcast(&cond_);
132  int ret = ::pthread_cond_signal(&cond_);
133  ASSERT_ND(ret == 0);
136  }
137 
138  ugly_atomic_dec(&notifiers_);
139 }
void ugly_atomic_dec(uint32_t *address)
Definition: shared_cond.cpp:86
void spinlock_yield()
Invoke _mm_pause(), x86 PAUSE instruction, or something equivalent in the env.
void ugly_atomic_inc(uint32_t *address)
Definition: shared_cond.cpp:83
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
Definition: assert_nd.hpp:72
void memory_fence_acq_rel()
Equivalent to std::atomic_thread_fence(std::memory_order_acq_rel).

Here is the call graph for this function:

void foedus::soc::SharedCond::broadcast_nolock ( )

Unblock all waiters without a mutex held by the signaller.

You should set the real condition variable itself after locking the mutex, AND release it before calling this method. This method does not assume a mutex, thus a lost signal is possible.

Definition at line 141 of file shared_cond.cpp.

References ASSERT_ND, foedus::soc::ugly_atomic_dec(), and foedus::soc::ugly_atomic_inc().

141  {
142  ugly_atomic_inc(&notifiers_);
143  // int ret = ::pthread_cond_broadcast(&cond_);
144  int ret = ::pthread_cond_signal(&cond_);
145  ASSERT_ND(ret == 0);
146  ugly_atomic_dec(&notifiers_);
147 }
void ugly_atomic_dec(uint32_t *address)
Definition: shared_cond.cpp:86
void ugly_atomic_inc(uint32_t *address)
Definition: shared_cond.cpp:83
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
Definition: assert_nd.hpp:72

Here is the call graph for this function:

bool foedus::soc::SharedCond::exists_waiters ( ) const
inline

A non-synchronized method to tell seemingly whether there is a waiter or not.

The caller is responsible for using this method with appropriate fences, retries, etc.

Definition at line 134 of file shared_cond.hpp.

134 { return waiters_ != 0; }
SharedMutex* foedus::soc::SharedCond::get_mutex ( )
inline

Returns the mutex that protects this condition variable.

You must lock this mutex BEFORE you call wait/notify/etc in this class along with checking the real boolean condition itself. Otherwise, you will get lost signals. This is why the methods above receive SharedMutexScope as parameter.

Definition at line 128 of file shared_cond.hpp.

128 { return &mutex_; }
void foedus::soc::SharedCond::initialize ( )

Definition at line 33 of file shared_cond.cpp.

References ASSERT_ND, foedus::soc::SharedMutex::initialize(), and uninitialize().

Referenced by SharedCond().

33  {
34  uninitialize();
35  waiters_ = 0;
36  notifiers_ = 0;
37  mutex_.initialize();
38  int attr_ret = ::pthread_condattr_init(&attr_);
39  ASSERT_ND(attr_ret == 0);
40 
41  int shared_ret = ::pthread_condattr_setpshared(&attr_, PTHREAD_PROCESS_SHARED);
42  ASSERT_ND(shared_ret == 0);
43 
44  int cond_ret = ::pthread_cond_init(&cond_, &attr_);
45  ASSERT_ND(cond_ret == 0);
46 
47  initialized_ = true;
48 }
void initialize(bool recursive=false)
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
Definition: assert_nd.hpp:72

Here is the call graph for this function:

Here is the caller graph for this function:

bool foedus::soc::SharedCond::is_initialized ( ) const
inline

Definition at line 50 of file shared_cond.hpp.

50 { return initialized_; }
SharedCond& foedus::soc::SharedCond::operator= ( const SharedCond )
delete
void foedus::soc::SharedCond::signal ( SharedMutexScope scope)

Unblock one waiter.

Parameters
[in,out]scopethe mutex scope that protects this conditional variable
Precondition
scope->is_locked_by_me() (the caller must have locked it)
scope->get_mutex() == &mutex_ (the scope should be protecting this variable)
Postcondition
scope is no longer locked

You should set the real condition variable itself after locking the mutex before calling this method to avoid lost signals.

Definition at line 149 of file shared_cond.cpp.

References ASSERT_ND, foedus::soc::ugly_atomic_dec(), foedus::soc::ugly_atomic_inc(), and foedus::soc::SharedMutexScope::unlock().

149  {
150  common_assert(scope);
151 
152  ugly_atomic_inc(&notifiers_);
153 
154  scope->unlock();
155  if (waiters_ > 0) {
156  int ret = ::pthread_cond_signal(&cond_);
157  ASSERT_ND(ret == 0);
158  }
159 
160  ugly_atomic_dec(&notifiers_);
161 }
void ugly_atomic_dec(uint32_t *address)
Definition: shared_cond.cpp:86
void ugly_atomic_inc(uint32_t *address)
Definition: shared_cond.cpp:83
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
Definition: assert_nd.hpp:72

Here is the call graph for this function:

bool foedus::soc::SharedCond::timedwait ( SharedMutexScope scope,
uint64_t  timeout_nanosec 
)

Wait for the event up to the given timeout.

Parameters
[in,out]scopethe mutex scope that protects this conditional variable
[in]timeout_nanosectimeout in nanoseconds
Precondition
scope->is_locked_by_me() (the caller must have locked it)
scope->get_mutex() == &mutex_ (the scope should be protecting this variable)
Postcondition
scope is still locked
Returns
whether this thread received the signal (though still spurrious wakeup possible). false if timeout happened.

This method does NOT rule out spurrious wakeup as described above.

Definition at line 103 of file shared_cond.cpp.

References ASSERT_ND, CXX11_NULLPTR, foedus::soc::SharedMutex::get_raw_mutex(), foedus::soc::ugly_atomic_dec(), and foedus::soc::ugly_atomic_inc().

103  {
104  common_assert(scope);
105  struct timespec timeout;
106  struct timeval now;
107  ::gettimeofday(&now, CXX11_NULLPTR);
108  timeout.tv_sec = now.tv_sec + (timeout_nanosec / 1000000000ULL);
109  timeout.tv_nsec = now.tv_usec * 1000ULL + timeout_nanosec % 1000000000ULL;
110  timeout.tv_sec += (timeout.tv_nsec) / 1000000000ULL;
111  timeout.tv_nsec %= 1000000000ULL;
112 
113  ugly_atomic_inc(&waiters_);
114 
115  int ret = ::pthread_cond_timedwait(&cond_, mutex_.get_raw_mutex(), &timeout);
116  ASSERT_ND(ret == 0 || ret == ETIMEDOUT);
117 
118  ASSERT_ND(waiters_ > 0);
119  ugly_atomic_dec(&waiters_);
120  return ret == 0;
121 }
void ugly_atomic_dec(uint32_t *address)
Definition: shared_cond.cpp:86
#define CXX11_NULLPTR
Used in public headers in place of "nullptr" of C++11.
Definition: cxx11.hpp:132
pthread_mutex_t * get_raw_mutex()
void ugly_atomic_inc(uint32_t *address)
Definition: shared_cond.cpp:83
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
Definition: assert_nd.hpp:72

Here is the call graph for this function:

void foedus::soc::SharedCond::uninitialize ( )

Definition at line 50 of file shared_cond.cpp.

References ASSERT_ND, foedus::assorted::memory_fence_acquire(), foedus::assorted::spinlock_yield(), and foedus::soc::SharedMutex::uninitialize().

Referenced by initialize(), and ~SharedCond().

50  {
51  if (!initialized_) {
52  return;
53  }
54 
56  ASSERT_ND(waiters_ == 0);
57  // we must wait until all notifiers exit notify_all.
58  // this assumes no new notifiers are newly coming in this situation.
59  while (notifiers_ > 0) {
62  }
63 
64  mutex_.uninitialize();
65 
66  int cond_ret = ::pthread_cond_destroy(&cond_);
67  ASSERT_ND(cond_ret == 0);
68 
69  int attr_ret = ::pthread_condattr_destroy(&attr_);
70  ASSERT_ND(attr_ret == 0);
71 
72  initialized_ = false;
73 }
void spinlock_yield()
Invoke _mm_pause(), x86 PAUSE instruction, or something equivalent in the env.
void memory_fence_acquire()
Equivalent to std::atomic_thread_fence(std::memory_order_acquire).
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
Definition: assert_nd.hpp:72

Here is the call graph for this function:

Here is the caller graph for this function:

void foedus::soc::SharedCond::wait ( SharedMutexScope scope)

Unconditionally wait for the event.

Parameters
[in,out]scopethe mutex scope that protects this conditional variable
Precondition
scope->is_locked_by_me() (the caller must have locked it)
scope->get_mutex() == &mutex_ (the scope should be protecting this variable)
Postcondition
scope is still locked

This method does NOT rule out spurrious wakeup. We could receive a lambda to check the condition, but this class should be C++11-free. So, the caller should do the loop herself if she doesn't want a spurrious wakeup. Instead, you can easily avoid lost signals by checking the condition after locking the mutex before calling this method.

Definition at line 90 of file shared_cond.cpp.

References ASSERT_ND, foedus::soc::SharedMutex::get_raw_mutex(), foedus::soc::ugly_atomic_dec(), and foedus::soc::ugly_atomic_inc().

90  {
91  common_assert(scope);
92 
93  ugly_atomic_inc(&waiters_);
94 
95  // probably pthread_cond_wait implies a full fence, but to make sure.
96  int ret = ::pthread_cond_wait(&cond_, mutex_.get_raw_mutex());
97  ASSERT_ND(ret == 0);
98 
99  ASSERT_ND(waiters_ > 0);
100  ugly_atomic_dec(&waiters_);
101 }
void ugly_atomic_dec(uint32_t *address)
Definition: shared_cond.cpp:86
pthread_mutex_t * get_raw_mutex()
void ugly_atomic_inc(uint32_t *address)
Definition: shared_cond.cpp:83
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
Definition: assert_nd.hpp:72

Here is the call graph for this function:


The documentation for this class was generated from the following files: