THRIFT-3978: tighten up pthread mutex implementation, removing asserts and replacing them with exceptions
Client: cpp
This closes #1228
diff --git a/lib/cpp/src/thrift/concurrency/Mutex.cpp b/lib/cpp/src/thrift/concurrency/Mutex.cpp
index b6b915d..bcab05e 100644
--- a/lib/cpp/src/thrift/concurrency/Mutex.cpp
+++ b/lib/cpp/src/thrift/concurrency/Mutex.cpp
@@ -17,18 +17,24 @@
* under the License.
*/
+// needed to test for pthread implementation capabilities:
+#define __USE_GNU
+
#include <thrift/thrift-config.h>
#include <thrift/Thrift.h>
+#include <thrift/concurrency/Exception.h>
#include <thrift/concurrency/Mutex.h>
#include <thrift/concurrency/Util.h>
#include <assert.h>
-#ifdef HAVE_PTHREAD_H
+#include <stdlib.h>
#include <pthread.h>
-#endif
#include <signal.h>
+#include <string.h>
+#include <boost/format.hpp>
+#include <boost/shared_ptr.hpp>
using boost::shared_ptr;
namespace apache {
@@ -110,9 +116,17 @@
#define PROFILE_MUTEX_UNLOCKED()
#endif // THRIFT_PTHREAD_MUTEX_CONTENTION_PROFILING
+#define EINTR_LOOP(_CALL) int ret; do { ret = _CALL; } while (ret == EINTR)
+#define ABORT_ONFAIL(_CALL) { EINTR_LOOP(_CALL); if (ret) { abort(); } }
+#define THROW_SRE(_CALLSTR, RET) { throw SystemResourceException(boost::str(boost::format("%1% returned %2% (%3%)") % _CALLSTR % RET % ::strerror(RET))); }
+#define THROW_SRE_ONFAIL(_CALL) { EINTR_LOOP(_CALL); if (ret) { THROW_SRE(#_CALL, ret); } }
+#define THROW_SRE_TRYFAIL(_CALL) { EINTR_LOOP(_CALL); if (ret == 0) { return true; } else if (ret == EBUSY) { return false; } THROW_SRE(#_CALL, ret); }
+
/**
* Implementation of Mutex class using POSIX mutex
*
+ * Throws apache::thrift::concurrency::SystemResourceException on error.
+ *
* @version $Id:$
*/
class Mutex::impl {
@@ -128,19 +142,19 @@
~impl() {
if (initialized_) {
initialized_ = false;
- int ret = pthread_mutex_destroy(&pthread_mutex_);
- THRIFT_UNUSED_VARIABLE(ret);
- assert(ret == 0);
+ ABORT_ONFAIL(pthread_mutex_destroy(&pthread_mutex_));
}
}
void lock() const {
PROFILE_MUTEX_START_LOCK();
- pthread_mutex_lock(&pthread_mutex_);
+ THROW_SRE_ONFAIL(pthread_mutex_lock(&pthread_mutex_));
PROFILE_MUTEX_LOCKED();
}
- bool trylock() const { return (0 == pthread_mutex_trylock(&pthread_mutex_)); }
+ bool trylock() const {
+ THROW_SRE_TRYFAIL(pthread_mutex_trylock(&pthread_mutex_));
+ }
bool timedlock(int64_t milliseconds) const {
#if defined(_POSIX_TIMEOUTS) && _POSIX_TIMEOUTS >= 200112L
@@ -148,14 +162,16 @@
struct THRIFT_TIMESPEC ts;
Util::toTimespec(ts, milliseconds + Util::currentTime());
- int ret = pthread_mutex_timedlock(&pthread_mutex_, &ts);
+ EINTR_LOOP(pthread_mutex_timedlock(&pthread_mutex_, &ts));
if (ret == 0) {
PROFILE_MUTEX_LOCKED();
return true;
+ } else if (ret == ETIMEDOUT) {
+ PROFILE_MUTEX_NOT_LOCKED();
+ return false;
}
- PROFILE_MUTEX_NOT_LOCKED();
- return false;
+ THROW_SRE("pthread_mutex_timedlock(&pthread_mutex_, &ts)", ret);
#else
/* Otherwise follow solution used by Mono for Android */
struct THRIFT_TIMESPEC sleepytime, now, to;
@@ -180,7 +196,7 @@
void unlock() const {
PROFILE_MUTEX_START_UNLOCK();
- pthread_mutex_unlock(&pthread_mutex_);
+ THROW_SRE_ONFAIL(pthread_mutex_unlock(&pthread_mutex_));
PROFILE_MUTEX_UNLOCKED();
}
@@ -219,28 +235,16 @@
void Mutex::DEFAULT_INITIALIZER(void* arg) {
pthread_mutex_t* pthread_mutex = (pthread_mutex_t*)arg;
- int ret = pthread_mutex_init(pthread_mutex, NULL);
- THRIFT_UNUSED_VARIABLE(ret);
- assert(ret == 0);
+ THROW_SRE_ONFAIL(pthread_mutex_init(pthread_mutex, NULL));
}
-#if defined(PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP) \
- || defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
+#if defined(PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP) || defined(PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP) || defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
static void init_with_kind(pthread_mutex_t* mutex, int kind) {
pthread_mutexattr_t mutexattr;
- int ret = pthread_mutexattr_init(&mutexattr);
- assert(ret == 0);
-
- // Apparently, this can fail. Should we really be aborting?
- ret = pthread_mutexattr_settype(&mutexattr, kind);
- assert(ret == 0);
-
- ret = pthread_mutex_init(mutex, &mutexattr);
- assert(ret == 0);
-
- ret = pthread_mutexattr_destroy(&mutexattr);
- assert(ret == 0);
- THRIFT_UNUSED_VARIABLE(ret);
+ THROW_SRE_ONFAIL(pthread_mutexattr_init(&mutexattr));
+ THROW_SRE_ONFAIL(pthread_mutexattr_settype(&mutexattr, kind));
+ THROW_SRE_ONFAIL(pthread_mutex_init(mutex, &mutexattr));
+ THROW_SRE_ONFAIL(pthread_mutexattr_destroy(&mutexattr));
}
#endif
@@ -258,6 +262,12 @@
}
#endif
+#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
+void Mutex::ERRORCHECK_INITIALIZER(void* arg) {
+ init_with_kind((pthread_mutex_t*)arg, PTHREAD_MUTEX_ERRORCHECK);
+}
+#endif
+
#ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
void Mutex::RECURSIVE_INITIALIZER(void* arg) {
init_with_kind((pthread_mutex_t*)arg, PTHREAD_MUTEX_RECURSIVE_NP);
@@ -275,40 +285,36 @@
#ifdef THRIFT_PTHREAD_MUTEX_CONTENTION_PROFILING
profileTime_ = 0;
#endif
- int ret = pthread_rwlock_init(&rw_lock_, NULL);
- THRIFT_UNUSED_VARIABLE(ret);
- assert(ret == 0);
+ THROW_SRE_ONFAIL(pthread_rwlock_init(&rw_lock_, NULL));
initialized_ = true;
}
~impl() {
if (initialized_) {
initialized_ = false;
- int ret = pthread_rwlock_destroy(&rw_lock_);
- THRIFT_UNUSED_VARIABLE(ret);
- assert(ret == 0);
+ ABORT_ONFAIL(pthread_rwlock_destroy(&rw_lock_));
}
}
void acquireRead() const {
PROFILE_MUTEX_START_LOCK();
- pthread_rwlock_rdlock(&rw_lock_);
+ THROW_SRE_ONFAIL(pthread_rwlock_rdlock(&rw_lock_));
PROFILE_MUTEX_NOT_LOCKED(); // not exclusive, so use not-locked path
}
void acquireWrite() const {
PROFILE_MUTEX_START_LOCK();
- pthread_rwlock_wrlock(&rw_lock_);
+ THROW_SRE_ONFAIL(pthread_rwlock_wrlock(&rw_lock_));
PROFILE_MUTEX_LOCKED();
}
- bool attemptRead() const { return !pthread_rwlock_tryrdlock(&rw_lock_); }
+ bool attemptRead() const { THROW_SRE_TRYFAIL(pthread_rwlock_tryrdlock(&rw_lock_)); }
- bool attemptWrite() const { return !pthread_rwlock_trywrlock(&rw_lock_); }
+ bool attemptWrite() const { THROW_SRE_TRYFAIL(pthread_rwlock_trywrlock(&rw_lock_)); }
void release() const {
PROFILE_MUTEX_START_UNLOCK();
- pthread_rwlock_unlock(&rw_lock_);
+ THROW_SRE_ONFAIL(pthread_rwlock_unlock(&rw_lock_));
PROFILE_MUTEX_UNLOCKED();
}