Thread-safe one-time initialization
Feature or enhancement
Some CPython internals require initialization exactly once. Some of these one time initializations are not thread-safe without the GIL or have data races according to the C11 memory model.
We should add a lightweight, thread-safe one-time initialization API similar to C++11's std::call_once 1. The proposed internal-only API follows C++11's std::call_once, but adapted for C (i.e., error returns and function pointers):
typedef struct { uint8_t v; } _PyOnceFlag; typedef int _Py_once_fn_t(void *arg); // Calls `fn` once using `flag`. The `arg` is passed to the call to `fn`. // // Returns 1 on success and 0 on failure. // // If `fn` returns 1 (success), then subsequent calls immediately return 1. // If `fn` returns 0 (failure), then subsequent calls will retry the call. int _PyOnceFlag_CallOnce(_PyOnceFlag *flag, _Py_once_fn_t *fn, void *arg);
As an example, the Python-ast.c relies on the GIL and an initialized variable to ensure that it is only initialized once:
| static int | |
| init_types(struct ast_state *state) | |
| { | |
| // init_types() must not be called after _PyAST_Fini() | |
| // has been called | |
| assert(state->initialized >= 0); | |
| if (state->initialized) { | |
| return 1; | |
| } |
Linked PRs
-
Also,
pthread_onceand C11'scall_once.std::call_oncesupports error returns, which is important for CPython's use cases. ↩