Kea  1.5.0
sync.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <util/threads/sync.h>
10 
11 #include <exceptions/exceptions.h>
12 
13 #include <cstring>
14 #include <memory>
15 #include <cerrno>
16 #include <cassert>
17 
18 #include <pthread.h>
19 
20 using std::unique_ptr;
21 
22 namespace isc {
23 namespace util {
24 namespace thread {
25 
26 class Mutex::Impl {
27 public:
28  Impl()
29 #ifdef ENABLE_DEBUG
30  : locked_count(0)
31 #endif // ENABLE_DEBUG
32  {}
33 
34  pthread_mutex_t mutex;
35 #ifdef ENABLE_DEBUG
36  size_t locked_count;
37 #endif // ENABLE_DEBUG
38 };
39 
40 namespace {
41 
42 struct Deinitializer {
43  Deinitializer(pthread_mutexattr_t& attributes):
44  attributes_(attributes)
45  {}
46  ~Deinitializer() {
47  const int result = pthread_mutexattr_destroy(&attributes_);
48  // This should never happen. According to the man page,
49  // if there's error, it's our fault.
50  assert(result == 0);
51  }
52  pthread_mutexattr_t& attributes_;
53 };
54 
55 }
56 
58  impl_(NULL)
59 {
60  pthread_mutexattr_t attributes;
61  int result = pthread_mutexattr_init(&attributes);
62  switch (result) {
63  case 0: // All 0K
64  break;
65  case ENOMEM:
66  throw std::bad_alloc();
67  default:
68  isc_throw(isc::InvalidOperation, std::strerror(result));
69  }
70  Deinitializer deinitializer(attributes);
71 
72  // If debug mode is enabled in compilation, use the slower
73  // error-checking mutexes that detect deadlocks. Otherwise, use fast
74  // mutexes which don't. See the pthread_mutexattr_settype() POSIX
75  // documentation which describes these type attributes.
76 #ifdef ENABLE_DEBUG
77  result = pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_ERRORCHECK);
78 #else
79  result = pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_NORMAL);
80 #endif // ENABLE_DEBUG
81  if (result != 0) {
82  isc_throw(isc::InvalidOperation, std::strerror(result));
83  }
84 
85  unique_ptr<Impl> impl(new Impl);
86  result = pthread_mutex_init(&impl->mutex, &attributes);
87  switch (result) {
88  case 0: // All 0K
89  impl_ = impl.release();
90  break;
91  case ENOMEM:
92  case EAGAIN:
93  throw std::bad_alloc();
94  default:
95  isc_throw(isc::InvalidOperation, std::strerror(result));
96  }
97 }
98 
100  if (impl_ != NULL) {
101  const int result = pthread_mutex_destroy(&impl_->mutex);
102 
103 #ifdef ENABLE_DEBUG
104  const bool locked = impl_->locked_count != 0;
105 #endif // ENABLE_DEBUG
106 
107  delete impl_;
108  // We don't want to throw from the destructor. Also, if this ever
109  // fails, something is really screwed up a lot.
110  assert(result == 0);
111 
112 #ifdef ENABLE_DEBUG
113  // We should not try to destroy a locked mutex, bad threaded monsters
114  // could get loose if we ever do and it is also forbidden by pthreads.
115 
116  // This should not be possible to happen, since the
117  // pthread_mutex_destroy should check for it already. But it seems
118  // there are systems that don't check it.
119  assert(!locked);
120 #endif // ENABLE_DEBUG
121  }
122 }
123 
124 #ifdef ENABLE_DEBUG
125 
126 void
127 Mutex::postLockAction() {
128  assert(impl_->locked_count == 0);
129  ++impl_->locked_count;
130 }
131 
132 void
133 Mutex::preUnlockAction(bool throw_ok) {
134  if (impl_->locked_count == 0) {
135  if (throw_ok) {
137  "Unlock attempt for unlocked mutex");
138  } else {
139  assert(false);
140  }
141  }
142  --impl_->locked_count;
143 }
144 
145 bool
146 Mutex::locked() const {
147  return (impl_->locked_count != 0);
148 }
149 
150 #endif // ENABLE_DEBUG
151 
152 void
153 Mutex::lock() {
154  assert(impl_ != NULL);
155  const int result = pthread_mutex_lock(&impl_->mutex);
156  if (result != 0) {
157  isc_throw(isc::InvalidOperation, std::strerror(result));
158  }
159 #ifdef ENABLE_DEBUG
160  postLockAction(); // Only in debug mode
161 #endif // ENABLE_DEBUG
162 }
163 
164 bool
165 Mutex::tryLock() {
166  assert(impl_ != NULL);
167  const int result = pthread_mutex_trylock(&impl_->mutex);
168  // In the case of pthread_mutex_trylock(), if it is called on a
169  // locked mutex from the same thread, some platforms (such as fedora
170  // and debian) return EBUSY whereas others (such as centos 5) return
171  // EDEADLK. We return false and don't pass the lock attempt in both
172  // cases.
173  if (result == EBUSY || result == EDEADLK) {
174  return (false);
175  } else if (result != 0) {
176  isc_throw(isc::InvalidOperation, std::strerror(result));
177  }
178 #ifdef ENABLE_DEBUG
179  postLockAction(); // Only in debug mode
180 #endif // ENABLE_DEBUG
181  return (true);
182 }
183 
184 void
185 Mutex::unlock() {
186  assert(impl_ != NULL);
187 #ifdef ENABLE_DEBUG
188  preUnlockAction(false); // Only in debug mode. Ensure no throw.
189 #endif // ENABLE_DEBUG
190  const int result = pthread_mutex_unlock(&impl_->mutex);
191  assert(result == 0); // This should never be possible
192 }
193 
195 public:
196  Impl() {
197  const int result = pthread_cond_init(&cond_, NULL);
198  if (result != 0) {
199  isc_throw(isc::Unexpected, "pthread_cond_init failed: "
200  << std::strerror(result));
201  }
202  }
203  ~Impl() {
204  const int result = pthread_cond_destroy(&cond_);
205 
206  // This can happen if we try to destroy cond_ while some other thread
207  // is waiting on it. assert() may be too strong for such a case,
208  // but we cannot safely destroy cond_ anyway. In order to avoid
209  // throwing from a destructor we simply let the process die.
210  assert(result == 0);
211  }
212 
213  // For convenience allow the main class to access this directly.
214  pthread_cond_t cond_;
215 };
216 
217 CondVar::CondVar() : impl_(new Impl)
218 {}
219 
221  delete impl_;
222 }
223 
224 void
226 #ifdef ENABLE_DEBUG
227  mutex.preUnlockAction(true); // Only in debug mode
228  const int result = pthread_cond_wait(&impl_->cond_, &mutex.impl_->mutex);
229  mutex.postLockAction(); // Only in debug mode
230 #else
231  const int result = pthread_cond_wait(&impl_->cond_, &mutex.impl_->mutex);
232 #endif
233  // pthread_cond_wait should normally succeed unless mutex is completely
234  // broken.
235  if (result != 0) {
236  isc_throw(isc::BadValue, "pthread_cond_wait failed unexpectedly: " <<
237  std::strerror(result));
238  }
239 }
240 
241 void
243  const int result = pthread_cond_signal(&impl_->cond_);
244 
245  // pthread_cond_signal() can only fail when if cond_ is invalid. It
246  //should be impossible as long as this is a valid CondVar object.
247  assert(result == 0);
248 }
249 
250 }
251 }
252 }
void wait(Mutex &mutex)
Wait on the condition variable.
Definition: sync.cc:225
~Mutex()
Destructor.
Definition: sync.cc:99
pthread_mutex_t mutex
Definition: sync.cc:34
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown when an unexpected error condition occurs.
HAImplPtr impl
Definition: ha_callouts.cc:23
Mutex()
Constructor.
Definition: sync.cc:57
bool locked() const
If the mutex is currently locked.
Defines the logger used by the top-level component of kea-dhcp-ddns.
Mutex with very simple interface.
Definition: sync.h:39
pthread_mutexattr_t & attributes_
Definition: sync.cc:52
void signal()
Unblock a thread waiting for the condition variable.
Definition: sync.cc:242
A generic exception that is thrown if a function is called in a prohibited way.
~CondVar()
Destructor.
Definition: sync.cc:220
CondVar()
Constructor.
Definition: sync.cc:217