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?
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 
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 
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
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 }
Impl(const boost::function< void()> &main)
Definition: thread.cc:66
static void done(Impl *impl)
Definition: thread.cc:76
int main(int argc, char *argv[])
Definition: agent/main.cc:16
This holds a lock on a Mutex.
Definition: sync.h:72
boost::function< void()> main_
Definition: thread.cc:108
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
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
There's an uncaught exception in a thread.
Definition: thread.h:43
HAImplPtr impl
Definition: ha_callouts.cc:23
Defines the logger used by the top-level component of kea-dhcp-ddns.
Mutex with very simple interface.
Definition: sync.h:39
A generic exception that is thrown if a function is called in a prohibited way.
static void * run(void *impl_raw)
Definition: thread.cc:89