List (add-only, singly-linked, ordered)
Source Files
└───liblfds710 ├───inc │ └───liblfds710 │ lfds710_list_addonly_singlylinked_ordered.h └───src └───lfds710_addonly_singlylinked_ordered_list lfds710_list_addonly_singlylinked_ordered_cleanup.c lfds710_list_addonly_singlylinked_ordered_get.c lfds710_list_addonly_singlylinked_ordered_init.c lfds710_list_addonly_singlylinked_ordered_insert.c lfds710_list_addonly_singlylinked_ordered_internal.h lfds710_list_addonly_singlylinked_ordered_query.c
Enums
enum lfds710_list_aso_existing_key; enum lfds710_list_aso_insert_result; enum lfds710_list_aso_query;
Opaque Structures
struct lfds710_list_aso_element; struct lfds710_list_aso_state;
Macros
#define LFDS710_LIST_ASO_GET_START( list_aso_state ) #define LFDS710_LIST_ASO_GET_NEXT( list_aso_element ) #define LFDS710_LIST_ASO_GET_START_AND_THEN_NEXT( list_aso_state, pointer_to_list_aso_element ) #define LFDS710_LIST_ASO_GET_KEY_FROM_ELEMENT( list_aso_element ) #define LFDS710_LIST_ASO_SET_KEY_IN_ELEMENT( list_aso_element, new_key ) #define LFDS710_LIST_ASO_GET_VALUE_FROM_ELEMENT( list_aso_element ) #define LFDS710_LIST_ASO_SET_VALUE_IN_ELEMENT( list_aso_element, new_value ) #define LFDS710_LIST_ASO_GET_USER_STATE_FROM_STATE( list_aso_state )
Prototypes
void lfds710_list_aso_init_valid_on_current_logical_core( struct lfds710_list_aso_state *lasos, int (*key_compare_function)(void const *new_key, void const *existing_key), enum lfds710_list_aso_existing_key existing_key, void *user_state ); void lfds710_list_aso_cleanup( struct lfds710_list_aso_state *lasos, void (*element_cleanup_callback)(struct lfds710_list_aso_state *lasos, struct lfds710_list_aso_element *lasoe) ); enum lfds710_list_aso_insert_result lfds710_list_aso_insert( struct lfds710_list_aso_state *lasos, struct lfds710_list_aso_element *lasoe, struct lfds710_list_aso_element **existing_lasoe ); int lfds710_list_aso_get_by_key( struct lfds710_list_aso_state *lasos, void *key, struct lfds710_list_aso_element **lasoe ); void lfds710_list_aso_query( struct lfds710_list_aso_state *lasos, enum lfds710_list_aso_query query_type, void *query_input, void *query_output );
Overview
This data structure implements an add-only, singly-linked, ordered list. It supports any number of concurrent users, and internally implements exponential backoff to help deal with high load and so improve scalability.
The implementation performs no allocations. The user is responsible for all allocations (and deallocations), where these allocations are passed into the API functions, which then use them. As such, allocations can be on the stack, on the heap, or as can sometimes be the the case in embedded systems, allocated with fixed addresses at compile time from a fixed global store. Allocations can also be shared memory, but in this case, the virtual addresses used must be the same in all processes.
General usage is that the user calls lfds710_list_aso_init_valid_on_current_logical_core to initialize a struct lfds710_list_aso_state, and then calls lfds710_list_aso_insert to add elements to the list and lfds710_list_aso_get_by_key to find elements in the list.
(See the section below, on lock-free specific behaviour, for an explanation of the unusual init function name.)
A list element provides the ability to store a key and a value, both of which are of type void *. The key is used for list ordering. The key and value are get and set by macros, such as LFDS710_LIST_ASO_SET_VALUE_IN_ELEMENT. The key can only be set in elements before they are added to a tree. The value can be set at any time, in elements both inside and outside of the list.
The state and element structures are both public, present in the lfds710_list_addonly_ordered_singlylinked.h header file, so that users can embed them in their own structures (and where necessary pass them to sizeof). Expected use is that user structures which are to enter lists contain within themselves a struct lfds710_list_aso_element, and this is used when calling lfds710_list_aso_insert. This approach permits zero run-time allocation of store and also ensures the stack element is normally in the same memory page as the user data it refers to.
Lock-free Specific Behaviour
The state initialization function, lfds710_list_aso_init_valid_on_current_logical_core, as the same suggests, initializes the state structure but that initialization is only valid on the current logical core. For the initialization to be valid on other logical cores (i.e. other threads where they are running on other logical cores) those other threads need to call the long-windedly named macro LFDS710_MISC_MAKE_VALID_ON_CURRENT_LOGICAL_CORE_INITS_COMPLETED_BEFORE_NOW_ON_ANY_OTHER_LOGICAL_CORE, which will do that which its name suggests.
The SET macro for the key in an element can only be correctly used on elements which are outside of a list. The SET macro for the value in an element can be used at any time, on any element. By correctly is it meant to say that the GET macros will actually read the data written by the SET macros, and not some other data. I don't have to tell you how much chaso will ensure if different logical cores are reading different keys for the same element...
If shared memory is used for allocations, the virtual addresses must be the same across different processes.
White Paper
There is no white paper for this data structure; it is native to liblfds.
License
Standard liblfds license - there is no license. You are free to use this code in any way. Go forth and create wealth!
If however for legal reasons a licence is required, the license of your choice will be granted, and license for convenience is hereby granted up front for a range of popular licenses : the MIT license, the BSD license, the Apache license, the GPL and LPGL (all versions thereof) and the Creative Commons licenses (all of them). Additionally, this library (which is to say, the source code, build files, documentation, everything) is placed in the public domain.
Example
#include <stdio.h> #include <stdlib.h> #include "liblfds710.h" struct test_data { struct lfds710_list_aso_element lasue; lfds710_pal_uint_t key; }; int key_compare_function( void const *new_key, void const *existing_key ) { lfds710_pal_uint_t new_order_number, existing_order_number; new_order_number = *(lfds710_pal_uint_t *) new_key; existing_order_number = *(lfds710_pal_uint_t *) existing_key; if( new_order_number < existing_order_number ) return( -1 ); if( new_order_number > existing_order_number ) return( 1 ); return( 0 ); } int main() { enum lfds710_list_aso_insert_result ir; struct lfds710_prng_state ps; struct lfds710_list_aso_element *lasoe; struct lfds710_list_aso_state lasos; struct test_data *td; int unsigned loop; lfds710_list_aso_init_valid_on_current_logical_core( &lasos, compare_function, LFDS710_LIST_ASO_INSERT_RESULT_FAILURE_EXISTING_KEY, NULL ); lfds710_prng_init_valid_on_current_logical_core( &ps, LFDS710_PRNG_SEED ); td = malloc( sizeof(struct test_data) * 10 ); for( loop = 0 ; loop < 10 ; loop++ ) { LFDS710_PRNG_GENERATE( &ps, &td[loop].key ); LFDS710_LIST_ASO_SET_KEY_IN_ELEMENT( td[loop].lasoe, &td[loop].key ); LFDS710_LIST_ASO_SET_VALUE_IN_ELEMENT( td[loop].lasoe, &td[loop] ); // TRD: assuming no duplicate keys will be generated, so not checking the return ir = lfds710_list_aso_insert( &lasos, &lasoe, NULL ); } // TRD : now read them out - they should now be in ascending numeric order lasoe = NULL; while( LFDS710_LIST_ASO_GET_START_AND_THEN_NEXT(&lasos,lasoe) ) { td = LFDS710_LIST_ASO_GET_VALUE_FROM_ELEMENT( *lasoe ); printf( "ordered number = %llu\n", (int long long unsigned) td->key ); } lfds710_list_aso_cleanup( &lasos, NULL ); free( td ); return( EXIT_SUCCESS ); }