Simple TU initialization and cleanup handling with dependencies. More...
#include <stdlib.h>
#include <threads.h>
Go to the source code of this file.
Macros | |
#define | _ONCE_AT_QUICK_EXIT_I(NAME, ...) |
#define | _ONCE_ATEXIT_I(NAME, ...) |
#define | _ONCE_DECLARE(NAME) |
#define | _ONCE_DEFINE_I(NAME, ...) |
#define | _ONCE_DEFINE_STRONG_I(NAME, ...) |
#define | _ONCE_DEPEND_I(NAME, ...) |
#define | _ONCE_DEPEND_WEAK_I(NAME, ...) |
#define | ONCE_AT_QUICK_EXIT(...) _ONCE_AT_QUICK_EXIT(__VA_ARGS__) |
Define optional cleanup code that goes with feature named NAME for quick_exit | |
#define | ONCE_ATEXIT(...) _ONCE_ATEXIT(__VA_ARGS__) |
Define optional cleanup code that goes with feature named NAME for atexit | |
#define | ONCE_DEFINE(...) _ONCE_DEFINE(__VA_ARGS__) |
Define the initialization code that goes with feature named NAME | |
#define | ONCE_DEFINE_STRONG(...) _ONCE_DEFINE_STRONG(__VA_ARGS__) |
Similar to ONCE_DEFINE but triggers unconditionally if the platform supports this. | |
#define | ONCE_DEPEND(...) _ONCE_DEPEND(__VA_ARGS__) |
Declare that this TU depends on a initialization and cleanup feature named NAME | |
#define | ONCE_DEPEND_WEAK(...) _ONCE_DEPEND_WEAK(__VA_ARGS__) |
Similar to ONCE_DEPEND but only triggers if ONCE_DEFINE_STRONG is not able to guarantee unconditional initialization. | |
Internal macros | |
#define | _ONCE_AT_QUICK_EXIT(...) _ONCE_AT_QUICK_EXIT_I(__VA_ARGS__ __VA_OPT__(,) __UNIT__,) |
#define | _ONCE_AT_QUICK_EXIT_CALLBACK(NAME) NAME ∷_Once∷at_quick_exit∷callback |
#define | _ONCE_AT_QUICK_EXIT_POINTER(NAME) NAME ∷_Once∷at_quick_exit∷pointer |
#define | _ONCE_ATEXIT(...) _ONCE_ATEXIT_I(__VA_ARGS__ __VA_OPT__(,) __UNIT__,) |
#define | _ONCE_ATEXIT_CALLBACK(NAME) NAME ∷_Once∷atexit∷callback |
#define | _ONCE_ATEXIT_POINTER(NAME) NAME ∷_Once∷atexit∷pointer |
#define | _ONCE_CALLBACK(NAME) NAME ∷_Once∷init∷callback |
#define | _ONCE_DEFINE(...) _ONCE_DEFINE_I(__VA_ARGS__ __VA_OPT__(,) __UNIT__,) |
#define | _ONCE_DEFINE_STRONG(...) _ONCE_DEFINE_STRONG_I(__VA_ARGS__ __VA_OPT__(,) __UNIT__,) |
#define | _ONCE_DEPEND(...) _ONCE_DEPEND_I(__VA_ARGS__ __VA_OPT__(,) __UNIT__,) |
#define | _ONCE_DEPEND_WEAK(...) _ONCE_DEPEND_WEAK_I(__VA_ARGS__ __VA_OPT__(,) __UNIT__,) |
#define | _ONCE_DOTTY_ARC(A, B) "ONCE_DEPEND_MARKER: " A " -> " B |
#define | _ONCE_DOTTY_FILE __FILE__ |
#define | _ONCE_DOTTY_NODE(A, ...) "ONCE_DEPEND_MARKER: " A " [" #__VA_ARGS__ "]" |
#define | _ONCE_INIT(NAME) NAME ∷_Once∷init |
#define | _ONCE_IS_STRONG extern |
#define | _ONCE_STRONG_FLAG(NAME) NAME ∷_Once∷strong |
#define | _ONCE_STRONG_INIT |
#define | _ONCE_USER(NAME) NAME ∷_Once∷init∷user |
Simple TU initialization and cleanup handling with dependencies.
This is a header only library as a small wrapper around call_once
and atexit
. It has just a handful user interfaces. For triggered dependencies these are
For unconditional dependencies (that should always run) there are
To fully work ONCE_DEFINE_STRONG
needs compiler magic, here we use a gnu feature. ONCE_DEPEND_WEAK
then triggers a dependency just as ONCE_DEPEND
but only if running code unconditionally at startup is not supported. Otherwise this is a no-op.
Currently there is no detection of dependency cycles, but a graph can be extracted from the strings of the binary. It should only have cycles between a symbolic dependency and the TU in which is found.
There is a script called dot_extract.sh
that filters the string of the executable to obtain the graph, which basically looks as follows:
#define _ONCE_AT_QUICK_EXIT | ( | ... | ) | _ONCE_AT_QUICK_EXIT_I(__VA_ARGS__ __VA_OPT__(,) __UNIT__,) |
#define _ONCE_AT_QUICK_EXIT_CALLBACK | ( | NAME | ) | NAME ∷_Once∷at_quick_exit∷callback |
#define _ONCE_AT_QUICK_EXIT_I | ( | NAME, | |
... | |||
) |
#define _ONCE_AT_QUICK_EXIT_POINTER | ( | NAME | ) | NAME ∷_Once∷at_quick_exit∷pointer |
#define _ONCE_ATEXIT | ( | ... | ) | _ONCE_ATEXIT_I(__VA_ARGS__ __VA_OPT__(,) __UNIT__,) |
#define _ONCE_ATEXIT_CALLBACK | ( | NAME | ) | NAME ∷_Once∷atexit∷callback |
#define _ONCE_ATEXIT_I | ( | NAME, | |
... | |||
) |
#define _ONCE_ATEXIT_POINTER | ( | NAME | ) | NAME ∷_Once∷atexit∷pointer |
#define _ONCE_CALLBACK | ( | NAME | ) | NAME ∷_Once∷init∷callback |
#define _ONCE_DECLARE | ( | NAME | ) |
#define _ONCE_DEFINE | ( | ... | ) | _ONCE_DEFINE_I(__VA_ARGS__ __VA_OPT__(,) __UNIT__,) |
#define _ONCE_DEFINE_I | ( | NAME, | |
... | |||
) |
#define _ONCE_DEFINE_STRONG | ( | ... | ) | _ONCE_DEFINE_STRONG_I(__VA_ARGS__ __VA_OPT__(,) __UNIT__,) |
#define _ONCE_DEFINE_STRONG_I | ( | NAME, | |
... | |||
) |
#define _ONCE_DEPEND | ( | ... | ) | _ONCE_DEPEND_I(__VA_ARGS__ __VA_OPT__(,) __UNIT__,) |
#define _ONCE_DEPEND_I | ( | NAME, | |
... | |||
) |
#define _ONCE_DEPEND_WEAK | ( | ... | ) | _ONCE_DEPEND_WEAK_I(__VA_ARGS__ __VA_OPT__(,) __UNIT__,) |
#define _ONCE_DEPEND_WEAK_I | ( | NAME, | |
... | |||
) |
#define _ONCE_DOTTY_ARC | ( | A, | |
B | |||
) | "ONCE_DEPEND_MARKER: " A " -> " B |
#define _ONCE_DOTTY_NODE | ( | A, | |
... | |||
) | "ONCE_DEPEND_MARKER: " A " [" #__VA_ARGS__ "]" |
#define _ONCE_INIT | ( | NAME | ) | NAME ∷_Once∷init |
#define _ONCE_IS_STRONG extern |
#define _ONCE_STRONG_FLAG | ( | NAME | ) | NAME ∷_Once∷strong |
#define _ONCE_STRONG_INIT |
#define _ONCE_USER | ( | NAME | ) | NAME ∷_Once∷init∷user |
#define ONCE_AT_QUICK_EXIT | ( | ... | ) | _ONCE_AT_QUICK_EXIT(__VA_ARGS__) |
Define optional cleanup code that goes with feature named NAME
for quick_exit
Use this as a prefix to a function definition such as in
This creates some symbols with internal linkage in the current TU. For this to work ONCE_DEFINE(toto)
has to be defined in the same TU. The other way around, this is not mandatory: ONCE_DEFINE(toto)
can work without having ONCE_AT_QUICK_EXIT(toto)
.
Technically this creates a functions and a function pointer is then used with at_quick_exit
. All implicitly defined symbols have internal linkage.
#define ONCE_ATEXIT | ( | ... | ) | _ONCE_ATEXIT(__VA_ARGS__) |
Define optional cleanup code that goes with feature named NAME
for atexit
Use this as a prefix to a function definition such as in
This creates some symbols with internal linkage in the current TU. For this to work ONCE_DEFINE(toto)
has to be defined in the same TU. The other way around, this is not mandatory: ONCE_DEFINE(toto)
can work without having ONCE_ATEXIT(toto)
.
Technically this creates a functions and a function pointer is then used with atexit
. All implicitly defined symbols have internal linkage.
#define ONCE_DEFINE | ( | ... | ) | _ONCE_DEFINE(__VA_ARGS__) |
Define the initialization code that goes with feature named NAME
Use this as a prefix to a function definition such as in
This creates some symbols with internal linkage and one external symbol, but with a naming that is not portable between different implementations.
Technically this creates separate functions and a once_flag
that are then used with call_once
.
Use this, even in the same TU, as ONCE_DEPEND(toto)
to be sure that your initialization code is guaranteed to have run before any of your other code.
#define ONCE_DEFINE_STRONG | ( | ... | ) | _ONCE_DEFINE_STRONG(__VA_ARGS__) |
Similar to ONCE_DEFINE
but triggers unconditionally if the platform supports this.
#define ONCE_DEPEND | ( | ... | ) | _ONCE_DEPEND(__VA_ARGS__) |
Declare that this TU depends on a initialization and cleanup feature named NAME
Use this in block scope to refer to the one external symbol that can be used by other TU to indicate a dependency of this TU here.
The external name is not portable between different implementations.
Under the hood that is a simple function call of a void
function.
This can be placed anywhere where a declaration is allowed.
#define ONCE_DEPEND_WEAK | ( | ... | ) | _ONCE_DEPEND_WEAK(__VA_ARGS__) |
Similar to ONCE_DEPEND
but only triggers if ONCE_DEFINE_STRONG
is not able to guarantee unconditional initialization.
This can be placed anywhere where a declaration is allowed.