Problems using PC-Lint lock semantic checking with FreeRTOS
Hi,
I’m trying to set up PC-Lint to check for correct releasing of mutexes in FreeRTOS. This is normally done by specifying semantics for the functions which perform the locking and unlocking operations, specifically:
~~~~~~
-sem(xSemaphoreTake, threadlock, 1p)
-sem(xSemaphoreGive, threadunlock, 1p)
-sem(xTaskCreate, thread_create(1), 1p)
~~~~~~
The problem is that xSemaphoreTake and xSemaphoreGive are implemented as macros:
~~~~~~
:::c
Problems using PC-Lint lock semantic checking with FreeRTOS
All the design choices in FreeRTOS have a reason behind them. Some date back to the original versions of FreeRTOS, which were intended to be as small as possible. Hence, the queue is the fundemental primitiave that has all the event management built in, and semaphores were (originally) implemented by macros without adding anything to the code size – that is not quite true now as mutexes in particular are treated differently in the code from binary semaphores. Likewise error detection is done by assert macros that trap errors during development, but can be completely removed once code is tested, keeping size and run-time minimal. SafeRTOS on the other hand makes lots of error checks directly in the code, returns error codes for all anomalies, and therefore requires more code to implement equivalent functionality.
These days the original design choices still have benefits as the smaller amount of code means there is less code to test. However, newer features, such as event groups and direct to task notifications, are not based on queue data structures or functionality. Direct to task notifications can often be used in place of semahores (but not mutexes) and are smaller and much faster than semaphores.
FreeRTOS source code has always avoided using any features or syntax introduced by newer C standards, as it is compiled with more than 20 compilers, and the more obscure compilers only support the older standards. Hence inline functions have never been used in the core of FreeRTOS. However, inline functions are being used in the newer FreeRTOS components (TCP and FAT) as it is unlikely these newer components would not be used on really small processors anyway.
We did once make a type safe version of FreeRTOS, but had to back the changes out, I think (if I recall correctly) because of bugs in elf parsers used by some debuggers meant the debugger veiws were corrupted.
Problems using PC-Lint lock semantic checking with FreeRTOS
Thanks for the explaination. I sat and thought about it last night, and realised I hadn’t factored in old compilers… I guess I’ve been spoiled by IAR EWARM and GCC, both of which are C99 compliant.
Incidentally, I’ve found a way to add type safety by wrapping the macros in functions. This involves creating a new header file which is included after the FreeRTOS headers. Said header contains a series of functions like this:
~~~~~~
:::c
//lint -sem(xSemaphoreTake, threadlock, 1p)
static inline BaseTypet (xSemaphoreTake)(SemaphoreHandlet xSemaphore, const TickTypet xBlockTime)
{
return xSemaphoreTake(xSemaphore, xBlockTime);
}
undef xSemaphoreTake
~~~~~~ The preprocessor is prevented from substituting the macro for the function name by surrounding the function name in brackets; this has no effect on the function definition. Then we simply insert the relevant macro, and undefine the macro after exiting. A few Lint comments are added to the file to inform PC-Lint of the function semantics, e.g. locking and unlocking. This has the advantage that all the strong type checking can be disabled by wrapping the type-checking wrappers with an “#if TYPE_CHECKING” guard, and only define this when Lint is being run (or simply “#if defined(_lint)”). As the code has no side effects, it could even be added to debug builds. To go further (I’m working on this!), an additional “MutexHandle_t” typedef could be defined based on SemaphoreHandle_t. Functions which accept mutexes could then be modified to accept this type, and PC-Lint can then be configured to enforce strong type checking upon it. That is to say, any function which only accepts a mutex will only accept a mutex (in Lint at least). And all this because of someone hamming the name of a variable… I’ll have a look at direct-to-task notifications, but I seem to recall there was some reason we could’t use them. One common pattern we use is that a function kicks off an interrupt-driven operation (e.g. A/D sampling), the interrupt raises a semaphore, and another function waits on the semaphore. The semaphore itself is not exposed outside of the code module in which it resides. The rationale there is that different tasks (or at least different functions running in a given task context) may make use of the A/D at different times. Thanks, Phil.Problems using PC-Lint lock semantic checking with FreeRTOS
As a side note, I think I’ve found a bug — no impact at runtime, but causes an Unreachable Code warning from Lint:
Line 1687 of task.h:
~~~~~~
:::c
define xTaskNotifyGive( xTaskToNotify ) xTaskNotify( ( xTaskToNotify ), 0, eIncrement );
~~~~~~ Should be: ~~~~~~ :::cdefine xTaskNotifyGive( xTaskToNotify ) xTaskNotify( ( xTaskToNotify ), 0, eIncrement )
~~~~~~ Note the trailing semicolon at the end of the macro.Problems using PC-Lint lock semantic checking with FreeRTOS
Thanks for pointing this out – it looks like it has been changed already:
http://sourceforge.net/p/freertos/code/HEAD/tree/trunk/FreeRTOS/Source/include/task.h#l1711
Regards.
Problems using PC-Lint lock semantic checking with FreeRTOS
One common pattern we use is that a function kicks off an interrupt-driven operation (e.g. A/D sampling), the interrupt raises a semaphore, and another function waits on the semaphore. The semaphore itself is not exposed outside of the code module in which it resides. The rationale there is that different tasks (or at least different functions running in a given task context) may make use of the A/D at different times.If I understand this scenario correctly, then you can use a task notification by saving the handle of the task to notify in a variable, then the interrupt service routine clears the variable back to NULL once it has used it to notify the task. http://www.freertos.org/RTOSTaskNotificationAsBinary_Semaphore.html Ideally the variable would be inside a structure used by the driver to describe the peripheral being used. Regards.