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"
46#include "blocxx/Timeout.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
63namespace BLOCXX_NAMESPACE
64{
65
69
70namespace
71{
72const GlobalString COMPONENT_NAME = BLOCXX_GLOBAL_STRING_INIT("blocxx");
73
75// this is what's really passed to threadRunner
76struct ThreadParam
77{
78 ThreadParam(Thread* t, const ThreadDoneCallbackRef& c, const ThreadBarrier& b)
79 : thread(t)
80 , cb(c)
82 {}
83 Thread* thread;
85 ThreadBarrier thread_barrier;
86};
87
89static Thread_t
90zeroThread()
91{
92 Thread_t zthr;
93 ::memset(&zthr, 0, sizeof(zthr));
94 return zthr;
95}
96
97static Thread_t NULLTHREAD = zeroThread();
99static inline bool
100sameId(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
139void
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.
165Int32
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
182Int32
183Thread::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
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
288void
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
308void
310{
311 doShutdown();
312}
314bool
316{
317 doShutdown();
318 return timedWait(timeout);
319}
321void
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}
348bool
349Thread::definitiveCancel(UInt32 waitForCooperativeSecs)
350{
351 return definitiveCancel(Timeout::relative(waitForCooperativeSecs));
352}
354bool
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
403void
405{
406 this->cancel_internal(false);
407}
408
410void
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 {
424 NonRecursiveMutexLock l(is_locked ? mtx : m_stateGuard);
425 m_cancelled = true;
426 m_isRunning = false;
428 }
429}
431void
436
438void
443void
448void
452
454bool
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
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(),...
Definition Assertion.hpp:57
#define BLOCXX_DEFINE_EXCEPTION_WITH_ID(NAME)
Define a new exception class named <NAME>Exception that derives from Exception.
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
#define BLOCXX_THROW_ERRNO_MSG1(exType, msg, errnum)
Throw an exception using FILE, LINE, errnum and strerror(errnum)
#define BLOCXX_GLOBAL_STRING_INIT(str)
#define BLOCXX_LOG_DEBUG3(logger, message)
Log message to logger with the Debug3 level.
Definition Logger.hpp:345
#define BLOCXX_LOG_ERROR(logger, message)
Log message to logger with the Error level.
Definition Logger.hpp:433
Thread * thread
Definition Thread.cpp:83
ThreadBarrier thread_barrier
Definition Thread.cpp:85
ThreadDoneCallbackRef cb
Definition Thread.cpp:84
#define BLOCXX_THREAD_FLG_JOINABLE
void notifyAll()
Signal all threads that are currently waiting on the Condition object.
bool timedWait(NonRecursiveMutexLock &lock, const Timeout &timeout)
Atomically unlock a given mutex and wait for a given amount of time for this Condition object to get ...
This class is the base of all exceptions thrown by BloCxx code.
Definition Exception.hpp:66
virtual const char * what() const
Returns getMessage()
virtual const char * type() const
Returns a string representing the concrete type.
This class can be used to store a global variable that is lazily initialized in a thread safe manner.
Logging interface.
Definition Logger.hpp:87
Note that descriptions of what exceptions may be thrown assumes that object is used correctly,...
Note that descriptions of what exceptions may be thrown assumes that object is used correctly,...
The ThreadBarrier class is used to synchronize threads.
Descriptions of exceptions thrown assume that the object is used correctly, i.e., method precondition...
Definition Thread.hpp:67
static void testCancel()
Test if this thread has been cancelled.
Definition Thread.cpp:432
Thread()
Create a new Thread object.
Definition Thread.cpp:108
virtual void start(const ThreadDoneCallbackRef &cb=ThreadDoneCallbackRef(0))
Start this Thread's execution.
Definition Thread.cpp:140
Int32 join()
Join with this Thread's execution.
Definition Thread.cpp:166
virtual ~Thread()
Destroy this Thread object.
Definition Thread.cpp:118
bool timedWait(const Timeout &timeout)
Wait for the thread to finish.
Definition Thread.cpp:455
void cancel()
Definitively cancel this Threads execution.
Definition Thread.cpp:404
NonRecursiveMutex m_stateGuard
Definition Thread.hpp:356
void doneRunning(const ThreadDoneCallbackRef &cb)
Definition Thread.cpp:289
virtual void doDefinitiveCancel()
See the documentation for doCooperativeCancel().
Definition Thread.cpp:449
void shutdown()
Call the thread's doShutdown(), which may be used by the thread to safely stop.
Definition Thread.cpp:309
virtual void doCooperativeCancel()
This function is available for subclasses of Thread to override if they wish to be notified when a co...
Definition Thread.cpp:444
virtual void doShutdown()
This function is available for subclasses of Thread to override if they wish to be notified when shut...
Definition Thread.cpp:439
static Int32 threadRunner(void *paramPtr)
Definition Thread.cpp:183
virtual Int32 run()=0
The method that will be run when the start method is called.
void cancel_internal(bool is_locked)
Definition Thread.cpp:411
bool definitiveCancel(const Timeout &timeout=Timeout::relative(60))
Attempt to cooperatively and then definitively cancel this Thread's execution.
Definition Thread.cpp:355
void cooperativeCancel()
Attempt to cooperatively cancel this Threads execution.
Definition Thread.cpp:322
A timeout can be absolute, which means that it will happen at the specified DateTime.
Definition Timeout.hpp:56
static Timeout relative(float seconds)
Definition Timeout.cpp:58
A TimeoutTimer is used by an algorithm to determine when a timeout has expired.
Timeout asAbsoluteTimeout() const
Converts the timer to an absolute timeout.
BLOCXX_COMMON_API void testCancel()
Test if this thread has been cancelled.
BLOCXX_COMMON_API void destroyThread(Thread_t &handle)
Destroy any resources associated with a thread that was created with the createThread method.
BLOCXX_COMMON_API int joinThread(Thread_t &handle, Int32 &rval)
Join a thread that has been previously set to joinable.
bool sameThreads(const volatile Thread_t &handle1, const volatile Thread_t &handle2)
Check two platform dependant thread types for equality.
BLOCXX_COMMON_API int createThread(Thread_t &handle, ThreadFunction func, void *funcParm, UInt32 threadFlags)
Starts a thread running the given function.
BLOCXX_COMMON_API void cancel(Thread_t threadID)
BLOCXX_COMMON_API void exitThread(Thread_t &handle, Int32 rval)
Exit thread method.
BLOCXX_COMMON_API void saveThreadInTLS(void *pTheThread)
BLOCXX_COMMON_API void sendSignalToThread(Thread_t threadID, int signo)
Taken from RFC 1321.
IntrusiveReference< ThreadDoneCallback > ThreadDoneCallbackRef
In the event a thread has been cancelled, a ThreadCancelledException will be thrown.