Flow 3.0.0
Flow project: Full implementation reference.
uniq_id_holder.cpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
20#include <boost/functional/hash/hash.hpp>
21#include <boost/io/ios_state.hpp>
22#include <iomanip>
23#include <type_traits>
24
25namespace flow::util
26{
27
28// Unique_id_holder implementations.
29
31 m_id(create_unique_id())
32{
33 // Nothing else.
34}
35
37 Unique_id_holder() // This (unusual) behavior is explained in class doc header. Basically new obj = separate obj.
38{
39 // Nothing else.
40}
41
43{
44 // Intentionally blank. This (unusual) behavior is explained in class doc header.
45 return *this;
46}
47
49{
50 return m_id;
51}
52
54{
55 using std::atomic;
56 using std::memory_order_relaxed;
57
58 /* Get the next available ID in the universe. Use large type for id_t to avoid overflow.
59 * Use atomic<> to ensure thread safety. Use pre-increment ++x equivalent, so that the smallest possible ID 0 is
60 * reserved and never issued (might come in handy for debugging someday, identifying failure of
61 * this feature).
62 *
63 * Use local static; in C++17 this is formally thread-safe; this way the init happens on-demand (no danger of
64 * static-init mis-ordering). */
65 static atomic<id_t> s_last_id{0};
66 return s_last_id.fetch_add(1, memory_order_relaxed) + 1;
67}
68
69// Thread_token implementations.
70
72{
73 static_assert(std::is_same_v<uint64_t, Unique_id_holder::id_t>,
74 "Thread_token wraps Unique_id_holder-supplied values; this check ensures no surprises: that "
75 "Unique_id_holder issues the type we expect.");
76
77 /* Unique_id_holder::create_unique_id() never returns 0 (its first result is 1); hence default-cted
78 * Thread_token (= 0 inside) = not-yet-assigned, as Thread_token doc header promises.
79 * Both `static` (inside create_unique_id()) and this `thread_local` are trivially destructible and
80 * constant-initialized => usable at any time including during static/TLS teardown as advertised. */
81 thread_local Thread_token s_token;
82 if (s_token == Thread_token{})
83 {
84 s_token = Thread_token{Unique_id_holder::create_unique_id()}; // Slow-path.
85 }
86 // else { Fast-path: no-op. }
87 return s_token;
88}
89
90std::ostream& operator<<(std::ostream& os, Thread_token val)
91{
92 using boost::io::ios_all_saver;
93 using std::hex;
94 using std::setfill;
95 using std::setw;
96
97 ios_all_saver saver{os}; // Restore formatting at end of {block}.
98
99 /* Print the low 32 bits only (cf. abbreviated git hashes): the value comes from a monotonic counter, so
100 * the high bits are zeros until the 2^32nd ID is issued process-wide -- i.e., in practice always; printing
101 * them would be pure noise. (Display-truncation only: the stored value keeps all 64 bits, and code
102 * needing the full value printed can cast: `os << Thread_token::id_t(val)`.) */
103 static_assert(sizeof(uint32_t) == 4, "4 bytes <=> 8 hex-chars.");
104 return os << "0x" << hex << setfill('0') << setw(8) << uint32_t(Thread_token::id_t(val));
105}
106
108{
109 using boost::hash;
110 return hash<Thread_token::id_t>()(val);
111}
112
113} // namespace flow::util
Each object of this class stores (at construction) and returns (on demand) a numeric ID unique from a...
id_t unique_id() const
Raw unique ID identifying this object as well as any object of a derived type.
static id_t create_unique_id()
Short-hand for Unique_id_holder().unique_id(); useful when all you want is the unique integer itself.
Unique_id_holder()
Thread-safely construct an ID whose value is different from any other object of this class,...
const Unique_id_holder & operator=(const Unique_id_holder &) const
This assignment operator is a const no-op.
const id_t m_id
The ID. Note its constness alone forbids a classic assignment operator.
uint64_t id_t
Raw integer type to uniquely identify a thing. 64-bit width should make overflow extremely hard to re...
Flow module containing miscellaneous general-use facilities that don't fit into any other Flow module...
std::ostream & operator<<(std::ostream &os, const Thread_local_state_registry< Thread_local_state_t > &val)
Serializes a Thread_local_state_registry to a standard output stream.
Thread_token this_thread_unique_token()
Returns a value conceptually similar to this_thread::get_id() – a thread ID – but that is never recyc...
size_t hash_value(Thread_token val)
Hashes the given Thread_token; making it, e.g., boost.unordered-compatible as a key out of the box.
An integer-like, type-safe wrapper for the thread-token concept – see this_thread_unique_token(),...
Unique_id_holder::id_t id_t
Short-hand for the underlying integer type.