blocxx
Thread.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2 * Copyright (C) 2005, Vintela, Inc. All rights reserved.
3 * Copyright (C) 2006, Novell, Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of
14 * Vintela, Inc.,
15 * nor Novell, Inc.,
16 * nor the names of its contributors or employees may be used to
17 * endorse or promote products derived from this software without
18 * specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *******************************************************************************/
32 
33 
39 #include "blocxx/BLOCXX_config.h"
40 #include "blocxx/Thread.hpp"
41 #include "blocxx/Assertion.hpp"
42 #include "blocxx/Format.hpp"
43 #include "blocxx/ThreadBarrier.hpp"
45 #include "blocxx/ExceptionIds.hpp"
46 #include "blocxx/Timeout.hpp"
47 #include "blocxx/TimeoutTimer.hpp"
48 #include "blocxx/DateTime.hpp"
49 #include "blocxx/Logger.hpp"
50 
51 #include <cstring>
52 #include <cstdio>
53 #include <cerrno>
54 #include <iostream> // for cerr
55 #include <csignal>
56 #include <cassert>
57 
58 #ifdef BLOCXX_HAVE_OPENSSL
59 #include <openssl/err.h>
60 #endif
61 
62 
63 namespace BLOCXX_NAMESPACE
64 {
65 
68 BLOCXX_DEFINE_EXCEPTION_WITH_ID(CancellationDenied);
69 
70 namespace
71 {
73 
75 // this is what's really passed to threadRunner
76 struct ThreadParam
77 {
78  ThreadParam(Thread* t, const ThreadDoneCallbackRef& c, const ThreadBarrier& b)
79  : thread(t)
80  , cb(c)
81  , thread_barrier(b)
82  {}
83  Thread* thread;
85  ThreadBarrier thread_barrier;
86 };
87 
89 static Thread_t
90 zeroThread()
91 {
92  Thread_t zthr;
93  ::memset(&zthr, 0, sizeof(zthr));
94  return zthr;
95 }
96 
97 static Thread_t NULLTHREAD = zeroThread();
99 static inline bool
100 sameId(const Thread_t& t1, const Thread_t& t2)
101 {
102  return ThreadImpl::sameThreads(t1, t2);
103 }
104 } // end unnamed namespace
105 
107 // Constructor
109  : m_id(NULLTHREAD)
110  , m_isRunning(false)
111  , m_joined(false)
112  , m_cancelRequested(0)
113  , m_cancelled(false)
114 {
115 }
117 // Destructor
119 {
120  try
121  {
122  if (!sameId(m_id, NULLTHREAD))
123  {
124  if (!m_joined)
125  {
126  join();
127  }
128  assert(m_isRunning == false);
130  }
131  }
132  catch (...)
133  {
134  // don't let exceptions escape
135  }
136 }
138 // Start the thread
139 void
141 {
142  if (isRunning())
143  {
145  "Thread::start - thread is already running");
146  }
147  if (!sameId(m_id, NULLTHREAD))
148  {
150  "Thread::start - cannot start previously run thread");
151  }
152  UInt32 flgs = BLOCXX_THREAD_FLG_JOINABLE;
154  // p will be delted by threadRunner
155  ThreadParam* p = new ThreadParam(this, cb, thread_barrier);
156  int result = ThreadImpl::createThread(m_id, threadRunner, p, flgs);
157  if (result != 0)
158  {
159  BLOCXX_THROW_ERRNO_MSG1(ThreadException, "ThreadImpl::createThread() failed", result);
160  }
161  thread_barrier.wait();
162 }
164 // Wait for this object's thread execution (if any) to complete.
165 Int32
167 {
168  BLOCXX_ASSERT(!sameId(m_id, NULLTHREAD));
169  Int32 rval;
170  if (ThreadImpl::joinThread(m_id, rval) != 0)
171  {
173  Format("Thread::join - ThreadImpl::joinThread: %1(%2)",
174  errno, strerror(errno)).c_str());
175  }
176  m_joined = true;
177  return rval;
178 }
180 // STATIC
181 // Method used for starting threads
182 Int32
183 Thread::threadRunner(void* paramPtr)
184 {
185  Thread_t theThreadID;
186  Int32 rval = -1;
187  try
188  {
189  // scope is important so destructors will run before the thread is clobbered by exitThread
190  BLOCXX_ASSERT(paramPtr != NULL);
191  ThreadParam* pParam = static_cast<ThreadParam*>(paramPtr);
192  Thread* pTheThread = pParam->thread;
193  ThreadImpl::saveThreadInTLS(pTheThread);
194  theThreadID = pTheThread->m_id;
195  ThreadDoneCallbackRef cb = pParam->cb;
196  ThreadBarrier thread_barrier = pParam->thread_barrier;
197  delete pParam;
198  pTheThread->m_isRunning = true;
199  thread_barrier.wait();
200 
201  try
202  {
203  rval = pTheThread->run();
204  }
205  // make sure we get all exceptions so the thread is cleaned up properly
206  catch (ThreadCancelledException&)
207  {
208  }
209  catch (Exception& ex)
210  {
211 #ifdef BLOCXX_DEBUG
212  std::clog << "!!! Exception: " << ex.type() << " caught in Thread class\n";
213  std::clog << ex << std::endl;
214 #endif
215  Logger logger(COMPONENT_NAME);
216  BLOCXX_LOG_ERROR(logger, Format("!!! Exception caught in Thread class: %1", ex));
217  pTheThread->doneRunning(cb);
218  // we need to re-throw here, otherwise we'll segfault
219  // if pthread_cancel() does forced stack unwinding.
220  throw;
221  }
222  catch (std::exception& ex)
223  {
224 #ifdef BLOCXX_DEBUG
225  std::clog << "!!! std::exception: " << ex.what() << " caught in Thread class" << std::endl;
226 #endif
227  Logger logger(COMPONENT_NAME);
228  BLOCXX_LOG_ERROR(logger, Format("!!! std::exception caught in Thread class: %1", ex.what()));
229  pTheThread->doneRunning(cb);
230  // we need to re-throw here, otherwise we'll segfault
231  // if pthread_cancel() does forced stack unwinding.
232  throw;
233  }
234  catch (...)
235  {
236 #ifdef BLOCXX_DEBUG
237  std::clog << "!!! Unknown Exception caught in Thread class" << std::endl;
238 #endif
239  Logger logger(COMPONENT_NAME);
240  BLOCXX_LOG_ERROR(logger, "!!! Unknown Exception caught in Thread class.");
241 
242  pTheThread->doneRunning(cb);
243  // we need to re-throw here, otherwise we'll segfault
244  // if pthread_cancel() does forced stack unwinding.
245  throw;
246  }
247 
248  pTheThread->doneRunning(cb);
249 
250  }
251  catch (Exception& ex)
252  {
253 #ifdef BLOCXX_DEBUG
254  std::clog << "!!! Exception: " << ex.type() << " caught in Thread class\n";
255  std::clog << ex << std::endl;
256 #endif
257  Logger logger(COMPONENT_NAME);
258  BLOCXX_LOG_ERROR(logger, Format("!!! Exception caught in Thread class: %1", ex));
259  // end the thread. exitThread never returns.
260  ThreadImpl::exitThread(theThreadID, rval);
261  }
262  catch (std::exception& ex)
263  {
264 #ifdef BLOCXX_DEBUG
265  std::clog << "!!! std::exception: " << ex.what() << " caught in Thread class" << std::endl;
266 #endif
267  Logger logger(COMPONENT_NAME);
268  BLOCXX_LOG_ERROR(logger, Format("!!! std::exception caught in Thread class: %1", ex.what()));
269  // end the thread. exitThread never returns.
270  ThreadImpl::exitThread(theThreadID, rval);
271  }
272  catch (...)
273  {
274 #ifdef BLOCXX_DEBUG
275  std::clog << "!!! Unknown Exception caught in Thread class" << std::endl;
276 #endif
277  Logger logger(COMPONENT_NAME);
278  BLOCXX_LOG_ERROR(logger, "!!! Unknown Exception caught in Thread class.");
279  // end the thread. exitThread never returns.
280  ThreadImpl::exitThread(theThreadID, rval);
281  }
282  // end the thread. exitThread never returns.
283  ThreadImpl::exitThread(theThreadID, rval);
284  return rval;
285 }
286 
288 void
290 {
291  {
293  m_isRunning = false;
295  }
296 
297  if (cb)
298  {
299  cb->notifyThreadDone(this);
300  }
301 #ifdef BLOCXX_HAVE_OPENSSL
302  // this is necessary to free memory associated with the OpenSSL error queue for this thread.
303  ERR_remove_state(0);
304 #endif
305 }
306 
308 void
310 {
311  doShutdown();
312 }
314 bool
315 Thread::shutdown(const Timeout& timeout)
316 {
317  doShutdown();
318  return timedWait(timeout);
319 }
321 void
323 {
324  if (!isRunning())
325  {
326  return;
327  }
328 
329  // give the thread a chance to clean up a bit or abort the cancellation.
332 
333 #if !defined(BLOCXX_WIN32)
334  // send a SIGUSR1 to the thread to break it out of any blocking syscall.
335  // SIGUSR1 is ignored. It's set to SIG_IGN in ThreadImpl.cpp
336  // If the thread has already exited, an ThreadException will be thrown
337  // we just want to ignore that.
338  try
339  {
341  }
342  catch (ThreadException&)
343  {
344  }
345 #endif
346 }
348 bool
349 Thread::definitiveCancel(UInt32 waitForCooperativeSecs)
350 {
351  return definitiveCancel(Timeout::relative(waitForCooperativeSecs));
352 }
354 bool
356 {
357  if (!isRunning())
358  {
359  return true;
360  }
361 
362  // give the thread a chance to clean up a bit or abort the cancellation.
365 
366 #if !defined(BLOCXX_WIN32)
367  // send a SIGUSR1 to the thread to break it out of any blocking syscall.
368  // SIGUSR1 is ignored. It's set to SIG_IGN in ThreadImpl.cpp
369  // If the thread has already exited, an ThreadException will be thrown
370  // we just want to ignore that.
371  try
372  {
374  }
375  catch (ThreadException&)
376  {
377  }
378 #endif
379 
380  Logger logger(COMPONENT_NAME);
381  TimeoutTimer timer(timeout);
383  while (!m_cancelled && isRunning())
384  {
385  BLOCXX_LOG_DEBUG3(logger, "Thread::definitiveCancel waiting for thread to exit.");
386  if (!m_stateCond.timedWait(l, timer.asAbsoluteTimeout()))
387  {
388  // give the thread a chance to clean up a bit or abort the cancellation.
390  // thread didn't (or won't) exit by itself, we'll have to really kill it.
391  if (!m_cancelled && isRunning())
392  {
393  BLOCXX_LOG_ERROR(logger, "Thread::definitiveCancel cancelling thread because it did not exit!");
394  this->cancel_internal(true);
395  }
396  return false;
397  }
398  }
399  return true;
400 }
401 
403 void
405 {
406  this->cancel_internal(false);
407 }
408 
410 void
411 Thread::cancel_internal(bool is_locked)
412 {
413  // Ignore errors from ThreadImpl (usually caused by the fact that the thread
414  // has already exited)
415  try
416  {
418  }
419  catch (ThreadException&)
420  {
421  }
422  {
423  NonRecursiveMutex mtx;
424  NonRecursiveMutexLock l(is_locked ? mtx : m_stateGuard);
425  m_cancelled = true;
426  m_isRunning = false;
428  }
429 }
431 void
433 {
435 }
436 
438 void
440 {
441 }
443 void
445 {
446 }
448 void
450 {
451 }
452 
454 bool
455 Thread::timedWait(const Timeout& timeout)
456 {
457  TimeoutTimer tt(timeout);
459  while (m_isRunning == true)
460  {
461  if (!m_stateCond.timedWait(lock, tt.asAbsoluteTimeout()))
462  {
463  return false; // timeout
464  }
465  }
466  return true; // exited
467 }
468 
469 } // end namespace BLOCXX_NAMESPACE
470