Skip to content

Commit 3d815de

Browse files
authored
Merge pull request #5419 from kjbracey-arm/kernel_ticks
Add absolute millisecond tick count to RTOS classes
2 parents 964e6e7 + 2b77caa commit 3d815de

File tree

11 files changed

+303
-20
lines changed

11 files changed

+303
-20
lines changed

rtos/ConditionVariable.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@
2020
* SOFTWARE.
2121
*/
2222
#include "rtos/ConditionVariable.h"
23+
#include "rtos/Kernel.h"
2324
#include "rtos/Thread.h"
2425

2526
#include "mbed_error.h"
2627
#include "mbed_assert.h"
2728

2829
namespace rtos {
2930

30-
3131
ConditionVariable::Waiter::Waiter(): sem(0), prev(NULL), next(NULL), in_list(false)
3232
{
3333
// No initialization to do
@@ -64,6 +64,24 @@ bool ConditionVariable::wait_for(uint32_t millisec)
6464
return timeout;
6565
}
6666

67+
bool ConditionVariable::wait_until(uint64_t millisec)
68+
{
69+
uint64_t now = Kernel::get_ms_count();
70+
71+
if (now >= millisec) {
72+
// Time has already passed - standard behaviour is to
73+
// treat as a "try".
74+
return wait_for(0);
75+
} else if (millisec - now >= osWaitForever) {
76+
// Exceeds maximum delay of underlying wait_for -
77+
// spuriously wake after 49 days, indicating no timeout.
78+
wait_for(osWaitForever - 1);
79+
return false;
80+
} else {
81+
return wait_for(millisec - now);
82+
}
83+
}
84+
6785
void ConditionVariable::notify_one()
6886
{
6987
MBED_ASSERT(_mutex.get_owner() == Thread::gettid());

rtos/ConditionVariable.h

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class ConditionVariable : private mbed::NonCopyable<ConditionVariable> {
120120
public:
121121
/** Create and Initialize a ConditionVariable object
122122
*
123-
* @note You may call this function from ISR context.
123+
* @note You cannot call this function from ISR context.
124124
*/
125125
ConditionVariable(Mutex &mutex);
126126

@@ -150,6 +150,39 @@ class ConditionVariable : private mbed::NonCopyable<ConditionVariable> {
150150
*/
151151
void wait();
152152

153+
/** Wait for a notification until specified time
154+
*
155+
* @param millisec absolute end time referenced to Kernel::get_ms_count()
156+
* @return true if a timeout occurred, false otherwise.
157+
*
158+
* @note - The thread calling this function must be the owner of the
159+
* ConditionVariable's mutex and it must be locked exactly once
160+
* @note - Spurious notifications can occur so the caller of this API
161+
* should check to make sure the condition they are waiting on has
162+
* been met
163+
*
164+
* Example:
165+
* @code
166+
* mutex.lock();
167+
* uint64_t end_time = Kernel::get_ms_count() + COND_WAIT_TIMEOUT;
168+
*
169+
* while (!condition_met) {
170+
* if (cond.wait_until(end_time)) {
171+
* break;
172+
* }
173+
* }
174+
*
175+
* if (condition_met) {
176+
* function_to_handle_condition();
177+
* }
178+
*
179+
* mutex.unlock();
180+
* @endcode
181+
*
182+
* @note You cannot call this function from ISR context.
183+
*/
184+
bool wait_until(uint64_t millisec);
185+
153186
/** Wait for a notification or timeout
154187
*
155188
* @param millisec timeout value or osWaitForever in case of no time-out.
@@ -164,15 +197,12 @@ class ConditionVariable : private mbed::NonCopyable<ConditionVariable> {
164197
* Example:
165198
* @code
166199
* mutex.lock();
167-
* Timer timer;
168-
* timer.start();
169-
*
170-
* bool timed_out = false;
171-
* uint32_t time_left = TIMEOUT;
172-
* while (!condition_met && !timed_out) {
173-
* timed_out = cond.wait_for(time_left);
174-
* uint32_t elapsed = timer.read_ms();
175-
* time_left = elapsed > TIMEOUT ? 0 : TIMEOUT - elapsed;
200+
*
201+
* while (!condition_met) {
202+
* cond.wait_for(MAX_SLEEP_TIME);
203+
* if (!condition_met) {
204+
* do_other_work_while_condition_false();
205+
* }
176206
* }
177207
*
178208
* if (condition_met) {
@@ -190,21 +220,21 @@ class ConditionVariable : private mbed::NonCopyable<ConditionVariable> {
190220
*
191221
* @note - The thread calling this function must be the owner of the ConditionVariable's mutex
192222
*
193-
* @note This function may be called from ISR context.
223+
* @note You cannot call this function from ISR context.
194224
*/
195225
void notify_one();
196226

197227
/** Notify all waiters on this condition variable that a condition changed.
198228
*
199229
* @note - The thread calling this function must be the owner of the ConditionVariable's mutex
200230
*
201-
* @note This function may be called from ISR context.
231+
* @note You cannot call this function from ISR context.
202232
*/
203233
void notify_all();
204234

205235
/** ConditionVariable destructor
206236
*
207-
* @note You may call this function from ISR context.
237+
* @note You cannot call this function from ISR context.
208238
*/
209239
~ConditionVariable();
210240

rtos/Kernel.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2017 ARM Limited
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
23+
#include "rtos/Kernel.h"
24+
25+
#include "mbed.h"
26+
27+
namespace rtos {
28+
29+
uint64_t Kernel::get_ms_count() {
30+
// CMSIS-RTOS 2.1.0 and 2.1.1 differ in the time type. We assume
31+
// our header at least matches the implementation, so we don't try looking
32+
// at the run-time version report. (There's no compile-time version report)
33+
34+
// 2.1.0 uint64_t osKernelGetTickCount(void), not documented as callable from ISR (but RTX does allow)
35+
// 2.1.1 uint32_t osKernelGetTickCount(void), callable from ISR
36+
// 2.1.x who knows? We assume could go back to uint64_t
37+
if (sizeof osKernelGetTickCount() == sizeof(uint64_t)) {
38+
return osKernelGetTickCount();
39+
} else /* assume 32-bit */ {
40+
// Based on suggestion in CMSIS-RTOS 2.1.1 docs, but with reentrancy
41+
// protection for the tick memory. We use critical section rather than a
42+
// mutex, as hopefully this method can be callable from interrupt later -
43+
// only thing currently preventing it is that pre CMSIS RTOS 2.1.1, it's
44+
// not defined as safe.
45+
// We assume this is called multiple times per 32-bit wrap period (49 days).
46+
static uint32_t tick_h, tick_l;
47+
48+
core_util_critical_section_enter();
49+
// The 2.1.1 API says this is legal from an ISR - we assume this means
50+
// it's also legal with interrupts disabled. RTX implementation kind
51+
// of conflates the two.
52+
uint32_t tick32 = osKernelGetTickCount();
53+
if (tick32 < tick_l) {
54+
tick_h++;
55+
}
56+
tick_l = tick32;
57+
uint64_t ret = ((uint64_t) tick_h << 32) | tick_l;
58+
core_util_critical_section_exit();
59+
return ret;
60+
}
61+
}
62+
63+
}

rtos/Kernel.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2017 ARM Limited
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
#ifndef KERNEL_H
23+
#define KERNEL_H
24+
25+
#include <stdint.h>
26+
27+
namespace rtos {
28+
/** \addtogroup rtos */
29+
/** @{*/
30+
31+
/** Functions in the Kernel namespace control RTOS kernel information. */
32+
namespace Kernel {
33+
34+
/** Read the current RTOS kernel millisecond tick count.
35+
The tick count corresponds to the tick count used by the RTOS for timing
36+
purposes. It increments monotonically from 0 at boot, hence effectively
37+
never wraps. If the underlying RTOS only provides a 32-bit tick count,
38+
this method expands it to 64 bits.
39+
@return RTOS kernel current tick count
40+
@note mbed OS always uses millisecond RTOS ticks, and this could only wrap
41+
after half a billion years
42+
@note You cannot call this function from ISR context.
43+
*/
44+
uint64_t get_ms_count();
45+
46+
} // namespace Kernel
47+
48+
} // namespace rtos
49+
#endif
50+
51+
/** @}*/

rtos/Mutex.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* SOFTWARE.
2121
*/
2222
#include "rtos/Mutex.h"
23+
#include "rtos/Kernel.h"
2324

2425
#include <string.h>
2526
#include "mbed_error.h"
@@ -58,11 +59,30 @@ osStatus Mutex::lock(uint32_t millisec) {
5859
}
5960

6061
bool Mutex::trylock() {
61-
if (osMutexAcquire(_id, 0) == osOK) {
62-
_count++;
62+
return trylock_for(0);
63+
}
64+
65+
bool Mutex::trylock_for(uint32_t millisec) {
66+
osStatus status = lock(millisec);
67+
if (status == osOK) {
6368
return true;
69+
}
70+
71+
MBED_ASSERT(status == osErrorTimeout || status == osErrorResource);
72+
73+
return false;
74+
}
75+
76+
bool Mutex::trylock_until(uint64_t millisec) {
77+
uint64_t now = Kernel::get_ms_count();
78+
79+
if (now >= millisec) {
80+
return trylock();
81+
} else if (millisec - now >= osWaitForever) {
82+
// API permits early return
83+
return trylock_for(osWaitForever - 1);
6484
} else {
65-
return false;
85+
return trylock_for(millisec - now);
6686
}
6787
}
6888

rtos/Mutex.h

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,19 +78,44 @@ class Mutex : private mbed::NonCopyable<Mutex> {
7878

7979
/** Try to lock the mutex, and return immediately
8080
@return true if the mutex was acquired, false otherwise.
81+
@note equivalent to trylock_for(0)
8182
82-
@note This function cannot be called from ISR context.
83+
@note You cannot call this function from ISR context.
8384
*/
8485
bool trylock();
8586

87+
/** Try to lock the mutex for a specified time
88+
@param millisec timeout value or 0 in case of no time-out.
89+
@return true if the mutex was acquired, false otherwise.
90+
@note the underlying RTOS may have a limit to the maximum wait time
91+
due to internal 32-bit computations, but this is guaranteed to work if the
92+
wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
93+
the lock attempt will time out earlier than specified.
94+
95+
@note You cannot call this function from ISR context.
96+
*/
97+
bool trylock_for(uint32_t millisec);
98+
99+
/** Try to lock the mutex until specified time
100+
@param millisec absolute timeout time, referenced to Kernel::get_ms_count()
101+
@return true if the mutex was acquired, false otherwise.
102+
@note the underlying RTOS may have a limit to the maximum wait time
103+
due to internal 32-bit computations, but this is guaranteed to work if the
104+
wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
105+
the lock attempt will time out earlier than specified.
106+
107+
@note You cannot call this function from ISR context.
108+
*/
109+
bool trylock_until(uint64_t millisec);
110+
86111
/** Unlock the mutex that has previously been locked by the same thread
87112
@return status code that indicates the execution status of the function:
88113
@a osOK the mutex has been released.
89114
@a osErrorParameter internal error.
90115
@a osErrorResource the mutex was not locked or the current thread wasn't the owner.
91116
@a osErrorISR this function cannot be called from the interrupt service routine.
92117
93-
@note This function cannot be called from ISR context.
118+
@note You cannot call this function from ISR context.
94119
*/
95120
osStatus unlock();
96121

rtos/Semaphore.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* SOFTWARE.
2121
*/
2222
#include "rtos/Semaphore.h"
23+
#include "rtos/Kernel.h"
2324
#include "platform/mbed_assert.h"
2425

2526
#include <string.h>
@@ -57,6 +58,20 @@ int32_t Semaphore::wait(uint32_t millisec) {
5758
}
5859
}
5960

61+
int32_t Semaphore::wait_until(uint64_t millisec) {
62+
uint64_t now = Kernel::get_ms_count();
63+
uint32_t timeout;
64+
65+
if (now >= millisec) {
66+
return wait(0);
67+
} else if (millisec - now >= osWaitForever) {
68+
// API permits early return
69+
return wait(osWaitForever - 1);
70+
} else {
71+
return wait(millisec - now);
72+
}
73+
}
74+
6075
osStatus Semaphore::release(void) {
6176
return osSemaphoreRelease(_id);
6277
}

rtos/Semaphore.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,18 @@ class Semaphore : private mbed::NonCopyable<Semaphore> {
6767
*/
6868
int32_t wait(uint32_t millisec=osWaitForever);
6969

70+
/** Wait until a Semaphore resource becomes available.
71+
@param millisec absolute timeout time, referenced to Kernel::get_ms_count()
72+
@return number of available tokens, before taking one; or -1 in case of incorrect parameters
73+
@note the underlying RTOS may have a limit to the maximum wait time
74+
due to internal 32-bit computations, but this is guaranteed to work if the
75+
wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
76+
the acquire attempt will time out earlier than specified.
77+
78+
@note You cannot call this function from ISR context.
79+
*/
80+
int32_t wait_until(uint64_t millisec);
81+
7082
/** Release a Semaphore resource that was obtain with Semaphore::wait.
7183
@return status code that indicates the execution status of the function:
7284
@a osOK the token has been correctly released.

0 commit comments

Comments
 (0)