Porting Guide (liblfds)
Introduction
To permit liblfds to work on a range of platforms a porting abstraction layer has been written. Porting simply involves implementing the porting abstraction layer; the library will then compile and work. Implementation involves providing values to a small set defines, macros and typedefs.
The Porting Abstraction Layer
Each of the compiler, the operating system and the processor turns out to be a source of information for a porting abstraction layer and so the porting abstraction layer is arranged as three header files, one for each, thus;
└───liblfds710 └───inc └───liblfds710 lfds710_porting_abstraction_layer_compiler.h lfds710_porting_abstraction_layer_operating_system.h lfds710_porting_abstraction_layer_processor.h
Each header file uses #ifdefs and compiler defined macros to select the appropriate porting abstraction layer implementation for the local platform.
So, for example, in lfds710_porting_abstraction_layer_compiler.h, for MSVC version 1400 and higher, we see the following;
#if( defined _MSC_VER && _MSC_VER >= 1400 ) [snip porting abstraction layer implementation] #endif
Accordingly, to add a new platform, introduce a new #ifdef, which matches the appropriate compiler defined macros for your platform.
lfds710_porting_abstraction_layer_compiler.h
#define LFDS710_PAL_COMPILER_STRING #define LFDS710_PAL_ALIGN( alignment ) #define LFDS710_PAL_INLINE #define LFDS710_PAL_BARRIER_COMPILER_LOAD #define LFDS710_PAL_BARRIER_COMPILER_STORE #define LFDS710_PAL_BARRIER_COMPILER_FULL #define LFDS710_PAL_BARRIER_PROCESSOR_LOAD #define LFDS710_PAL_BARRIER_PROCESSOR_STORE #define LFDS710_PAL_BARRIER_PROCESSOR_FULL #define LFDS710_PAL_ATOMIC_ADD( pointer_to_target, value, result, result_type ) #define LFDS710_PAL_ATOMIC_CAS( pointer_to_destination, pointer_to_compare, new_destination, cas_strength, result ) #define LFDS710_PAL_ATOMIC_DWCAS( pointer_to_destination, pointer_to_compare, pointer_to_new_destination, cas_strength, result ) #define LFDS710_PAL_ATOMIC_EXCHANGE( pointer_to_destination, pointer_to_exchange ) #define LFDS710_PAL_ATOMIC_SET( pointer_to_destination, exchange )
lfds710_porting_abstraction_layer_operating_system.h
#define LFDS710_PAL_OS_STRING #define LFDS710_PAL_ASSERT( expression )
lfds710_porting_abstraction_layer_processor.h
typedef [type] lfds710_pal_int_t; typedef [type] lfds710_pal_uint_t; #define LFDS710_PAL_PROCESSOR_STRING #define LFDS710_PAL_ALIGN_SINGLE_POINTER #define LFDS710_PAL_ALIGN_DOUBLE_POINTER #define LFDS710_PAL_ATOMIC_ISOLATION_IN_BYTES
Partial Implementatons
It is not necessary to fully implement a porting abstraction layer; very little in the porting abstraction layer is mandatory, and everything non-mandatory has a dummy version provided, for the purpose of allowing the library to compile even if that particular part of the porting abstraction layer has not been implemented or is not available on the platform (which is necessary anyway, as not all atomic operations are available on all processors). Naturally, if a data structure depends on a given part or parts of the porting abstraction layer and those parts have not been implemented, then that data structure will not work.
Atomic Operation Support by Processor | ||||||
---|---|---|---|---|---|---|
Memory Barriers |
Add | CAS | DWCAS | Exchange | Set | |
ARM32/64, x86, x64 | ||||||
Alpha, Itanium, MIPS32/64, PowerPC32/64, SPARC32/64 |
The data structures have the following requirements;
Data Structure by Atomic Operations | ||||||
---|---|---|---|---|---|---|
Memory Barriers |
Add | CAS | DWCAS | Exchange | Set | |
Binary Tree (add-only, unbalanced) | ||||||
Freelist | ||||||
Hash (add-only) | ||||||
List (add-only, ordered, singly-linked) | ||||||
List (add-only, singly-linked, unordered) | ||||||
PRNG | ||||||
Queue, bounded, single producer, single consumer | ||||||
Queue, bounded, many producer, many consumer | ||||||
Queue, unbounded, many producer, many consumer | ||||||
Ringbuffer | ||||||
Stack |
Which means you end up with the following;
Data Structures by Processors | ||
---|---|---|
ARM32/64, x86, x64 | Alpha, Itanium, MIPS32/64, PowerPC32/64, SPARC32/64 | |
Binary Tree (add-only, unbalanced) | ||
Freelist | ||
Hash (add-only) | ||
List (add-only, ordered, singly-linked) | ||
List (add-only, singly-linked, unordered) | ||
PRNG | ||
Queue, bounded, single producer, single consumer | ||
Queue, bounded, many producer, many consumer | ||
Queue, unbounded, many producer, many consumer | ||
Ringbuffer | ||
Stack |
So, for example, if you port to some new x86 based platform and implement only memory barriers and DWCAS, then you can use the bounded, single producer, single consumer queue, the unbounded queue, the ringbuffer and the stack.
The library will compile fully, but if you try to use the other data structures, you will call a dummy version of one of the other atomic operations, where those dummy functions try to write 0 to memory location 0.