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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
|
/**
* \file
* \brief Semaphore class header
*
* \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
*
* \par License
* 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/.
*/
#ifndef INCLUDE_DISTORTOS_SEMAPHORE_HPP_
#define INCLUDE_DISTORTOS_SEMAPHORE_HPP_
#include "distortos/internal/scheduler/ThreadList.hpp"
#include "distortos/TickClock.hpp"
namespace distortos
{
/**
* \brief Semaphore is the basic synchronization primitive
*
* Similar to POSIX semaphores - http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_16
*
* \ingroup synchronization
*/
class Semaphore
{
public:
/// type used for semaphore's "value"
using Value = unsigned int;
/**
* \brief Semaphore constructor
*
* Similar to sem_init() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_init.html#
*
* \param [in] value is the initial value of the semaphore, if this value is greater than maxValue, it will be
* truncated
* \param [in] maxValue is the max value of the semaphore before post() returns EOVERFLOW, default - max for Value
* type
*/
constexpr explicit Semaphore(const Value value, const Value maxValue = std::numeric_limits<Value>::max()) :
blockedList_{},
value_{value <= maxValue ? value : maxValue},
maxValue_{maxValue}
{
}
/**
* \brief Semaphore destructor
*
* Similar to sem_destroy() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_destroy.html#
*
* It is safe to destroy a semaphore upon which no threads are currently blocked. The effect of destroying a
* semaphore upon which other threads are currently blocked is system error.
*/
~Semaphore()
{
}
/**
* \brief Gets current value of semaphore.
*
* Similar to sem_getvalue() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_getvalue.html#
*
* \return current value of semaphore, positive value if semaphore is not locked, zero otherwise
*/
Value getValue() const
{
return value_;
}
/**
* \brief Unlocks the semaphore.
*
* Similar to sem_post() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_post.html#
*
* This function shall unlock the semaphore by performing a semaphore unlock operation. If the semaphore value
* resulting from this operation is positive, then no threads were blocked waiting for the semaphore to become
* unlocked; the semaphore value is simply incremented. Otherwise one of the threads blocked waiting for the
* semaphore shall be allowed to return successfully from its call to lock() - the highest priority waiting thread
* shall be unblocked, and if there is more than one highest priority thread blocked waiting for the semaphore, then
* the highest priority thread that has been waiting the longest shall be unblocked.
*
* \return zero if the calling process successfully "posted" the semaphore, error code otherwise:
* - EOVERFLOW - the maximum allowable value for a semaphore would be exceeded;
*/
int post();
/**
* \brief Tries to lock the semaphore.
*
* Similar to sem_trywait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_trywait.html#
*
* This function shall lock the semaphore only if the semaphore is currently not locked; that is, if the semaphore
* value is currently positive. Otherwise, it shall not lock the semaphore. Upon successful return, the state of the
* semaphore shall be locked and shall remain locked until unlock() function is executed.
*
* \return zero if the calling process successfully performed the semaphore lock operation, error code otherwise:
* - EAGAIN - semaphore was already locked, so it cannot be immediately locked by the tryWait() operation;
*/
int tryWait();
/**
* \brief Tries to lock the semaphore for given duration of time.
*
* Similar to sem_timedwait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_timedwait.html#
*
* If the semaphore is already locked, the calling thread shall block until the semaphore becomes available as in
* wait() function. If the semaphore cannot be locked without waiting for another thread to unlock the semaphore,
* this wait shall be terminated when the specified timeout expires.
*
* Under no circumstance shall the function fail with a timeout if the semaphore can be locked immediately. The
* validity of the duration parameter need not be checked if the semaphore can be locked immediately.
*
* \param [in] duration is the duration after which the wait will be terminated without locking the semaphore
*
* \return zero if the calling process successfully performed the semaphore lock operation, error code otherwise:
* - EINTR - the wait was interrupted by an unmasked, caught signal;
* - ETIMEDOUT - the semaphore could not be locked before the specified timeout expired;
*/
int tryWaitFor(TickClock::duration duration);
/**
* \brief Tries to lock the semaphore for given duration of time.
*
* Template variant of tryWaitFor(TickClock::duration duration).
*
* \tparam Rep is type of tick counter
* \tparam Period is std::ratio type representing the tick period of the clock, in seconds
*
* \param [in] duration is the duration after which the wait will be terminated without locking the semaphore
*
* \return zero if the calling process successfully performed the semaphore lock operation, error code otherwise:
* - EINTR - the wait was interrupted by an unmasked, caught signal;
* - ETIMEDOUT - the semaphore could not be locked before the specified timeout expired;
*/
template<typename Rep, typename Period>
int tryWaitFor(const std::chrono::duration<Rep, Period> duration)
{
return tryWaitFor(std::chrono::duration_cast<TickClock::duration>(duration));
}
/**
* \brief Tries to lock the semaphore until given time point.
*
* Similar to sem_timedwait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_timedwait.html#
*
* If the semaphore is already locked, the calling thread shall block until the semaphore becomes available as in
* wait() function. If the semaphore cannot be locked without waiting for another thread to unlock the semaphore,
* this wait shall be terminated when the specified timeout expires.
*
* Under no circumstance shall the function fail with a timeout if the semaphore can be locked immediately. The
* validity of the timePoint parameter need not be checked if the semaphore can be locked immediately.
*
* \param [in] timePoint is the time point at which the wait will be terminated without locking the semaphore
*
* \return zero if the calling process successfully performed the semaphore lock operation, error code otherwise:
* - EINTR - the wait was interrupted by an unmasked, caught signal;
* - ETIMEDOUT - the semaphore could not be locked before the specified timeout expired;
*/
int tryWaitUntil(TickClock::time_point timePoint);
/**
* \brief Tries to lock the semaphore until given time point.
*
* Template variant of tryWaitUntil(TickClock::time_point timePoint).
*
* \tparam Duration is a std::chrono::duration type used to measure duration
*
* \param [in] timePoint is the time point at which the wait will be terminated without locking the semaphore
*
* \return zero if the calling process successfully performed the semaphore lock operation, error code otherwise:
* - EINTR - the wait was interrupted by an unmasked, caught signal;
* - ETIMEDOUT - the semaphore could not be locked before the specified timeout expired;
*/
template<typename Duration>
int tryWaitUntil(const std::chrono::time_point<TickClock, Duration> timePoint)
{
return tryWaitUntil(std::chrono::time_point_cast<TickClock::duration>(timePoint));
}
/**
* \brief Locks the semaphore.
*
* Similar to sem_wait() - http://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_trywait.html#
*
* This function shall lock the semaphore by performing a semaphore lock operation on that semaphore. If the
* semaphore value is currently zero, then the calling thread shall not return from the call to lock() until it
* either locks the semaphore or the call is interrupted by a signal. Upon successful return, the state of the
* semaphore shall be locked and shall remain locked until unlock() function is executed.
*
* \return zero if the calling process successfully performed the semaphore lock operation, error code otherwise:
* - EINTR - the wait was interrupted by an unmasked, caught signal;
*/
int wait();
Semaphore(const Semaphore&) = delete;
Semaphore(Semaphore&&) = default;
const Semaphore& operator=(const Semaphore&) = delete;
Semaphore& operator=(Semaphore&&) = delete;
private:
/**
* \brief Internal version of tryWait().
*
* Internal version with no interrupt masking.
*
* \return zero if the calling process successfully performed the semaphore lock operation, error code otherwise:
* - EAGAIN - semaphore was already locked, so it cannot be immediately locked by the tryWait() operation;
*/
int tryWaitInternal();
/// ThreadControlBlock objects blocked on this semaphore
internal::ThreadList blockedList_;
/// internal value of the semaphore
Value value_;
/// max value of the semaphore
Value maxValue_;
};
} // namespace distortos
#endif // INCLUDE_DISTORTOS_SEMAPHORE_HPP_
|