Skip to content

The future of global function pointers #288

@wegank

Description

@wegank

msolve currently depends on mutable global function pointers for algorithm dispatches. An example is

msolve/src/neogb/data.h

Lines 447 to 451 in ff6fb15

extern int (*initial_input_cmp)(
const void *a,
const void *b,
void *ht
);

Here, depending on the value of st->ht, the functions {re,}set_function_pointers set the global pointers to one of initial_input_cmp_{be,lex,drl}.

msolve/src/neogb/io.c

Lines 886 to 888 in ff6fb15

void set_function_pointers(
const md_t *st
)

However, one may quickly realize that the approach is, by design, incompatible with multi-threading:

  1. To compute one Gröbner basis over QQ with multiple threads, during the secondary modular phase, {re,}set_function_pointers may simultaneously modify a same address, or other functions can access the address on the same time: even if the data is the same, this remains absolutely UB.

This has become a real problem in recent weeks, and is now prohibited in #279. However, this means that

  1. To compute one Gröbner basis over QQ with multiple threads, it is impossible to choose prime numbers with radically different bitsizes.

What's worse, with or without #279,

  1. It is impossible to simultaneously compute two Gröbner bases, one over QQ and the other over small finite fields, using different threads of the same process.
    A reproducer in Julia can be easily adapted from Parallel computation on multiple threads causes segfaults AlgebraicSolving.jl#111; for a reproducer in C++, simply ask your favourite LLM.

To resolve these issues, the simplest solution is to point these global function pointers to immutable methods that perform the dispatches internally. However, this will change the C interface: for example, initial_input_cmp does not have md_t *st as an argument, so it does not know st->mo for dispatch unless we duplicate it in void *ht (see #289). Furthermore, since the dispatching must be done on each call, the performance impact remains uncertain.

Another solution is to make function pointers local to each thread, but this will obviously involve a major refactor and will further modify the C interface.

Let me know what you think.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions