aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/mt/CondVar.h
blob: 0ab956bd6c1669c871a2b81bb6fe8422285f0fe7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/*
 * barrier -- mouse and keyboard sharing utility
 * Copyright (C) 2012-2016 Symless Ltd.
 * Copyright (C) 2002 Chris Schoeneman
 * 
 * This package is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * found in the file LICENSE that should have accompanied this file.
 * 
 * This package is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "mt/Mutex.h"
#include "common/basic_types.h"

class Stopwatch;

//! Generic condition variable
/*!
This class provides functionality common to all condition variables
but doesn't provide the actual variable storage.  A condition variable
is a multiprocessing primitive that can be waited on.  Every condition
variable has an associated mutex.
*/
class CondVarBase {
public:
    /*!
    \c mutex must not be NULL.  All condition variables have an
    associated mutex.  The mutex needn't be unique to one condition
    variable.
    */
    CondVarBase(Mutex* mutex);
    ~CondVarBase();

    //! @name manipulators
    //@{

    //! Lock the condition variable's mutex
    /*!
    Lock the condition variable's mutex.  The condition variable should
    be locked before reading or writing it.  It must be locked for a
    call to wait().  Locks are not recursive;  locking a locked mutex
    will deadlock the thread.
    */
    void                lock() const;

    //! Unlock the condition variable's mutex
    void                unlock() const;

    //! Signal the condition variable
    /*!
    Wake up one waiting thread, if there are any.  Which thread gets
    woken is undefined.
    */
    void                signal();

    //! Signal the condition variable
    /*!
    Wake up all waiting threads, if any.
    */
    void                broadcast();

    //@}
    //! @name accessors
    //@{

    //! Wait on the condition variable
    /*!
    Wait on the condition variable.  If \c timeout < 0 then wait until
    signalled, otherwise up to \c timeout seconds or until signalled,
    whichever comes first.    Returns true if the object was signalled
    during the wait, false otherwise.
    
    The proper way to wait for a condition is:
    \code
    cv.lock();
    while (cv-expr) {
        cv.wait();
    }
    cv.unlock();
    \endcode
    where \c cv-expr involves the value of \c cv and is false when the
    condition is satisfied.

    (cancellation point)
    */
    bool                wait(double timeout = -1.0) const;

    //! Wait on the condition variable
    /*!
    Same as \c wait(double) but use \c timer to compare against \c timeout.
    Since clients normally wait on condition variables in a loop, clients
    can use this to avoid recalculating \c timeout on each iteration.
    Passing a stopwatch with a negative \c timeout is pointless (it will
    never time out) but permitted.

    (cancellation point)
    */
    bool                wait(Stopwatch& timer, double timeout) const;

    //! Get the mutex
    /*!
    Get the mutex passed to the c'tor.
    */
    Mutex*                getMutex() const;

    //@}

private:
    // not implemented
    CondVarBase(const CondVarBase&);
    CondVarBase&        operator=(const CondVarBase&);

private:
    Mutex*                m_mutex;
    ArchCond            m_cond;
};

//! Condition variable
/*!
A condition variable with storage for type \c T.
*/
template <class T>
class CondVar : public CondVarBase {
public:
    //! Initialize using \c value
    CondVar(Mutex* mutex, const T& value);
    //! Initialize using another condition variable's value
    CondVar(const CondVar&);
    ~CondVar();

    //! @name manipulators
    //@{

    //! Assigns the value of \c cv to this
    /*!
    Set the variable's value.  The condition variable should be locked
    before calling this method.
    */
    CondVar&            operator=(const CondVar& cv);

    //! Assigns \c value to this
    /*!
    Set the variable's value.  The condition variable should be locked
    before calling this method.
    */
    CondVar&            operator=(const T& v);

    //@}
    //! @name accessors
    //@{

    //! Get the variable's value
    /*!
    Get the variable's value.  The condition variable should be locked
    before calling this method.
    */
                        operator const volatile T&() const;

    //@}

private:
    volatile T            m_data;
};

template <class T>
inline
CondVar<T>::CondVar(
    Mutex* mutex,
    const T& data) :
    CondVarBase(mutex),
    m_data(data)
{
    // do nothing
}

template <class T>
inline
CondVar<T>::CondVar(
    const CondVar& cv) :
    CondVarBase(cv.getMutex()),
    m_data(cv.m_data)
{
    // do nothing
}

template <class T>
inline
CondVar<T>::~CondVar()
{
    // do nothing
}

template <class T>
inline
CondVar<T>&
CondVar<T>::operator=(const CondVar<T>& cv)
{
    m_data = cv.m_data;
    return *this;
}

template <class T>
inline
CondVar<T>&
CondVar<T>::operator=(const T& data)
{
    m_data = data;
    return *this;
}

template <class T>
inline
CondVar<T>::operator const volatile T&() const
{
    return m_data;
}