-
Notifications
You must be signed in to change notification settings - Fork 1
Extras
The library provides some miscellaneous functions and classes that don't relate to concurrency. They are available by including the vwpp.h header file in your source.
NOTE: Starting with v2.7, we introduced a nested, version namespace to enforce matching APIs when building and deploying. In the following code examples, we refer to the namespace as "VWPP". This translates to vwpp for pre-2.7 libraries and to vwpp::v2_7 for v2.7.
VWPP::Uncopyable()Many of the classes in this library derive from Uncopyable which renders a class unable to be copied in any way. It is recommended that you use private inheritance with this class.
VWPP::NoHeap()Classes derived from this class cannot be created on the heap. The library uses this to ensure mutexes and locks are only created globally, in function scope[^1] or inside an object (i.e. it lets the compiler manage the object's lifetime.) Creating mutexes and locks on the heap would break object lifetime semantics and would enable improper usage, resource leaks, deadlocks, etc. NoHeap constrains these objects to proper use. It is recommended that you use private inheritance with this class.
VWPP::ms_to_tick(int)This is a function that converts milliseconds to VxWorks OS ticks. The resulting value can be passed to the various VxWorks functions that take a tick count. If WAIT_FOREVER is specified, WAIT_FOREVER is returned. A negative value results in 0 being returned. Any other value will get rounded to the nearest tick (based on the current tick rate returned from sysClkRateGet().)
LIKELY()UNLIKELY()Modern processors and compilers use "branch prediction" algorithms which try to guess the most chosen path to minimize the cost of branch instructions. Some processors allow their branch instructions to be annotated with the path that happens most often. These macros generate those annotations to allow the programmer to tell the compiler which branch is more likely.
To use them, simply wrap the condition expression in an if-statement. For instance, a counter from 0 to 99 that then wraps back to 0 could be written like this:
if (UNLIKELY(++counter >= 100))
counter = 0;This tells the compiler that resetting the counter is not done often so the branch should be optimized to skip it.
The general consensus of the GNU project (this is a GNU compiler feature) is that it's better to write your code without branching, if possible. For instance, the previous example will always be faster if it is written:
counter = (counter + 1) % 100;VWPP::optimizer_barrier()The optimizer in the GNU compiler will interleave memory access operations to keep the processor's subsystems busy. This function creates a barrier for the optimizer so that it emits code to complete all reads and writes before the barrier and start new reads and writes after it. The VME::Memory class uses this function internally to make sure hardware accesses are done in the correct order. This function isn't much use unless you are doing very low-level hardware interaction.
VXPP_MEMORY_SYNCVXPP_INSTRUCTION_SYNCVXPP_ALL_SYNCVWPP::memory_sync()VWPP::instruction_sync()VWPP::global_sync()Modern processors use various techniques to greatly improve their execution speed. Instruction pipelines allow several instructions to be handled in parallel and reordering memory accesses helps keep the pipeline from stalling. In some cases, however, these optimizations can break an algorithm. Specifically, if a memory address maps to hardware registers, then reordering the access can place the hardware in an unknown state.
These macros[^2] allow you to turn off some of these CPU optimizations.
The VXPP_MEMORY_SYNC macro and memory_sync() function tell the CPU to finish all its memory I/O before continuing. (For PowerPC, they emit the eieio instruction.)
The VXPP_INSTRUCTION_SYNC macro and instruction_sync() function tell the CPU to finish all in-progress instructions before proceeding (i.e. finish executing the instructions in the pipeline.) This is a fairly expensive operation and shouldn't be used unless it's absolutely necessary. Usually VXPP_MEMORY_SYNC/memory_sync() is enough to force the correct behavior. (For PowerPC, they emit the isync instruction.)
The VXPP_ALL_SYNC macro and global_sync() function prevent the CPU pipeline from doing loads/stores and instruction fetches until all previous activity is complete. This is the most expensive operation and shouldn't be used unless it's absolutely necessary. (For PowerPC, they emit the sync instruction.)
It is interesting to note that C++11 introduced a portable way to express VXPP_MEMORY_SYNC:
atomic_signal_fence(memory_order_acq_rel);As our compilers become more modern, we may deprecate VXPP_MEMORY_SYNC.
VWPP::timeout_error(char const* msg = 0)This exception class can be used to indicate there was a timeout. All functions in this library that take a timeout parameter will throw this exception if the timeout occurs. This class is derived from std::runtime_error so it can also be caught in a run-time, catch-all clause.
[^1]: Defining a mutex in function scope isn't useful at all. However, it'll get cleaned up correctly and there's no way to detect this usage in C++, so we have to live with it.
[^2]: On PowerPC platforms, these macros emit EIEIO and SYNC instructions. When we support other processor families, these macros will adjust accordingly.