API changes

One of the to-do’s was to reorder the hash API arguments.

This has pulled on a mental thread and led me to remove all the key and value arguments, where-ever they’re present.

So, for example, before we might have had;

enum lfds700_btree_au_link_result lfds700_btree_au_link_element( struct lfds700_btree_au_state *baus,
struct lfds700_btree_au_element *baue,
void *key,
void *value,
int (*key_compare_function)(void *new_key, void *existing_key, void *user_state),
struct lfds700_btree_au_element **existing_baue,
struct lfds700_liblfds_prng_state *ps );

The key and value arguments are convenience arguments – the function sets them into *baue. However, the user can do this – and there are prototypes where the presence of those key and value arguments makes the prototypes ungainly and indeed more complicated.

So now this has been removed, and all the functions now simply deal with data structure elements, pointers to them, and the user uses macros to get/set keys and values.

C ternary operator ‘strangeness’

The code below does not compile; “the left side of the assignment operator must be an l-value” (this for the right-most assignment operator).

int cr = 1;
cr == 5 ? cr = 2 : cr = 3;

The code below does compile;

int cr = 1;
cr == 5 ? (cr = 2) : (cr = 3);

This is true for both MSVC and GCC.

Turns out to be an apparently common compiler issue.


However, many C compilers use non-standard expression grammar where ?: is designated higher precedence than =, which parses that expression as e = ( ((a < d) ? (a++) : a) = d ), which then fails to compile due to semantic constraints: ?: is never lvalue and = requires a modifiable lvalue on the left. This is the table presented on this page.

Pulling on a thread (thread – see? geddit? see what I did there?)

Sometimes, the only thing to say is this : gaaaaaaaaaaaaaaaaaaaaaahhhhhhhhhhh!!!

So, I had an innocent little thing on my small to-do list; “lfds700_list_asu_get_start_and_then_next() should be a macro, not a function”.

See, the lists have this convenience function, for iterating over the list. It takes the list state and a pointer to a list element, which is initialized before calling the function to NULL. If the pointer to the list element is NULL, then it is set to point to the first element in the list, otherwise, it is moved to point to the element after itself.

So you use it like this;

struct lfds700_asu_element
*le = NULL;

while( lfds700_list_asu_get_start_and_then_next(ls, &le) )
// TRD : do work

The problem of course is you’re making a function call per iteration, which is totally not okay.

Now, there exist already a pair of macros, LFDS700_LIST_ASU_GET_START and LFDS700_LIST_ASU_GET_NEXT. That function uses them.

Now, given the expected usage, inside a while loop, we cannot in the macro version of the function use curley braces.

Problem is, those two macros, they both issue a load barrier – and that load barrier, on some platforms, is an atomic exchange – and that means we need some store, two temporary variables, and to get them… we’re using curley braces.

In fact this work touches upon this larger issue – I would like to be able to use all these macros inside while() brackets.

Exchange is not the only atomic operation which currently needs store – given the normalized form I’ve selected for CAS and DCAS, some platforms need store for those ops as well.

The only way I can see to get round this is to have these macros as inline functions.

That means changing the liblfds header files to be C files, and #including them rather than the header files.