2 /***************************************************************************
3 * thread_manager.cpp - Thread manager
5 * Created: Thu Nov 3 19:11:31 2006 (on train to Cologne)
6 * Copyright 2006-2009 Tim Niemueller [www.niemueller.de]
8 ****************************************************************************/
10 /* This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version. A runtime exception applies to
14 * this software (see LICENSE.GPL_WRE file mentioned below for details).
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
21 * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
24 #include <mainapp/thread_manager.h>
25 #include <core/threading/thread.h>
26 #include <core/threading/mutex_locker.h>
27 #include <core/threading/wait_condition.h>
28 #include <core/threading/thread_initializer.h>
29 #include <core/threading/thread_finalizer.h>
30 #include <core/exceptions/software.h>
31 #include <core/exceptions/system.h>
33 #include <aspect/blocked_timing.h>
35 using namespace fawkes;
37 /** @class FawkesThreadManager mainapp/thread_manager.h
39 * This class provides a manager for the threads. Threads are memorized by
40 * their wakeup hook. When the thread manager is deleted, all threads are
41 * appropriately cancelled, joined and deleted. Thus the thread manager
42 * can be used for "garbage collection" of threads.
44 * The thread manager allows easy wakeup of threads of a given wakeup hook.
46 * The thread manager needs a thread initializer. Each thread that is added
47 * to the thread manager is initialized with this. The runtime type information
48 * (RTTI) supplied by C++ can be used to initialize threads if appropriate
49 * (if the thread has certain aspects that need special treatment).
51 * @author Tim Niemueller
54 FawkesThreadManager::FawkesThreadManagerAspectCollector::FawkesThreadManagerAspectCollector(FawkesThreadManager *parent_manager)
56 __parent_manager = parent_manager;
61 FawkesThreadManager::FawkesThreadManagerAspectCollector::add(ThreadList &tl)
63 BlockedTimingAspect *timed_thread;
65 for (ThreadList::iterator i = tl.begin(); i != tl.end(); ++i) {
66 if ( (timed_thread = dynamic_cast<BlockedTimingAspect *>(*i)) != NULL ) {
67 throw IllegalArgumentException("ThreadProducerAspect may not add threads with BlockedTimingAspect");
71 __parent_manager->add_maybelocked(tl, /* lock */ false);
76 FawkesThreadManager::FawkesThreadManagerAspectCollector::add(Thread *t)
78 BlockedTimingAspect *timed_thread;
80 if ( (timed_thread = dynamic_cast<BlockedTimingAspect *>(t)) != NULL ) {
81 throw IllegalArgumentException("ThreadProducerAspect may not add threads with BlockedTimingAspect");
84 __parent_manager->add_maybelocked(t, /* lock */ false);
89 FawkesThreadManager::FawkesThreadManagerAspectCollector::remove(ThreadList &tl)
91 BlockedTimingAspect *timed_thread;
93 for (ThreadList::iterator i = tl.begin(); i != tl.end(); ++i) {
94 if ( (timed_thread = dynamic_cast<BlockedTimingAspect *>(*i)) != NULL ) {
95 throw IllegalArgumentException("ThreadProducerAspect may not remove threads with BlockedTimingAspect");
99 __parent_manager->remove_maybelocked(tl, /* lock */ false);
104 FawkesThreadManager::FawkesThreadManagerAspectCollector::remove(Thread *t)
106 BlockedTimingAspect *timed_thread;
108 if ( (timed_thread = dynamic_cast<BlockedTimingAspect *>(t)) != NULL ) {
109 throw IllegalArgumentException("ThreadProducerAspect may not remove threads with BlockedTimingAspect");
112 __parent_manager->remove_maybelocked(t, /* lock */ false);
117 FawkesThreadManager::FawkesThreadManagerAspectCollector::force_remove(fawkes::ThreadList &tl)
119 throw AccessViolationException("ThreadManagerAspect threads may not force removal of threads");
123 FawkesThreadManager::FawkesThreadManagerAspectCollector::force_remove(fawkes::Thread *t)
125 throw AccessViolationException("ThreadManagerAspect threads may not force removal of threads");
131 FawkesThreadManager::FawkesThreadManager()
136 waitcond_timedthreads = new WaitCondition();
137 __interrupt_timed_thread_wait = false;
138 __aspect_collector = new FawkesThreadManagerAspectCollector(this);
143 FawkesThreadManager::~FawkesThreadManager()
145 // stop all threads, we call finalize, and we run through it as long as there are
146 // still running threads, after that, we force the thread's death.
147 for (tit = threads.begin(); tit != threads.end(); ++tit) {
148 (*tit).second.force_stop(finalizer);
150 untimed_threads.force_stop(finalizer);
153 delete waitcond_timedthreads;
154 delete __aspect_collector;
158 /** Set initializer/finalizer.
159 * This method has to be called before any thread is added/removed.
160 * @param initializer thread initializer
161 * @param finalizer thread finalizer
164 FawkesThreadManager::set_inifin(ThreadInitializer *initializer, ThreadFinalizer *finalizer)
166 this->initializer = initializer;
167 this->finalizer = finalizer;
171 /** Remove the given thread from internal structures.
172 * Thread is removed from the internal structures. If the thread has the
173 * BlockedTimingAspect then the hook is added to the changed list.
175 * @param t thread to remove
176 * @param changed list of changed hooks, appropriate hook is added if necessary
179 FawkesThreadManager::internal_remove_thread(Thread *t)
181 BlockedTimingAspect *timed_thread;
183 if ( (timed_thread = dynamic_cast<BlockedTimingAspect *>(t)) != NULL ) {
184 // find thread and remove
185 BlockedTimingAspect::WakeupHook hook = timed_thread->blockedTimingAspectHook();
186 if ( threads.find(hook) != threads.end() ) {
187 threads[hook].remove_locked(t);
188 if (threads[hook].empty()) threads.erase(hook);
191 untimed_threads.remove_locked(t);
196 /** Add the given thread to internal structures.
197 * Thread is added to the internal structures. If the thread has the
198 * BlockedTimingAspect then the hook is added to the changed list.
200 * @param t thread to add
201 * @param changed list of changed hooks, appropriate hook is added if necessary
204 FawkesThreadManager::internal_add_thread(Thread *t)
206 BlockedTimingAspect *timed_thread;
207 if ( (timed_thread = dynamic_cast<BlockedTimingAspect *>(t)) != NULL ) {
208 BlockedTimingAspect::WakeupHook hook = timed_thread->blockedTimingAspectHook();
210 if ( threads.find(hook) == threads.end() ) {
211 threads[hook].set_name("FawkesThreadManagerList Hook %i", hook);
212 threads[hook].set_maintain_barrier(true);
214 threads[hook].push_back_locked(t);
216 waitcond_timedthreads->wake_all();
218 untimed_threads.push_back_locked(t);
224 * Add the given threads to the thread manager. The threads are initialised
225 * as appropriate and started. See the class documentation for supported
226 * specialisations of threads and the performed initialisation steps.
227 * If the thread initializer cannot initalize one or more threads no thread
228 * is added. In this regard the operation is atomic, either all threads are
230 * @param tl thread list with threads to add
231 * @exception CannotInitializeThreadException thrown if at least one of the
232 * threads could not be initialised
235 FawkesThreadManager::add_maybelocked(ThreadList &tl, bool lock)
237 if ( ! (initializer && finalizer) ) {
238 throw NullPointerException("FawkesThreadManager: initializer/finalizer not set");
242 throw Exception("Not accepting new threads from list that is not fresh, "
243 "list '%s' already sealed", tl.name());
248 // Try to initialise all threads
250 tl.init(initializer, finalizer);
251 } catch (Exception &e) {
259 // All thread initialized, now add threads to internal structure
260 MutexLocker locker(threads.mutex(), lock);
261 for (ThreadList::iterator i = tl.begin(); i != tl.end(); ++i) {
262 internal_add_thread(*i);
270 * Add the given thread to the thread manager. The thread is initialized
271 * as appropriate and started. See the class documentation for supported
272 * specialisations of threads and the performed initialisation steps.
273 * If the thread initializer cannot initalize the thread it is not added.
274 * @param thread thread to add
275 * @param lock if true the environment is locked before adding the thread
276 * @exception CannotInitializeThreadException thrown if at least the
277 * thread could not be initialised
280 FawkesThreadManager::add_maybelocked(Thread *thread, bool lock)
282 if ( thread == NULL ) {
283 throw NullPointerException("FawkesThreadMananger: cannot add NULL as thread");
286 if ( ! (initializer && finalizer) ) {
287 throw NullPointerException("FawkesThreadManager: initializer/finalizer not set");
291 initializer->init(thread);
292 } catch (CannotInitializeThreadException &e) {
293 e.append("Adding thread in FawkesThreadManager failed");
298 MutexLocker locker(threads.mutex(), lock);
299 internal_add_thread(thread);
303 /** Remove the given threads.
304 * The thread manager tries to finalize and stop the threads and then removes the
305 * threads from the internal structures.
307 * This may fail if at least one thread of the given list cannot be finalized, for
308 * example if prepare_finalize() returns false or if the thread finalizer cannot
309 * finalize the thread. In this case a CannotFinalizeThreadException is thrown.
311 * @param tl threads to remove.
312 * @exception CannotFinalizeThreadException At least one thread cannot be safely
314 * @exception ThreadListNotSealedException if the given thread lits tl is not
315 * sealed the thread manager will refuse to remove it
318 FawkesThreadManager::remove_maybelocked(ThreadList &tl, bool lock)
320 if ( ! (initializer && finalizer) ) {
321 throw NullPointerException("FawkesThreadManager: initializer/finalizer not set");
325 if ( ! tl.sealed() ) {
326 throw ThreadListNotSealedException("(FawkesThreadManager) Cannot remove unsealed thread "
327 "list. Not accepting unsealed list '%s' for removal",
332 MutexLocker locker(threads.mutex(), lock);
335 if ( ! tl.prepare_finalize(finalizer) ) {
336 tl.cancel_finalize();
338 throw CannotFinalizeThreadException("One or more threads in list '%s' cannot be "
339 "finalized", tl.name());
341 } catch (CannotFinalizeThreadException &e) {
344 } catch (Exception &e) {
346 e.append("One or more threads in list '%s' cannot be finalized", tl.name());
347 throw CannotFinalizeThreadException(e);
351 tl.finalize(finalizer);
353 for (ThreadList::iterator i = tl.begin(); i != tl.end(); ++i) {
354 internal_remove_thread(*i);
361 /** Remove the given thread.
362 * The thread manager tries to finalize and stop the thread and then removes the
363 * thread from the internal structures.
365 * This may fail if the thread cannot be finalized, for
366 * example if prepare_finalize() returns false or if the thread finalizer cannot
367 * finalize the thread. In this case a CannotFinalizeThreadException is thrown.
369 * @param thread thread to remove.
370 * @exception CannotFinalizeThreadException At least one thread cannot be safely
374 FawkesThreadManager::remove_maybelocked(Thread *thread, bool lock)
376 if ( thread == NULL ) return;
378 if ( ! (initializer && finalizer) ) {
379 throw NullPointerException("FawkesThreadManager: initializer/finalizer not set");
382 MutexLocker locker(threads.mutex(), lock);
384 if ( ! thread->prepare_finalize() ) {
385 thread->cancel_finalize();
386 throw CannotFinalizeThreadException("Thread '%s'cannot be finalized", thread->name());
388 } catch (CannotFinalizeThreadException &e) {
389 e.append("FawkesThreadManager cannot stop thread '%s'", thread->name());
390 thread->cancel_finalize();
396 finalizer->finalize(thread);
399 internal_remove_thread(thread);
405 /** Force removal of the given threads.
406 * The thread manager tries to finalize and stop the threads and then removes the
407 * threads from the internal structures.
409 * This will succeed even if a thread of the given list cannot be finalized, for
410 * example if prepare_finalize() returns false or if the thread finalizer cannot
411 * finalize the thread.
413 * <b>Caution, using this function may damage your robot.</b>
415 * @param tl threads to remove.
416 * @exception ThreadListNotSealedException if the given thread lits tl is not
417 * sealed the thread manager will refuse to remove it
418 * The threads are removed from thread manager control. The threads will be stopped
419 * before they are removed (may cause unpredictable results otherwise).
422 FawkesThreadManager::force_remove(ThreadList &tl)
424 if ( ! tl.sealed() ) {
425 throw ThreadListNotSealedException("Not accepting unsealed list '%s' for removal",
430 threads.mutex()->stopby();
431 tl.force_stop(finalizer);
433 for (ThreadList::iterator i = tl.begin(); i != tl.end(); ++i) {
434 internal_remove_thread(*i);
441 /** Force removal of the given thread.
442 * The thread manager tries to finalize and stop the thread and then removes the
443 * thread from the internal structures.
445 * This will succeed even if the thread cannot be finalized, for
446 * example if prepare_finalize() returns false or if the thread finalizer cannot
447 * finalize the thread.
449 * <b>Caution, using this function may damage your robot.</b>
451 * @param thread thread to remove.
452 * @exception ThreadListNotSealedException if the given thread lits tl is not
453 * sealed the thread manager will refuse to remove it
454 * The threads are removed from thread manager control. The threads will be stopped
455 * before they are removed (may cause unpredictable results otherwise).
458 FawkesThreadManager::force_remove(fawkes::Thread *thread)
460 MutexLocker lock(threads.mutex());
462 thread->prepare_finalize();
463 } catch (Exception &e) {
469 if (finalizer) finalizer->finalize(thread);
472 internal_remove_thread(thread);
477 FawkesThreadManager::wakeup_and_wait(BlockedTimingAspect::WakeupHook hook,
478 unsigned int timeout_usec)
480 MutexLocker lock(threads.mutex());
482 unsigned int timeout_sec = 0;
483 if (timeout_usec >= 1000000) {
484 timeout_sec = timeout_usec / 1000000;
485 timeout_usec -= timeout_sec * 1000000;
488 // Note that the following lines might throw an exception, we just pass it on
489 if ( threads.find(hook) != threads.end() ) {
490 threads[hook].wakeup_and_wait(timeout_sec, timeout_usec * 1000);
496 FawkesThreadManager::wakeup(BlockedTimingAspect::WakeupHook hook, Barrier *barrier)
498 MutexLocker lock(threads.mutex());
500 if ( threads.find(hook) != threads.end() ) {
502 threads[hook].wakeup(barrier);
504 threads[hook].wakeup();
506 if ( threads[hook].size() == 0 ) {
514 FawkesThreadManager::try_recover(std::list<std::string> &recovered_threads)
517 for (tit = threads.begin(); tit != threads.end(); ++tit) {
518 tit->second.try_recover(recovered_threads);
525 FawkesThreadManager::timed_threads_exist()
527 return (threads.size() > 0);
532 FawkesThreadManager::wait_for_timed_threads()
534 __interrupt_timed_thread_wait = false;
535 waitcond_timedthreads->wait();
536 if ( __interrupt_timed_thread_wait ) {
537 __interrupt_timed_thread_wait = false;
538 throw InterruptedException("Waiting for timed threads was interrupted");
543 FawkesThreadManager::interrupt_timed_thread_wait()
545 __interrupt_timed_thread_wait = true;
546 waitcond_timedthreads->wake_all();
551 /** Get a thread collector to be used for an aspect initializer.
552 * @return thread collector instance to use for ThreadProducerAspect.
555 FawkesThreadManager::aspect_collector() const
557 return __aspect_collector;