lock.h 7.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#ifndef B10_THREAD_LOCK_H
#define B10_THREAD_LOCK_H

#include <boost/noncopyable.hpp>

#include <cstdlib> // for NULL.

namespace isc {
namespace util {
namespace thread {
25
class CondVar;
26
27
28
29
30
31
32
33
34
35
36

/// \brief Mutex with very simple interface
///
/// Since mutexes are very system dependant, we create our own wrapper around
/// whatever is available on the system and hide it.
///
/// To use this mutex, create it and then lock and unlock it by creating the
/// Mutex::Locker object.
///
/// Also, as mutex is a low-level system object, an error might happen at any
/// operation with it. We convert many errors to the isc::InvalidOperation,
37
38
39
40
41
/// since the errors usually happen only when used in a wrong way. Any methods
/// or constructors in this class can throw. Allocation errors are converted
/// to std::bad_alloc (for example when OS-dependant limit of mutexes is
/// exceeded). Some errors which usually mean a programmer error abort the
/// program, since there could be no safe way to recover from them.
42
43
44
///
/// The current interface is somewhat minimalistic. If we ever need more, we
/// can add it later.
45
class Mutex : boost::noncopyable {
46
47
48
public:
    /// \brief Constructor.
    ///
49
50
    /// Creates a mutex. It is a non-recursive mutex (can be locked just once,
    /// if the same threads tries to lock it again, Bad Things Happen).
51
52
53
54
55
56
57
58
59
    ///
    /// Depending on compilation parameters and OS, the mutex may or may not
    /// do some error and sanity checking. However, such checking is meant
    /// only to aid development, not rely on it as a feature.
    ///
    /// \throw std::bad_alloc In case allocation of something (memory, the
    ///     OS mutex) fails.
    /// \throw isc::InvalidOperation Other unspecified errors around the mutex.
    ///     This should be rare.
60
    Mutex();
61

62
63
    /// \brief Destructor.
    ///
64
    /// Destroys the mutex. It is not allowed to destroy a mutex which is
65
66
    /// currently locked. This means a Locker created with this Mutex must
    /// never live longer than the Mutex itself.
67
68
    ~Mutex();

69
70
    /// \brief This holds a lock on a Mutex.
    ///
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
71
    /// To lock a mutex, create a locker. It'll get unlocked when the locker
72
73
74
75
76
77
    /// is destroyed.
    ///
    /// If you create the locker on the stack or using some other "garbage
    /// collecting" mechanism (auto_ptr, for example), it ensures exception
    /// safety with regards to the mutex - it'll get released on the exit
    /// of function no matter by what means.
78
    class Locker : boost::noncopyable {
79
80
81
82
83
84
85
    public:
        /// \brief Constructor.
        ///
        /// Locks the mutex. May block for extended period of time.
        ///
        /// \throw isc::InvalidOperation when OS reports error. This usually
        ///     means an attempt to use the mutex in a wrong way (locking
86
        ///     a mutex second time from the same thread, for example).
87
        Locker(Mutex& mutex) :
88
            mutex_(mutex)
89
90
91
        {
            mutex.lock();
        }
92

93
94
95
        /// \brief Destructor.
        ///
        /// Unlocks the mutex.
96
        ~Locker() {
97
            mutex_.unlock();
98
99
        }
    private:
100
        Mutex& mutex_;
101
    };
102
103
104
105
106
107
108
109
    /// \brief If the mutex is currently locked
    ///
    /// This is debug aiding method only. And it might be unavailable in
    /// non-debug build (because keeping the state might be needlesly
    /// slow).
    ///
    /// \todo Disable in non-debug build
    bool locked() const;
110
private:
111
112
113
114
115
116
117
118
119
120
    friend class CondVar;

    // Commonly called after acquiring the lock, checking and updating
    // internal state for debug.
    void postLockAction();

    // Commonly called before releasing the lock, checking and updating
    // internal state for debug.
    void preUnlockAction();

121
    class Impl;
122
123
124
125
126
    Impl* impl_;
    void lock();
    void unlock();
};

127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/// \brief Encapsulation for a condition variable.
///
/// This class provides a simple encapsulation of condition variable for
/// inter-thread synchronization.  It has similar but simplified interface as
/// that for \c pthread_bond_ variants.
///
/// It uses the \c Mutex class object for the mutex used with the condition
/// variable.  Since for normal applications the internal \c Mutex::Locker
/// class is the only available interface to acquire a lock, sample code
/// for waiting on a condition variable would look like this:
/// \code
/// CondVar cond;
/// Mutex mutex;
/// {
///     Mutex::Locker locker(mutex);
///     while (some_condition) {
///         cond.wait(mutex);
///     }
///     // do something under the protection of locker
/// }   // lock is released here
/// \endcode
/// Note that \c mutex passed to the \c wait() method must be the same one
/// used to construct the \c locker.
///
/// \note This class is defined as a friend class of \c Mutex and directly
/// refers to and modifies private internals of the \c Mutex class.  It breaks
/// the assumption that the lock is only acquired or released via the
/// \c Locker class and breaks other integrity assumption on \c Mutex,
/// thereby making it more fragile, but we couldn't find other way to
/// implement a safe and still simple realization of condition variables.
/// So, this is a kind of compromise.  If this class is needed to be
/// extended, first consider a way to use public interfaces of \c Mutex;
/// do not easily rely on the fact that this class is a friend of it.
160
161
class CondVar : boost::noncopyable {
public:
162
163
164
165
    /// \brief Constructor.
    ///
    /// \throw std::bad_alloc memory allocation failure
    /// \throw isc::Unexpected other unexpected shortage of system resource
166
    CondVar();
167
168
169
170
171
172

    /// \brief Destructor.
    ///
    /// An object of this class must not be destructed while some thread
    /// is waiting on it.  If this condition isn't met the destructor will
    /// terminate the program.
173
    ~CondVar();
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189

    /// \brief Wait on the condition variable.
    ///
    /// This method works like \c pthread_cond_wait().  For mutex it takes
    /// an \c Mutex class object.  Unlike \c pthread_cond_wait(), however,
    /// this method requires a lock for the mutex has been acquired.
    /// If this condition isn't met, it can throw an exception (in the
    /// debug mode build) or result in undefined behavior.
    ///
    /// The lock will be automatically released within this method, and
    /// will be re-acquired on the exit of this method.
    ///
    /// \throw isc::InvalidOperation mutex isn't locked
    /// \throw isc::BadValue mutex is not a valid \c Mutex object
    ///
    /// \param mutex A \c Mutex object to be released on wait().
190
    void wait(Mutex& mutex);
191
192
193
194
195
196
197
198

    /// \brief Unblock a thread waiting for the condition variable.
    ///
    /// This method waits one of other threads (if any) waiting on this object
    /// via the \c wait() call.
    ///
    /// This method never throws; if some unexpected low level error happens
    /// it terminates the program.
199
200
201
202
203
    void signal();
private:
    class Impl;
    Impl* impl_;
};
204

205
206
207
} // namespace thread
} // namespace util
} // namespace isc
208
209

#endif
210
211
212
213

// Local Variables:
// mode: c++
// End: