// PERFORMANCE OF THIS SOFTWARE.
#include "thread.h"
+#include "lock.h"
+
+#include <memory>
+#include <string>
+#include <cstring>
+#include <cerrno>
+
+#include <pthread.h>
+
+using namespace std;
+
+namespace isc {
+namespace util {
+namespace thread {
+
+// The implementation of the Thread class.
+//
+// This internal state is not deleted until the thread terminates and is either
+// waited for or detached. We could do this with shared_ptr (or, shared_ptr and
+// weak_ptr), but we plan on compiling boost without thread support, so it
+// might not be safe. Therefore we use an explicit mutex. It is being locked
+// only 2-3 times in the lifetime of the thread, which should be negligible
+// overhead anyway.
+class Thread::Impl {
+public:
+ Impl(const boost::function<void ()>& main) :
+ waiting_(2),
+ main_(main),
+ exception_(false)
+ {}
+ // Another of the waiting events is done. If there are no more, delete
+ // impl.
+ static void done(Impl* impl) {
+ bool should_delete(false);
+ { // We need to make sure the mutex is unlocked before it is deleted
+ Mutex::Locker locker(impl->mutex);
+ if (-- impl->waiting_ == 0) {
+ should_delete = true;
+ }
+ }
+ if (should_delete) {
+ delete impl;
+ }
+ }
+ // Run the thread. The type of parameter is because the pthread API.
+ static void* run(void* impl_raw) {
+ Impl* impl = reinterpret_cast<Impl*>(impl_raw);
+ try {
+ impl->main_();
+ }
+ catch (const exception& e) {
+ Mutex::Locker locker(impl->mutex);
+ impl->exception_ = true;
+ impl->exception_text_ = e.what();
+ }
+ catch (...) {
+ Mutex::Locker locker(impl->mutex);
+ impl->exception_ = true;
+ }
+ done(impl);
+ return (NULL);
+ }
+ // How many events are waiting? One is for the thread to finish, one
+ // for the destructor of Thread or wait. Once both happen, this is
+ // no longer needed.
+ size_t waiting_;
+ // The main function of the thread.
+ boost::function<void ()> main_;
+ // Was there an exception?
+ bool exception_;
+ string exception_text_;
+ Mutex mutex;
+ // Which thread are we talking about anyway?
+ pthread_t tid;
+};
+
+Thread::Thread(const boost::function<void ()>& main) :
+ impl_(NULL)
+{
+ auto_ptr<Impl> impl(new Impl(main));
+ int result = pthread_create(&impl->tid, NULL, &Impl::run, impl.get());
+ // Any error here?
+ switch (result) {
+ case 0: // All 0K
+ impl_ = impl.release();
+ break;
+ case EAGAIN:
+ throw std::bad_alloc();
+ default: // Other errors. They should not happen.
+ isc_throw(isc::InvalidOperation, strerror(result));
+ }
+}
+
+Thread::~ Thread() {
+ if (impl_ != NULL) {
+ // In case we didn't call wait yet
+ int result = pthread_detach(impl_->tid);
+ Impl::done(impl_);
+ impl_ = NULL;
+ if (result != 0) {
+ // Yes, really throwing from destructor. But this would
+ // mean someone really messed up the internal state, so
+ // we need to do something about it, even if it causes
+ // application to terminate.
+ isc_throw(isc::InvalidOperation, strerror(result));
+ }
+ }
+}
+
+void
+Thread::wait() {
+ if (impl_ == NULL) {
+ isc_throw(isc::InvalidOperation, "Wait called and no thread to wait for");
+ }
+
+ int result = pthread_join(impl_->tid, NULL);
+ if (result != 0) {
+ isc_throw(isc::InvalidOperation, strerror(result));
+ }
+
+ // Was there an exception in the thread?
+ auto_ptr<UncaughtException> ex;
+ if (impl_->exception_) {
+ ex.reset(new UncaughtException(__FILE__, __LINE__,
+ impl_->exception_text_.c_str()));
+ }
+ Impl::done(impl_);
+ impl_ = NULL;
+ if (ex.get() != NULL) {
+ throw UncaughtException(*ex);
+ }
+}
+
+}
+}
+}
/// live peacefully.
///
/// The interface is minimalistic for now. We may need to extend it later.
+///
+/// \note While the objects of this class represent another thread, they
+/// are not thread-safe. You're not supposed to call wait() on the same
+/// object from multiple threads or so. They are reentrant (you can
+/// wait for different threads from different threads).
class Thread : public boost::noncopyable {
public:
/// \brief There's an uncaught exception in a thread.
/// is considered an error. You should generally catch any exceptions form
/// within there and handle them somehow.
///
- /// \param body The code to run inside the thread.
+ /// \param main The code to run inside the thread.
///
/// \throw std::bad_alloc if allocation of the new thread or other resources
/// fails.
- Thread(boost::function<void()> body);
+ /// \throw isc::InvalidOperation for other errors (should not happen).
+ Thread(const boost::function<void()>& main);
/// \brief Destructor.
///
/// It is completely legitimate to destroy the thread without calling