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
12
13#include <cstring>
14#include <memory>
15#include <cerrno>
16#include <cassert>
17
18#include <pthread.h>
19
20using std::unique_ptr;
21
22namespace isc {
23namespace util {
24namespace thread {
25
27public:
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
40namespace {
41
42struct 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
126void
127Mutex::postLockAction() {
128 assert(impl_->locked_count == 0);
129 ++impl_->locked_count;
130}
131
132void
133Mutex::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
145bool
146Mutex::locked() const {
147 return (impl_->locked_count != 0);
148}
149
150#endif // ENABLE_DEBUG
151
152void
153Mutex::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
164bool
165Mutex::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
184void
185Mutex::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
195public:
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 }
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
217CondVar::CondVar() : impl_(new Impl)
218{}
219
221 delete impl_;
222}
223
224void
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
241void
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}
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 if a function is called in a prohibited way.
A generic exception that is thrown when an unexpected error condition occurs.
void wait(Mutex &mutex)
Wait on the condition variable.
Definition: sync.cc:225
~CondVar()
Destructor.
Definition: sync.cc:220
CondVar()
Constructor.
Definition: sync.cc:217
void signal()
Unblock a thread waiting for the condition variable.
Definition: sync.cc:242
pthread_mutex_t mutex
Definition: sync.cc:34
Mutex with very simple interface.
Definition: sync.h:39
~Mutex()
Destructor.
Definition: sync.cc:99
bool locked() const
If the mutex is currently locked.
Mutex()
Constructor.
Definition: sync.cc:57
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Defines the logger used by the top-level component of kea-dhcp-ddns.
pthread_mutexattr_t & attributes_
Definition: sync.cc:52