Porting Guide (liblfds)

From liblfds.org
Jump to navigation Jump to search

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.

See Also