Kea
1.5.0
thread.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/thread.h
>
10
#include <
util/threads/sync.h
>
11
12
#include <memory>
13
#include <string>
14
#include <cstring>
15
#include <cerrno>
16
17
#include <pthread.h>
18
#include <signal.h>
19
20
#include <boost/noncopyable.hpp>
21
#include <boost/scoped_ptr.hpp>
22
23
using
std::string;
24
using
std::exception;
25
using
std::unique_ptr;
26
using
boost::scoped_ptr;
27
28
namespace
isc
{
29
namespace
util {
30
namespace
thread {
31
32
namespace
{
33
34
// Signal blocker class.
35
class
Blocker : boost::noncopyable {
36
public
:
37
// Constructor blocks all signals
38
Blocker() {
39
sigset_t new_mask;
40
sigfillset(&new_mask);
41
pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_);
42
}
43
44
// Destructor restores the previous signal mask
45
~Blocker() {
46
pthread_sigmask(SIG_SETMASK, &old_mask_, 0);
47
}
48
49
private
:
50
// The previous signal mask
51
sigset_t old_mask_;
52
};
53
54
}
55
56
// The implementation of the Thread class.
57
//
58
// This internal state is not deleted until the thread terminates and is either
59
// waited for or detached. We could do this with shared_ptr (or, shared_ptr and
60
// weak_ptr), but we plan on compiling boost without thread support, so it
61
// might not be safe. Therefore we use an explicit mutex. It is being locked
62
// only 2-3 times in the lifetime of the thread, which should be negligible
63
// overhead anyway.
64
class
Thread::Impl
{
65
public
:
66
Impl
(
const
boost::function<
void
()>&
main
) :
67
// Two things to happen before destruction - thread needs to terminate
68
// and the creating thread needs to release it.
69
waiting_
(2),
70
main_
(
main
),
71
exception_
(false),
72
tid_
(0)
73
{}
74
// Another of the waiting events is done. If there are no more, delete
75
// impl.
76
static
void
done
(
Impl
*
impl
) {
77
bool
should_delete(
false
);
78
{
// We need to make sure the mutex is unlocked before it is deleted
79
Mutex::Locker
locker(
impl
->mutex_);
80
if
(--
impl
->waiting_ == 0) {
81
should_delete =
true
;
82
}
83
}
84
if
(should_delete) {
85
delete
impl
;
86
}
87
}
88
// Run the thread. The type of parameter is because the pthread API.
89
static
void
*
run
(
void
* impl_raw) {
90
Impl
*
impl
=
static_cast<
Impl
*
>
(impl_raw);
91
try
{
92
impl
->main_();
93
}
catch
(
const
exception& e) {
94
impl
->exception_ =
true
;
95
impl
->exception_text_ = e.what();
96
}
catch
(...) {
97
impl
->exception_ =
true
;
98
impl
->exception_text_ =
"Unknown exception"
;
99
}
100
done
(
impl
);
101
return
(NULL);
102
}
103
// How many events are waiting? One is for the thread to finish, one
104
// for the destructor of Thread or wait. Once both happen, this is
105
// no longer needed.
106
size_t
waiting_
;
107
// The main function of the thread.
108
boost::function<void ()>
main_
;
109
// Was there an exception?
110
bool
exception_
;
111
string
exception_text_
;
112
// The mutex protects the waiting_ member, which ensures there are
113
// no race conditions and collisions when terminating. The other members
114
// should be safe, because:
115
// * tid_ is read only.
116
// * exception_ and exception_text_ is accessed outside of the thread
117
// only after join, by that time the thread must have terminated.
118
// * main_ is used in a read-only way here. If there are any shared
119
// resources used inside, it is up to the main_ itself to take care.
120
Mutex
mutex_
;
121
// Which thread are we talking about anyway?
122
pthread_t
tid_
;
123
};
124
125
Thread::Thread
(
const
boost::function<
void
()>&
main
) :
126
impl_(NULL)
127
{
128
unique_ptr<Impl>
impl
(
new
Impl
(
main
));
129
Blocker blocker;
130
const
int
result = pthread_create(&
impl
->tid_, NULL, &
Impl::run
,
131
impl
.get());
132
// Any error here?
133
switch
(result) {
134
case
0:
// All 0K
135
impl_ =
impl
.release();
136
break
;
137
case
EAGAIN:
138
throw
std::bad_alloc();
139
default
:
// Other errors. They should not happen.
140
isc_throw
(
isc::InvalidOperation
, std::strerror(result));
141
}
142
}
143
144
Thread::~Thread
() {
145
if
(impl_ != NULL) {
146
// In case we didn't call wait yet
147
const
int
result = pthread_detach(impl_->
tid_
);
148
Impl::done
(impl_);
149
impl_ = NULL;
150
// If the detach ever fails, something is screwed rather badly.
151
assert(result == 0);
152
}
153
}
154
155
void
156
Thread::wait
() {
157
if
(impl_ == NULL) {
158
isc_throw
(
isc::InvalidOperation
,
159
"Wait called and no thread to wait for"
);
160
}
161
162
const
int
result = pthread_join(impl_->
tid_
, NULL);
163
if
(result != 0) {
164
isc_throw
(
isc::InvalidOperation
, std::strerror(result));
165
}
166
167
// Was there an exception in the thread?
168
scoped_ptr<UncaughtException> ex;
169
// Something here could in theory throw. But we already terminated the thread, so
170
// we need to make sure we are in consistent state even in such situation (like
171
// releasing the mutex and impl_).
172
try
{
173
if
(impl_->
exception_
) {
174
ex.reset(
new
UncaughtException
(__FILE__, __LINE__,
175
impl_->
exception_text_
.c_str()));
176
}
177
}
catch
(...) {
178
Impl::done
(impl_);
179
impl_ = NULL;
180
// We have eaten the UncaughtException by now, but there's another
181
// exception instead, so we have at least something.
182
throw
;
183
}
184
185
Impl::done
(impl_);
186
impl_ = NULL;
187
if
(ex.get() != NULL) {
188
throw
UncaughtException
(*ex);
189
}
190
}
191
192
}
193
}
194
}
isc::util::thread::Thread::Impl::waiting_
size_t waiting_
Definition:
thread.cc:106
isc::util::thread::Thread::Impl::exception_
bool exception_
Definition:
thread.cc:110
isc::util::thread::Thread::Impl::main_
boost::function< void()> main_
Definition:
thread.cc:108
isc::util::thread::Mutex::Locker
This holds a lock on a Mutex.
Definition:
sync.h:72
sync.h
isc::util::thread::Thread::Impl::run
static void * run(void *impl_raw)
Definition:
thread.cc:89
isc::util::thread::Mutex
Mutex with very simple interface.
Definition:
sync.h:39
isc::util::thread::Thread::wait
void wait()
Wait for the thread to terminate.
Definition:
thread.cc:156
isc::util::thread::Thread::Impl::exception_text_
string exception_text_
Definition:
thread.cc:111
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition:
agent_parser.cc:144
isc::util::thread::Thread::Impl::mutex_
Mutex mutex_
Definition:
thread.cc:120
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition:
exceptions/exceptions.h:192
isc::InvalidOperation
A generic exception that is thrown if a function is called in a prohibited way.
Definition:
exceptions/exceptions.h:143
isc::ha::impl
HAImplPtr impl
Definition:
ha_callouts.cc:23
isc::util::thread::Thread::Impl
Definition:
thread.cc:64
main
int main(int argc, char *argv[])
Definition:
agent/main.cc:16
isc::util::thread::Thread::Impl::Impl
Impl(const boost::function< void()> &main)
Definition:
thread.cc:66
isc::util::thread::Thread::UncaughtException
There's an uncaught exception in a thread.
Definition:
thread.h:43
isc::util::thread::Thread::~Thread
~Thread()
Destructor.
Definition:
thread.cc:144
isc::util::thread::Thread::Thread
Thread(const boost::function< void()> &main)
Create and start a thread.
Definition:
thread.cc:125
isc::util::thread::Thread::Impl::done
static void done(Impl *impl)
Definition:
thread.cc:76
thread.h
isc::util::thread::Thread::Impl::tid_
pthread_t tid_
Definition:
thread.cc:122
src
lib
util
threads
thread.cc
Generated on Fri Jul 10 2020 09:12:51 for Kea by
1.8.18