wiki:Guidelines/VxWorks

Version 2 (modified by Peter Brockamp, 9 years ago) ( diff )

Started this documentation. Not finished yet, there's more to come!

Introduction

Differences to other operating systems

VxWorks is a real time operating system intended for the execution of "mission critical" tasks. This has some impact on the way things are organized and do work internally. One aspect is that it uses a completely different approach for the scheduling of tasks with different priorities. A task in VxWorks more or less corresponds to what is known as a thread in other operating systems, but the operating system will not assign any CPU-cycles to a low priority task if another, higher priority task is ready to run. This could lead to a problematic scenario called "priority inversion problem". You may read more details about priority inversion on wikipedia:
http://en.wikipedia.org/wiki/Priority_inversion

The effect of a priority inversion situation is that tasks could deadlock, when competing for a resource: The classical example is a low priority task T1 owning a semaphore. Now T1 is prempted by a higher priority task T2, it doesn't get any cpu-cylces assigned anymore. Eventually an even higher priority Task T3 prempts T2 and tries to get the ownership of the semaphore. This results in a low priority task (T2) blocking a high priority task (T3), potentially deadlocking all three tasks. This obviously is a situation to be avoided and this can be done via a mechanism called "priority inversion protocol". Basically the OS temporarily boosts the priority of a low priority task owning a semaphore to the priority of the task with the highest priority trying to aquire this semaphore.

Operating modes

We are talking here about the actual Version of VxWorks, for the timing being this is V6.9. Much older versions like the V5.x branch are being left aside as they are very outdated and the libraries that come bundled with it are partially not standard conformant, making it impossible to use boost. The current version of VxWorks can execute software in two modes. You can compile your project as a DKM (Downlodable Kernel Module, sort of a driver in Windows terms) or as a RTP (Real Time Process, sort of a normal program in Windows terms). For both modes different behaviour, available functionality and default values are present, keep that in mind for the next couple of minutes.

POSIX-conformance

Boost internally comes mainly in two flavors, a Windows implementation and a POSIX implementation. VxWorks in itself is not a POSIX-compliant operating system. Instead it implements the POSIX functionality via a compatibility layer (sort of a wrapper, converting POSIX function calls to native OS calls).

Now, let's take a look at what the standard says about the properties of POSIX-semaphores (see http://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread.h.html):
When creating a mutex (i. e. a semaphore) via a call to pthread_mutex_init() one is allowed to specify NULL as the pointer value for the mutex attributes. For this case the standard specifies "If attr is NULL, the default mutex attributes are used". And about the values of the default is only says they are "defined by the implementation". So, then what are the default values for VxWorks? Actually, they are different for DKM's and RTP's. When we take a closer look at the attributes, POSIX gives you the option to choose the priority protocol to use via a call to pthread_mutexattr_setprotocol(). The possible options are PTHREAD_PRIO_NONE, PTHREAD_PRIO_INHERIT and PTHREAD_PRIO_PROTECT. PTHREAD_PRIO_NONE switches the priority inversion protocol off, whereas the others choose different flavors of priority inversion protocols. As a default, in VxWorks DKM's use PTHREAD_PRIO_INHERIT (which should be fine) and RTP's use PTHREAD_PRIO_NONE. The latter has the advantage to offer the best performance but potentially makes RTP's susceptible for the priority inversion problem.

Usage of Boost

When taking a look at the implementation of the Boost libraries you will quickly find out that a couple of them do use mutexes. And as universal libraries by definition you cannot predict the way a user will use them. A striking example is the shared_ptr. Naturally, the implementation uses a mutex to control access to the reference counter and the makers of shared_ptr can't (and won't) make any assumptions about the way a shared_ptr will be used. A more than likely scenario is the access to heap objects from different tasks with different priorities. It's plain to see that this scenario is prone to the priority inheritance problem. What we can conclude from this is that to be on the safe side we must assume the worst case and all mutexes internally used by Boost should adhere to the priority inversion protocol.
Almost all mutex-instances used by Boost do use the default values, which at glance appears logical - it's the OS' resposibility to choose the proper default! But here's a little dilemma: What is a proper value depends on what you're trying to do, the ball is passed back to Boost from the OS. Now you could pass it over from Boost to the user: Boost can't know what is a proper value, it depends on what the user tries to do! I think you got the problem, right?
So, how will we get out of this dilemma (or is it even a tri-lemma)? Three possibilities:

  • Implement and interface, allowing the user to choose the proper value for his/her application. This is not really an option! It will bleed out deeply burried implementation details to the user, resulting in unportable, error prone code and it force the user to study mutex details for a specific platform - whereas he just wanted to use e.g. a shared_ptr - Yuk!
  • Change the implementation of all Boost libraries to use mutexes with non-default attributes. This would be viable and would not interfere with the non-Boost part of a user application. But it has the disadvantage to be very fragile. If with a new version of Boost all in a sudden a library starts using mutexes the priority inversion problem will resurrect every once in a while.
  • Change the operating system to use other defaults. This may at first sound odd for users of a closed source system, but VxWorks in its latest versions luckily comes with the source code included and the possibility to compile a new kernel adapted to your needs. So, this has a lot of advantages, but it may interfere with the user's code (e. g. when he's using POSIX-mutexes with default values). Anyway, this should not pose a major problem, obeying the priority inheritance protocol does not break anything, it just puts on some additional burden on the task-scheduler where it wouldn't be neccessary. But if utmost performance and timing accuracy where a mission cirtical point for your application you would prabably not be using the heap and Boost, right?
Note: See TracWiki for help on using the wiki.