sync.cc 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. // Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include "sync.h"
  15. #include <exceptions/exceptions.h>
  16. #include <cstring>
  17. #include <memory>
  18. #include <cerrno>
  19. #include <cassert>
  20. #include <pthread.h>
  21. using std::auto_ptr;
  22. namespace isc {
  23. namespace util {
  24. namespace thread {
  25. class Mutex::Impl {
  26. public:
  27. Impl() :
  28. locked_count(0)
  29. {}
  30. pthread_mutex_t mutex;
  31. // Only in debug mode
  32. size_t locked_count;
  33. };
  34. namespace {
  35. struct Deinitializer {
  36. Deinitializer(pthread_mutexattr_t& attributes):
  37. attributes_(attributes)
  38. {}
  39. ~Deinitializer() {
  40. const int result = pthread_mutexattr_destroy(&attributes_);
  41. // This should never happen. According to the man page,
  42. // if there's error, it's our fault.
  43. assert(result == 0);
  44. }
  45. pthread_mutexattr_t& attributes_;
  46. };
  47. }
  48. Mutex::Mutex() :
  49. impl_(NULL)
  50. {
  51. pthread_mutexattr_t attributes;
  52. int result = pthread_mutexattr_init(&attributes);
  53. switch (result) {
  54. case 0: // All 0K
  55. break;
  56. case ENOMEM:
  57. throw std::bad_alloc();
  58. default:
  59. isc_throw(isc::InvalidOperation, std::strerror(result));
  60. }
  61. Deinitializer deinitializer(attributes);
  62. // TODO: Distinguish if debug mode is enabled in compilation.
  63. // If so, it should be PTHREAD_MUTEX_NORMAL or NULL
  64. result = pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_ERRORCHECK);
  65. if (result != 0) {
  66. isc_throw(isc::InvalidOperation, std::strerror(result));
  67. }
  68. auto_ptr<Impl> impl(new Impl);
  69. result = pthread_mutex_init(&impl->mutex, &attributes);
  70. switch (result) {
  71. case 0: // All 0K
  72. impl_ = impl.release();
  73. break;
  74. case ENOMEM:
  75. case EAGAIN:
  76. throw std::bad_alloc();
  77. default:
  78. isc_throw(isc::InvalidOperation, std::strerror(result));
  79. }
  80. }
  81. Mutex::~Mutex() {
  82. if (impl_ != NULL) {
  83. const int result = pthread_mutex_destroy(&impl_->mutex);
  84. const bool locked = impl_->locked_count != 0;
  85. delete impl_;
  86. // We don't want to throw from the destructor. Also, if this ever
  87. // fails, something is really screwed up a lot.
  88. assert(result == 0);
  89. // We should not try to destroy a locked mutex, bad threaded monsters
  90. // could get loose if we ever do and it is also forbidden by pthreads.
  91. // This should not be possible to happen, since the
  92. // pthread_mutex_destroy should check for it already. But it seems
  93. // there are systems that don't check it.
  94. assert(!locked);
  95. }
  96. }
  97. void
  98. Mutex::postLockAction() {
  99. // This assertion would fail only in non-debugging mode, in which case
  100. // this method wouldn't be called either, so we simply assert the
  101. // condition.
  102. assert(impl_->locked_count == 0);
  103. ++impl_->locked_count;
  104. }
  105. void
  106. Mutex::lock() {
  107. assert(impl_ != NULL);
  108. const int result = pthread_mutex_lock(&impl_->mutex);
  109. if (result != 0) {
  110. isc_throw(isc::InvalidOperation, std::strerror(result));
  111. }
  112. postLockAction(); // Only in debug mode
  113. }
  114. bool
  115. Mutex::tryLock() {
  116. assert(impl_ != NULL);
  117. const int result = pthread_mutex_trylock(&impl_->mutex);
  118. // In the case of pthread_mutex_trylock(), if it is called on a
  119. // locked mutex from the same thread, some platforms (such as fedora
  120. // and debian) return EBUSY whereas others (such as centos 5) return
  121. // EDEADLK. We return false and don't pass the lock attempt in both
  122. // cases.
  123. if (result == EBUSY || result == EDEADLK) {
  124. return (false);
  125. } else if (result != 0) {
  126. isc_throw(isc::InvalidOperation, std::strerror(result));
  127. }
  128. postLockAction(); // Only in debug mode
  129. return (true);
  130. }
  131. void
  132. Mutex::preUnlockAction(bool throw_ok) {
  133. if (impl_->locked_count == 0) {
  134. if (throw_ok) {
  135. isc_throw(isc::InvalidOperation,
  136. "Unlock attempt for unlocked mutex");
  137. } else {
  138. assert(false);
  139. }
  140. }
  141. --impl_->locked_count;
  142. }
  143. void
  144. Mutex::unlock() {
  145. assert(impl_ != NULL);
  146. preUnlockAction(false); // Only in debug mode. Ensure no throw.
  147. const int result = pthread_mutex_unlock(&impl_->mutex);
  148. assert(result == 0); // This should never be possible
  149. }
  150. // TODO: Disable in non-debug build
  151. bool
  152. Mutex::locked() const {
  153. return (impl_->locked_count != 0);
  154. }
  155. class CondVar::Impl {
  156. public:
  157. Impl() {
  158. const int result = pthread_cond_init(&cond_, NULL);
  159. if (result != 0) {
  160. isc_throw(isc::Unexpected, "pthread_cond_init failed: "
  161. << std::strerror(result));
  162. }
  163. }
  164. ~Impl() {
  165. const int result = pthread_cond_destroy(&cond_);
  166. // This can happen if we try to destroy cond_ while some other thread
  167. // is waiting on it. assert() may be too strong for such a case,
  168. // but we cannot safely destroy cond_ anyway. In order to avoid
  169. // throwing from a destructor we simply let the process die.
  170. assert(result == 0);
  171. }
  172. // For convenience allow the main class to access this directly.
  173. pthread_cond_t cond_;
  174. };
  175. CondVar::CondVar() : impl_(new Impl)
  176. {}
  177. CondVar::~CondVar() {
  178. delete impl_;
  179. }
  180. void
  181. CondVar::wait(Mutex& mutex) {
  182. mutex.preUnlockAction(true); // Only in debug mode
  183. const int result = pthread_cond_wait(&impl_->cond_, &mutex.impl_->mutex);
  184. mutex.postLockAction(); // Only in debug mode
  185. // pthread_cond_wait should normally succeed unless mutex is completely
  186. // broken.
  187. if (result != 0) {
  188. isc_throw(isc::BadValue, "pthread_cond_wait failed unexpectedly: " <<
  189. std::strerror(result));
  190. }
  191. }
  192. void
  193. CondVar::signal() {
  194. const int result = pthread_cond_signal(&impl_->cond_);
  195. // pthread_cond_signal() can only fail when if cond_ is invalid. It
  196. //should be impossible as long as this is a valid CondVar object.
  197. assert(result == 0);
  198. }
  199. }
  200. }
  201. }