Merge pull request #5419 from kjbracey-arm/kernel_ticks

Add absolute millisecond tick count to RTOS classes
pull/6026/head
Cruz Monrreal 2018-02-06 10:37:27 -06:00 committed by GitHub
commit 3d815de0fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 302 additions and 19 deletions

View File

@ -20,6 +20,7 @@
* SOFTWARE.
*/
#include "rtos/ConditionVariable.h"
#include "rtos/Kernel.h"
#include "rtos/Thread.h"
#include "mbed_error.h"
@ -27,7 +28,6 @@
namespace rtos {
ConditionVariable::Waiter::Waiter(): sem(0), prev(NULL), next(NULL), in_list(false)
{
// No initialization to do
@ -64,6 +64,24 @@ bool ConditionVariable::wait_for(uint32_t millisec)
return timeout;
}
bool ConditionVariable::wait_until(uint64_t millisec)
{
uint64_t now = Kernel::get_ms_count();
if (now >= millisec) {
// Time has already passed - standard behaviour is to
// treat as a "try".
return wait_for(0);
} else if (millisec - now >= osWaitForever) {
// Exceeds maximum delay of underlying wait_for -
// spuriously wake after 49 days, indicating no timeout.
wait_for(osWaitForever - 1);
return false;
} else {
return wait_for(millisec - now);
}
}
void ConditionVariable::notify_one()
{
MBED_ASSERT(_mutex.get_owner() == Thread::gettid());

View File

@ -120,7 +120,7 @@ class ConditionVariable : private mbed::NonCopyable<ConditionVariable> {
public:
/** Create and Initialize a ConditionVariable object
*
* @note You may call this function from ISR context.
* @note You cannot call this function from ISR context.
*/
ConditionVariable(Mutex &mutex);
@ -150,6 +150,39 @@ public:
*/
void wait();
/** Wait for a notification until specified time
*
* @param millisec absolute end time referenced to Kernel::get_ms_count()
* @return true if a timeout occurred, false otherwise.
*
* @note - The thread calling this function must be the owner of the
* ConditionVariable's mutex and it must be locked exactly once
* @note - Spurious notifications can occur so the caller of this API
* should check to make sure the condition they are waiting on has
* been met
*
* Example:
* @code
* mutex.lock();
* uint64_t end_time = Kernel::get_ms_count() + COND_WAIT_TIMEOUT;
*
* while (!condition_met) {
* if (cond.wait_until(end_time)) {
* break;
* }
* }
*
* if (condition_met) {
* function_to_handle_condition();
* }
*
* mutex.unlock();
* @endcode
*
* @note You cannot call this function from ISR context.
*/
bool wait_until(uint64_t millisec);
/** Wait for a notification or timeout
*
* @param millisec timeout value or osWaitForever in case of no time-out.
@ -164,15 +197,12 @@ public:
* Example:
* @code
* mutex.lock();
* Timer timer;
* timer.start();
*
* bool timed_out = false;
* uint32_t time_left = TIMEOUT;
* while (!condition_met && !timed_out) {
* timed_out = cond.wait_for(time_left);
* uint32_t elapsed = timer.read_ms();
* time_left = elapsed > TIMEOUT ? 0 : TIMEOUT - elapsed;
* while (!condition_met) {
* cond.wait_for(MAX_SLEEP_TIME);
* if (!condition_met) {
* do_other_work_while_condition_false();
* }
* }
*
* if (condition_met) {
@ -190,7 +220,7 @@ public:
*
* @note - The thread calling this function must be the owner of the ConditionVariable's mutex
*
* @note This function may be called from ISR context.
* @note You cannot call this function from ISR context.
*/
void notify_one();
@ -198,13 +228,13 @@ public:
*
* @note - The thread calling this function must be the owner of the ConditionVariable's mutex
*
* @note This function may be called from ISR context.
* @note You cannot call this function from ISR context.
*/
void notify_all();
/** ConditionVariable destructor
*
* @note You may call this function from ISR context.
* @note You cannot call this function from ISR context.
*/
~ConditionVariable();

63
rtos/Kernel.cpp Normal file
View File

@ -0,0 +1,63 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "rtos/Kernel.h"
#include "mbed.h"
namespace rtos {
uint64_t Kernel::get_ms_count() {
// CMSIS-RTOS 2.1.0 and 2.1.1 differ in the time type. We assume
// our header at least matches the implementation, so we don't try looking
// at the run-time version report. (There's no compile-time version report)
// 2.1.0 uint64_t osKernelGetTickCount(void), not documented as callable from ISR (but RTX does allow)
// 2.1.1 uint32_t osKernelGetTickCount(void), callable from ISR
// 2.1.x who knows? We assume could go back to uint64_t
if (sizeof osKernelGetTickCount() == sizeof(uint64_t)) {
return osKernelGetTickCount();
} else /* assume 32-bit */ {
// Based on suggestion in CMSIS-RTOS 2.1.1 docs, but with reentrancy
// protection for the tick memory. We use critical section rather than a
// mutex, as hopefully this method can be callable from interrupt later -
// only thing currently preventing it is that pre CMSIS RTOS 2.1.1, it's
// not defined as safe.
// We assume this is called multiple times per 32-bit wrap period (49 days).
static uint32_t tick_h, tick_l;
core_util_critical_section_enter();
// The 2.1.1 API says this is legal from an ISR - we assume this means
// it's also legal with interrupts disabled. RTX implementation kind
// of conflates the two.
uint32_t tick32 = osKernelGetTickCount();
if (tick32 < tick_l) {
tick_h++;
}
tick_l = tick32;
uint64_t ret = ((uint64_t) tick_h << 32) | tick_l;
core_util_critical_section_exit();
return ret;
}
}
}

51
rtos/Kernel.h Normal file
View File

@ -0,0 +1,51 @@
/* mbed Microcontroller Library
* Copyright (c) 2017 ARM Limited
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef KERNEL_H
#define KERNEL_H
#include <stdint.h>
namespace rtos {
/** \addtogroup rtos */
/** @{*/
/** Functions in the Kernel namespace control RTOS kernel information. */
namespace Kernel {
/** Read the current RTOS kernel millisecond tick count.
The tick count corresponds to the tick count used by the RTOS for timing
purposes. It increments monotonically from 0 at boot, hence effectively
never wraps. If the underlying RTOS only provides a 32-bit tick count,
this method expands it to 64 bits.
@return RTOS kernel current tick count
@note mbed OS always uses millisecond RTOS ticks, and this could only wrap
after half a billion years
@note You cannot call this function from ISR context.
*/
uint64_t get_ms_count();
} // namespace Kernel
} // namespace rtos
#endif
/** @}*/

View File

@ -20,6 +20,7 @@
* SOFTWARE.
*/
#include "rtos/Mutex.h"
#include "rtos/Kernel.h"
#include <string.h>
#include "mbed_error.h"
@ -58,11 +59,30 @@ osStatus Mutex::lock(uint32_t millisec) {
}
bool Mutex::trylock() {
if (osMutexAcquire(_id, 0) == osOK) {
_count++;
return trylock_for(0);
}
bool Mutex::trylock_for(uint32_t millisec) {
osStatus status = lock(millisec);
if (status == osOK) {
return true;
} else {
}
MBED_ASSERT(status == osErrorTimeout || status == osErrorResource);
return false;
}
bool Mutex::trylock_until(uint64_t millisec) {
uint64_t now = Kernel::get_ms_count();
if (now >= millisec) {
return trylock();
} else if (millisec - now >= osWaitForever) {
// API permits early return
return trylock_for(osWaitForever - 1);
} else {
return trylock_for(millisec - now);
}
}

View File

@ -78,11 +78,36 @@ public:
/** Try to lock the mutex, and return immediately
@return true if the mutex was acquired, false otherwise.
@note equivalent to trylock_for(0)
@note This function cannot be called from ISR context.
@note You cannot call this function from ISR context.
*/
bool trylock();
/** Try to lock the mutex for a specified time
@param millisec timeout value or 0 in case of no time-out.
@return true if the mutex was acquired, false otherwise.
@note the underlying RTOS may have a limit to the maximum wait time
due to internal 32-bit computations, but this is guaranteed to work if the
wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
the lock attempt will time out earlier than specified.
@note You cannot call this function from ISR context.
*/
bool trylock_for(uint32_t millisec);
/** Try to lock the mutex until specified time
@param millisec absolute timeout time, referenced to Kernel::get_ms_count()
@return true if the mutex was acquired, false otherwise.
@note the underlying RTOS may have a limit to the maximum wait time
due to internal 32-bit computations, but this is guaranteed to work if the
wait is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
the lock attempt will time out earlier than specified.
@note You cannot call this function from ISR context.
*/
bool trylock_until(uint64_t millisec);
/** Unlock the mutex that has previously been locked by the same thread
@return status code that indicates the execution status of the function:
@a osOK the mutex has been released.
@ -90,7 +115,7 @@ public:
@a osErrorResource the mutex was not locked or the current thread wasn't the owner.
@a osErrorISR this function cannot be called from the interrupt service routine.
@note This function cannot be called from ISR context.
@note You cannot call this function from ISR context.
*/
osStatus unlock();

View File

@ -20,6 +20,7 @@
* SOFTWARE.
*/
#include "rtos/Semaphore.h"
#include "rtos/Kernel.h"
#include "platform/mbed_assert.h"
#include <string.h>
@ -57,6 +58,20 @@ int32_t Semaphore::wait(uint32_t millisec) {
}
}
int32_t Semaphore::wait_until(uint64_t millisec) {
uint64_t now = Kernel::get_ms_count();
uint32_t timeout;
if (now >= millisec) {
return wait(0);
} else if (millisec - now >= osWaitForever) {
// API permits early return
return wait(osWaitForever - 1);
} else {
return wait(millisec - now);
}
}
osStatus Semaphore::release(void) {
return osSemaphoreRelease(_id);
}

View File

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

View File

@ -353,6 +353,35 @@ osStatus Thread::wait(uint32_t millisec) {
return osDelay(millisec);
}
osStatus Thread::wait_until(uint64_t millisec) {
// CMSIS-RTOS 2.1.0 and 2.1.1 differ in the time type, which we determine
// by looking at the return type of osKernelGetTickCount. We assume
// our header at least matches the implementation, so we don't try looking
// at the run-time version report. (There's no compile-time version report)
if (sizeof osKernelGetTickCount() == sizeof(uint64_t)) {
// CMSIS-RTOS 2.1.0 has a 64-bit API. The corresponding RTX 5.2.0 can't
// delay more than 0xfffffffe ticks, but there's no limit stated for
// the generic API.
return osDelayUntil(millisec);
} else {
// 64-bit time doesn't wrap (for half a billion years, at last)
uint64_t now = Kernel::get_ms_count();
// Report being late on entry
if (now >= millisec) {
return osErrorParameter;
}
// We're about to make a 32-bit delay call, so have at least this limit
if (millisec - now > 0xFFFFFFFF) {
return osErrorParameter;
}
// And this may have its own internal limit - we'll find out.
// We hope/assume there's no problem with passing
// osWaitForever = 0xFFFFFFFF - that value is only specified to have
// special meaning for osSomethingWait calls.
return osDelay(millisec - now);
}
}
osStatus Thread::yield() {
return osThreadYield();
}

View File

@ -360,7 +360,10 @@ public:
*/
static osEvent signal_wait(int32_t signals, uint32_t millisec=osWaitForever);
/** Wait for a specified time period in millisec:
/** Wait for a specified time period in milliseconds
Being tick-based, the delay will be up to the specified time - eg for
a value of 1 the system waits until the next millisecond tick occurs,
leading to a delay of 0-1 milliseconds.
@param millisec time delay value
@return status code that indicates the execution status of the function.
@ -368,6 +371,22 @@ public:
*/
static osStatus wait(uint32_t millisec);
/** Wait until a specified time in millisec
The specified time is according to Kernel::get_ms_count().
@param millisec absolute time in millisec
@return status code that indicates the execution status of the function.
@note not callable from interrupt
@note if millisec is equal to or lower than the current tick count, this
returns immediately, either with an error or "osOK".
@note the underlying RTOS may have a limit to the maximum wait time
due to internal 32-bit computations, but this is guaranteed to work if the
delay is <= 0x7fffffff milliseconds (~24 days). If the limit is exceeded,
it may return with an immediate error, or wait for the maximum delay.
@note You cannot call this function from ISR context.
*/
static osStatus wait_until(uint64_t millisec);
/** Pass control to next thread that is in state READY.
@return status code that indicates the execution status of the function.

View File

@ -26,6 +26,7 @@
#define RTOS_H
#include "mbed_rtos_storage.h"
#include "rtos/Kernel.h"
#include "rtos/Thread.h"
#include "rtos/Mutex.h"
#include "rtos/RtosTimer.h"