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
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
23using std::string;
24using std::exception;
25using std::unique_ptr;
26using boost::scoped_ptr;
27
28namespace isc {
29namespace util {
30namespace thread {
31
32namespace {
33
34// Signal blocker class.
35class Blocker : boost::noncopyable {
36public:
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
49private:
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.
65public:
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?
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.
121 // Which thread are we talking about anyway?
122 pthread_t tid_;
123};
124
125Thread::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
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
155void
157 if (impl_ == NULL) {
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}
int main(int argc, char *argv[])
Definition: agent/main.cc:16
A generic exception that is thrown if a function is called in a prohibited way.
This holds a lock on a Mutex.
Definition: sync.h:72
Mutex with very simple interface.
Definition: sync.h:39
static void * run(void *impl_raw)
Definition: thread.cc:89
Impl(const boost::function< void()> &main)
Definition: thread.cc:66
boost::function< void()> main_
Definition: thread.cc:108
static void done(Impl *impl)
Definition: thread.cc:76
There's an uncaught exception in a thread.
Definition: thread.h:43
void wait()
Wait for the thread to terminate.
Definition: thread.cc:156
~Thread()
Destructor.
Definition: thread.cc:144
Thread(const boost::function< void()> &main)
Create and start a thread.
Definition: thread.cc:125
#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.