Class UniqueIDManager

Class Documentation

class UniqueIDManager

A thread-safe generator of sequential unique ID numbers.

Public Functions

inline UniqueIDManager()

Construct a UniqueIDManager, with the first ID it hands out being mpiRank() * mpiUidStride (0, when this build was not compiled with SLUG_MPI, or was compiled with it but is running as a single rank)

Seeding the very first ID from this process’s own MPI rank (rather than always 0) is what keeps every rank’s own live-generated IDs permanently non-overlapping for the whole run – see mpiUidStride’s own comment. Safe to call this early even under MPI: this is a function-local static (see uniqueID() below), so it is not actually constructed until the first call to uniqueID() itself, well after main()’s own MPIGuard has already initialized MPI.

~UniqueIDManager() = default
UniqueIDManager(const UniqueIDManager&) = delete
UniqueIDManager(UniqueIDManager&&) = delete
auto operator=(const UniqueIDManager&) -> UniqueIDManager& = delete
auto operator=(UniqueIDManager&&) -> UniqueIDManager& = delete
inline auto get() -> unsigned long

Return the next unique ID.

This routine properly handles inter-thread synchronization when openMP is enabled: uniqueID_ is read and incremented as a single atomic operation, so no two calls – whether from the same thread or different ones – can ever be handed the same value.

Returns:

An ID that has never been, and will never again be, returned by any other call to get() on this object – so long as set() is never used to rewind uniqueID_ back past a value already handed out this way (see set()’s own comment for the one caller that does use it, and why that caller’s own use is still safe)

inline void set(const unsigned long value)

Set the next ID get() will hand out.

Atomic, like get() itself, but this alone does not make calling this concurrently with get() meaningful – which of the two “wins” (i.e. whether a get() racing this set() sees the value before or after it) is unspecified. The one caller that actually uses this, OutputManagerH5::restartSetup(), only ever calls it once, on a single thread, before any thread has had a chance to call get()

again (mirroring the same “only ever called from

outside any active parallel region” rule checkpoint() itself relies on – see its own comment) – restoring uniqueID_ to the “restart_uid” value read back from the checkpoint being resumed from, i.e. the value

get() was about to hand out next when that checkpoint was closed, so this process’s own IDs continue exactly where the run being resumed left off. Any IDs a since-abandoned, never-fully-written checkpoint’s own in-flight trials had already been handed simply become gaps in the sequence, not reused – get()

’s own “never

returns the same value twice” guarantee still holds.

Parameters:

value – The next ID get() should return

inline auto read() const -> unsigned long

Return the next ID get() would hand out, without handing it out.

Atomic, like get() itself. Used by OutputManagerH5::closeOutputFile() to record the next ID about to be handed out as each checkpoint’s own “restart_uid” attribute, without itself consuming that ID (calling get() instead would waste one ID per checkpoint for no reason, and – worse – record the ID after the one actually still next, since get()’s own increment would already have run by the time its return value was written out).

Returns:

The value get() would return if called right now – i.e. get() without its own increment