Porting Guide (liblfds)
Introduction
To permit liblfds to work on a range of platforms a porting abstraction layer has been written. Porting means to implement the porting abstraction layer for the new platform; the library will then compile and work. Implementating means 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;
└───liblfds711 └───inc └───liblfds711 lfds711_porting_abstraction_layer_compiler.h lfds711_porting_abstraction_layer_operating_system.h lfds711_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 lfds711_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.
lfds711_porting_abstraction_layer_compiler.h
#define LFDS711_PAL_COMPILER_STRING #define LFDS711_PAL_ALIGN( alignment ) #define LFDS711_PAL_INLINE #define LFDS711_PAL_BARRIER_COMPILER_LOAD #define LFDS711_PAL_BARRIER_COMPILER_STORE #define LFDS711_PAL_BARRIER_COMPILER_FULL #define LFDS711_PAL_BARRIER_PROCESSOR_LOAD #define LFDS711_PAL_BARRIER_PROCESSOR_STORE #define LFDS711_PAL_BARRIER_PROCESSOR_FULL #define LFDS711_PAL_ATOMIC_ADD( pointer_to_target, value, result, result_type ) #define LFDS711_PAL_ATOMIC_CAS( pointer_to_destination, pointer_to_compare, new_destination, cas_strength, result ) #define LFDS711_PAL_ATOMIC_DWCAS( pointer_to_destination, pointer_to_compare, pointer_to_new_destination, cas_strength, result ) #define LFDS711_PAL_ATOMIC_EXCHANGE( pointer_to_destination, pointer_to_exchange ) #define LFDS711_PAL_ATOMIC_SET( pointer_to_destination, exchange )
lfds711_porting_abstraction_layer_operating_system.h
#define LFDS711_PAL_OS_STRING #define LFDS711_PAL_ASSERT( expression )
lfds711_porting_abstraction_layer_processor.h
typedef [type] lfds711_pal_int_t; typedef [type] lfds711_pal_uint_t; #define LFDS711_PAL_PROCESSOR_STRING #define LFDS711_PAL_ALIGN_SINGLE_POINTER #define LFDS711_PAL_ALIGN_DOUBLE_POINTER #define LFDS711_PAL_ATOMIC_ISOLATION_IN_BYTES
Partial Implementatons
It is not necessary to fully implement a porting abstraction layer. Only part, and the simple part, of the porting layer is mandatory. Dummy versions are provided for everything non-mandatory, so the library will compile and work once that which is mandatory has been provided.
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 cannot work.
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 |
The atomic operations above map to the following porting layer macros;
Atomic Operations | ||||||
---|---|---|---|---|---|---|
Memory Barriers | LFDS711_PAL_BARRIER_PROCESSOR_LOAD LFDS711_PAL_BARRIER_PROCESSOR_STORE LFDS711_PAL_BARRIER_PROCESSOR_FULL |
|||||
Add | LFDS711_PAL_ATOMIC_ADD | |||||
CAS | LFDS711_PAL_ATOMIC_CAS | |||||
DWCAS | LFDS711_PAL_ATOMIC_DWCAS | |||||
Exchange | LFDS711_PAL_ATOMIC_EXCHANGE | |||||
Set | LFDS711_PAL_ATOMIC_SET |
So, for example, if you port to some new 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 intentionally try to write 0 to memory location 0, so you notice the error of your ways.