thread.cc 5.69 KB
Newer Older
1
// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
2
//
3 4 5
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6

7 8
#include <config.h>

9 10
#include <util/threads/thread.h>
#include <util/threads/sync.h>
11 12 13 14 15 16 17

#include <memory>
#include <string>
#include <cstring>
#include <cerrno>

#include <pthread.h>
18
#include <signal.h>
19

20
#include <boost/noncopyable.hpp>
21 22
#include <boost/scoped_ptr.hpp>

23 24
using std::string;
using std::exception;
25
using std::unique_ptr;
26
using boost::scoped_ptr;
27 28 29 30 31

namespace isc {
namespace util {
namespace thread {

32 33 34 35 36 37 38
namespace {

// Signal blocker class.
class Blocker : boost::noncopyable {
public:
    // Constructor blocks all signals
    Blocker() {
Francis Dupont's avatar
Francis Dupont committed
39 40 41
        sigset_t new_mask;
        sigfillset(&new_mask);
        pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask_);
42 43 44 45
    }

    // Destructor restores the previous signal mask
    ~Blocker() {
Francis Dupont's avatar
Francis Dupont committed
46
        pthread_sigmask(SIG_SETMASK, &old_mask_, 0);
47 48 49 50 51 52 53 54 55
    }

private:
    // The previous signal mask
    sigset_t old_mask_;
};

}

56 57 58 59 60 61 62 63 64 65 66
// 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) :
67 68
        // Two things to happen before destruction - thread needs to terminate
        // and the creating thread needs to release it.
69 70
        waiting_(2),
        main_(main),
71
        exception_(false),
Francis Dupont's avatar
Francis Dupont committed
72
        tid_(0)
73 74 75 76 77 78
    {}
    // 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
79
            Mutex::Locker locker(impl->mutex_);
80
            if (--impl->waiting_ == 0) {
81 82 83 84 85 86 87 88 89
                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) {
90
        Impl* impl = static_cast<Impl*>(impl_raw);
91 92
        try {
            impl->main_();
93
        } catch (const exception& e) {
94 95
            impl->exception_ = true;
            impl->exception_text_ = e.what();
96
        } catch (...) {
97
            impl->exception_ = true;
98
            impl->exception_text_ = "Unknown exception";
99 100 101 102 103 104 105 106 107 108 109 110 111
        }
        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_;
112 113 114 115 116 117 118 119 120
    // The mutex protects the waiting_ member, which ensures there are
    // no race conditions and collisions when terminating. The other members
    // should be safe, because:
    // * tid_ is read only.
    // * exception_ and exception_text_ is accessed outside of the thread
    //   only after join, by that time the thread must have terminated.
    // * main_ is used in a read-only way here. If there are any shared
    //   resources used inside, it is up to the main_ itself to take care.
    Mutex mutex_;
121
    // Which thread are we talking about anyway?
122
    pthread_t tid_;
123 124 125 126 127
};

Thread::Thread(const boost::function<void ()>& main) :
    impl_(NULL)
{
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 133 134 135 136 137 138 139
    // 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.
140
            isc_throw(isc::InvalidOperation, std::strerror(result));
141 142 143
    }
}

144
Thread::~Thread() {
145
    if (impl_ != NULL) {
146 147
        // In case we didn't call wait yet
        const int result = pthread_detach(impl_->tid_);
148 149
        Impl::done(impl_);
        impl_ = NULL;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
150
        // If the detach ever fails, something is screwed rather badly.
151
        assert(result == 0);
152 153 154 155 156 157
    }
}

void
Thread::wait() {
    if (impl_ == NULL) {
158 159
        isc_throw(isc::InvalidOperation,
                  "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 168
    // Was there an exception in the thread?
    scoped_ptr<UncaughtException> ex;
169 170 171 172
    // Something here could in theory throw. But we already terminated the thread, so
    // we need to make sure we are in consistent state even in such situation (like
    // releasing the mutex and impl_).
    try {
173 174 175 176 177 178 179
        if (impl_->exception_) {
            ex.reset(new UncaughtException(__FILE__, __LINE__,
                                           impl_->exception_text_.c_str()));
        }
    } catch (...) {
        Impl::done(impl_);
        impl_ = NULL;
180 181
        // We have eaten the UncaughtException by now, but there's another
        // exception instead, so we have at least something.
182
        throw;
183
    }
184

185 186 187 188 189 190 191 192 193 194
    Impl::done(impl_);
    impl_ = NULL;
    if (ex.get() != NULL) {
        throw UncaughtException(*ex);
    }
}

}
}
}