abseil-cpp/absl/random/internal/pool_urbg.cc
Go to the documentation of this file.
1 // Copyright 2017 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "absl/random/internal/pool_urbg.h"
16 
17 #include <algorithm>
18 #include <atomic>
19 #include <cstdint>
20 #include <cstring>
21 #include <iterator>
22 
23 #include "absl/base/attributes.h"
24 #include "absl/base/call_once.h"
25 #include "absl/base/config.h"
26 #include "absl/base/internal/endian.h"
27 #include "absl/base/internal/raw_logging.h"
28 #include "absl/base/internal/spinlock.h"
29 #include "absl/base/internal/sysinfo.h"
30 #include "absl/base/internal/unaligned_access.h"
31 #include "absl/base/optimization.h"
32 #include "absl/random/internal/randen.h"
33 #include "absl/random/internal/seed_material.h"
34 #include "absl/random/seed_gen_exception.h"
35 
38 
39 namespace absl {
41 namespace random_internal {
42 namespace {
43 
44 // RandenPoolEntry is a thread-safe pseudorandom bit generator, implementing a
45 // single generator within a RandenPool<T>. It is an internal implementation
46 // detail, and does not aim to conform to [rand.req.urng].
47 //
48 // NOTE: There are alignment issues when used on ARM, for instance.
49 // See the allocation code in PoolAlignedAlloc().
50 class RandenPoolEntry {
51  public:
52  static constexpr size_t kState = RandenTraits::kStateBytes / sizeof(uint32_t);
53  static constexpr size_t kCapacity =
55 
57  SpinLockHolder l(&mu_); // Always uncontested.
58  std::copy(data.begin(), data.end(), std::begin(state_));
59  next_ = kState;
60  }
61 
62  // Copy bytes into out.
63  void Fill(uint8_t* out, size_t bytes) ABSL_LOCKS_EXCLUDED(mu_);
64 
65  // Returns random bits from the buffer in units of T.
66  template <typename T>
67  inline T Generate() ABSL_LOCKS_EXCLUDED(mu_);
68 
69  inline void MaybeRefill() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mu_) {
70  if (next_ >= kState) {
71  next_ = kCapacity;
72  impl_.Generate(state_);
73  }
74  }
75 
76  private:
77  // Randen URBG state.
78  uint32_t state_[kState] ABSL_GUARDED_BY(mu_); // First to satisfy alignment.
80  const Randen impl_;
81  size_t next_ ABSL_GUARDED_BY(mu_);
82 };
83 
84 template <>
85 inline uint8_t RandenPoolEntry::Generate<uint8_t>() {
87  MaybeRefill();
88  return static_cast<uint8_t>(state_[next_++]);
89 }
90 
91 template <>
92 inline uint16_t RandenPoolEntry::Generate<uint16_t>() {
94  MaybeRefill();
95  return static_cast<uint16_t>(state_[next_++]);
96 }
97 
98 template <>
99 inline uint32_t RandenPoolEntry::Generate<uint32_t>() {
100  SpinLockHolder l(&mu_);
101  MaybeRefill();
102  return state_[next_++];
103 }
104 
105 template <>
106 inline uint64_t RandenPoolEntry::Generate<uint64_t>() {
107  SpinLockHolder l(&mu_);
108  if (next_ >= kState - 1) {
109  next_ = kCapacity;
111  }
112  auto p = state_ + next_;
113  next_ += 2;
114 
116  std::memcpy(&result, p, sizeof(result));
117  return result;
118 }
119 
120 void RandenPoolEntry::Fill(uint8_t* out, size_t bytes) {
121  SpinLockHolder l(&mu_);
122  while (bytes > 0) {
123  MaybeRefill();
124  size_t remaining = (kState - next_) * sizeof(state_[0]);
125  size_t to_copy = std::min(bytes, remaining);
126  std::memcpy(out, &state_[next_], to_copy);
127  out += to_copy;
128  bytes -= to_copy;
129  next_ += (to_copy + sizeof(state_[0]) - 1) / sizeof(state_[0]);
130  }
131 }
132 
133 // Number of pooled urbg entries.
134 static constexpr int kPoolSize = 8;
135 
136 // Shared pool entries.
137 static absl::once_flag pool_once;
138 ABSL_CACHELINE_ALIGNED static RandenPoolEntry* shared_pools[kPoolSize];
139 
140 // Returns an id in the range [0 ... kPoolSize), which indexes into the
141 // pool of random engines.
142 //
143 // Each thread to access the pool is assigned a sequential ID (without reuse)
144 // from the pool-id space; the id is cached in a thread_local variable.
145 // This id is assigned based on the arrival-order of the thread to the
146 // GetPoolID call; this has no binary, CL, or runtime stability because
147 // on subsequent runs the order within the same program may be significantly
148 // different. However, as other thread IDs are not assigned sequentially,
149 // this is not expected to matter.
150 int GetPoolID() {
151  static_assert(kPoolSize >= 1,
152  "At least one urbg instance is required for PoolURBG");
153 
154  ABSL_CONST_INIT static std::atomic<int64_t> sequence{0};
155 
156 #ifdef ABSL_HAVE_THREAD_LOCAL
157  static thread_local int my_pool_id = -1;
158  if (ABSL_PREDICT_FALSE(my_pool_id < 0)) {
159  my_pool_id = (sequence++ % kPoolSize);
160  }
161  return my_pool_id;
162 #else
163  static pthread_key_t tid_key = [] {
164  pthread_key_t tmp_key;
165  int err = pthread_key_create(&tmp_key, nullptr);
166  if (err) {
167  ABSL_RAW_LOG(FATAL, "pthread_key_create failed with %d", err);
168  }
169  return tmp_key;
170  }();
171 
172  // Store the value in the pthread_{get/set}specific. However an uninitialized
173  // value is 0, so add +1 to distinguish from the null value.
174  intptr_t my_pool_id =
175  reinterpret_cast<intptr_t>(pthread_getspecific(tid_key));
176  if (ABSL_PREDICT_FALSE(my_pool_id == 0)) {
177  // No allocated ID, allocate the next value, cache it, and return.
178  my_pool_id = (sequence++ % kPoolSize) + 1;
179  int err = pthread_setspecific(tid_key, reinterpret_cast<void*>(my_pool_id));
180  if (err) {
181  ABSL_RAW_LOG(FATAL, "pthread_setspecific failed with %d", err);
182  }
183  }
184  return my_pool_id - 1;
185 #endif
186 }
187 
188 // Allocate a RandenPoolEntry with at least 32-byte alignment, which is required
189 // by ARM platform code.
190 RandenPoolEntry* PoolAlignedAlloc() {
191  constexpr size_t kAlignment =
193 
194  // Not all the platforms that we build for have std::aligned_alloc, however
195  // since we never free these objects, we can over allocate and munge the
196  // pointers to the correct alignment.
197  intptr_t x = reinterpret_cast<intptr_t>(
198  new char[sizeof(RandenPoolEntry) + kAlignment]);
199  auto y = x % kAlignment;
200  void* aligned = reinterpret_cast<void*>(y == 0 ? x : (x + kAlignment - y));
201  return new (aligned) RandenPoolEntry();
202 }
203 
204 // Allocate and initialize kPoolSize objects of type RandenPoolEntry.
205 //
206 // The initialization strategy is to initialize one object directly from
207 // OS entropy, then to use that object to seed all of the individual
208 // pool instances.
209 void InitPoolURBG() {
210  static constexpr size_t kSeedSize =
212  // Read the seed data from OS entropy once.
213  uint32_t seed_material[kPoolSize * kSeedSize];
215  absl::MakeSpan(seed_material))) {
217  }
218  for (int i = 0; i < kPoolSize; i++) {
219  shared_pools[i] = PoolAlignedAlloc();
220  shared_pools[i]->Init(
221  absl::MakeSpan(&seed_material[i * kSeedSize], kSeedSize));
222  }
223 }
224 
225 // Returns the pool entry for the current thread.
226 RandenPoolEntry* GetPoolForCurrentThread() {
227  absl::call_once(pool_once, InitPoolURBG);
228  return shared_pools[GetPoolID()];
229 }
230 
231 } // namespace
232 
233 template <typename T>
235  auto* pool = GetPoolForCurrentThread();
236  return pool->Generate<T>();
237 }
238 
239 template <typename T>
241  auto* pool = GetPoolForCurrentThread();
242  pool->Fill(reinterpret_cast<uint8_t*>(data.data()),
243  data.size() * sizeof(result_type));
244 }
245 
246 template class RandenPool<uint8_t>;
247 template class RandenPool<uint16_t>;
248 template class RandenPool<uint32_t>;
249 template class RandenPool<uint64_t>;
250 
251 } // namespace random_internal
253 } // namespace absl
ABSL_PREDICT_FALSE
#define ABSL_PREDICT_FALSE(x)
Definition: abseil-cpp/absl/base/optimization.h:180
_gevent_test_main.result
result
Definition: _gevent_test_main.py:96
absl::base_internal::SpinLockHolder
Definition: third_party/abseil-cpp/absl/base/internal/spinlock.h:196
ABSL_CONST_INIT
#define ABSL_CONST_INIT
Definition: abseil-cpp/absl/base/attributes.h:716
absl::random_internal::RandenPool::Generate
static result_type Generate()
Definition: abseil-cpp/absl/random/internal/pool_urbg.cc:234
begin
char * begin
Definition: abseil-cpp/absl/strings/internal/str_format/float_conversion.cc:1007
uint16_t
unsigned short uint16_t
Definition: stdint-msvc2008.h:79
absl::Span
Definition: abseil-cpp/absl/types/span.h:152
y
const double y
Definition: bloaty/third_party/googletest/googlemock/test/gmock-matchers_test.cc:3611
copy
static int copy(grpc_slice_buffer *input, grpc_slice_buffer *output)
Definition: message_compress.cc:145
kCapacity
static constexpr size_t kCapacity
Definition: abseil-cpp/absl/random/internal/pool_urbg.cc:53
absl::random_internal::RandenPool::Fill
static void Fill(absl::Span< result_type > data)
Definition: abseil-cpp/absl/random/internal/pool_urbg.cc:240
ABSL_CACHELINE_SIZE
#define ABSL_CACHELINE_SIZE
Definition: abseil-cpp/absl/base/optimization.h:149
error_ref_leak.err
err
Definition: error_ref_leak.py:35
absl::internal_any_invocable::kAlignment
@ kAlignment
Definition: internal/any_invocable.h:92
absl::base_internal::SpinLock
Definition: third_party/abseil-cpp/absl/base/internal/spinlock.h:52
google::protobuf::python::cmessage::Init
static int Init(CMessage *self, PyObject *args, PyObject *kwargs)
Definition: bloaty/third_party/protobuf/python/google/protobuf/pyext/message.cc:1287
absl::random_internal::Randen::Generate
void Generate(void *state) const
Definition: abseil-cpp/absl/random/internal/randen.h:47
xds_manager.p
p
Definition: xds_manager.py:60
ABSL_NAMESPACE_END
#define ABSL_NAMESPACE_END
Definition: third_party/abseil-cpp/absl/base/config.h:171
uint8_t
unsigned char uint8_t
Definition: stdint-msvc2008.h:78
T
#define T(upbtypeconst, upbtype, ctype, default_value)
uint32_t
unsigned int uint32_t
Definition: stdint-msvc2008.h:80
mu_
SpinLock mu_
Definition: abseil-cpp/absl/random/internal/pool_urbg.cc:79
absl::call_once
void call_once(absl::once_flag &flag, Callable &&fn, Args &&... args)
Definition: abseil-cpp/absl/base/call_once.h:206
memcpy
memcpy(mem, inblock.get(), min(CONTAINING_RECORD(inblock.get(), MEMBLOCK, data) ->size, size))
absl::random_internal::ThrowSeedGenException
void ThrowSeedGenException()
Definition: abseil-cpp/absl/random/seed_gen_exception.cc:35
kState
static constexpr size_t kState
Definition: abseil-cpp/absl/random/internal/pool_urbg.cc:52
ABSL_NAMESPACE_BEGIN
#define ABSL_NAMESPACE_BEGIN
Definition: third_party/abseil-cpp/absl/base/config.h:170
absl::random_internal::RandenPool
Definition: abseil-cpp/absl/random/internal/pool_urbg.h:33
absl::random_internal::ReadSeedMaterialFromOSEntropy
bool ReadSeedMaterialFromOSEntropy(absl::Span< uint32_t > values)
Definition: abseil-cpp/absl/random/internal/seed_material.cc:205
ABSL_EXCLUSIVE_LOCKS_REQUIRED
#define ABSL_EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: abseil-cpp/absl/base/thread_annotations.h:145
uint64_t
unsigned __int64 uint64_t
Definition: stdint-msvc2008.h:90
intptr_t
_W64 signed int intptr_t
Definition: stdint-msvc2008.h:118
x
int x
Definition: bloaty/third_party/googletest/googlemock/test/gmock-matchers_test.cc:3610
data
char data[kBufferLength]
Definition: abseil-cpp/absl/strings/internal/str_format/float_conversion.cc:1006
min
#define min(a, b)
Definition: qsort.h:83
absl::base_internal::tid_key
static ABSL_CONST_INIT pthread_key_t tid_key
Definition: abseil-cpp/absl/base/internal/sysinfo.cc:418
absl::random_internal::RandenTraits::kStateBytes
static constexpr size_t kStateBytes
Definition: abseil-cpp/absl/random/internal/randen_traits.h:58
FATAL
#define FATAL(msg)
Definition: task.h:88
absl::random_internal::RandenTraits::kCapacityBytes
static constexpr size_t kCapacityBytes
Definition: abseil-cpp/absl/random/internal/randen_traits.h:62
bytes
uint8 bytes[10]
Definition: bloaty/third_party/protobuf/src/google/protobuf/io/coded_stream_unittest.cc:153
absl::random_internal::RandenPool::result_type
T result_type
Definition: abseil-cpp/absl/random/internal/pool_urbg.h:35
ABSL_CACHELINE_ALIGNED
#define ABSL_CACHELINE_ALIGNED
Definition: abseil-cpp/absl/base/optimization.h:150
ABSL_LOCKS_EXCLUDED
#define ABSL_LOCKS_EXCLUDED(...)
Definition: abseil-cpp/absl/base/thread_annotations.h:163
state_
grpc_connectivity_state state_
Definition: channel_connectivity.cc:213
pool
InternalDescriptorPool * pool
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/protobuf.h:807
absl::ABSL_GUARDED_BY
struct absl::SynchWaitParams ABSL_GUARDED_BY[kNSynchEvent]
absl
Definition: abseil-cpp/absl/algorithm/algorithm.h:31
absl::out
char * out
Definition: abseil-cpp/absl/synchronization/mutex.h:1048
impl_
const Randen impl_
Definition: abseil-cpp/absl/random/internal/pool_urbg.cc:80
absl::once_flag
Definition: abseil-cpp/absl/base/call_once.h:85
run_grpclb_interop_tests.l
dictionary l
Definition: run_grpclb_interop_tests.py:410
ABSL_RAW_LOG
#define ABSL_RAW_LOG(severity,...)
Definition: abseil-cpp/absl/base/internal/raw_logging.h:44
absl::MakeSpan
constexpr Span< T > MakeSpan(T *ptr, size_t size) noexcept
Definition: abseil-cpp/absl/types/span.h:661
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230


grpc
Author(s):
autogenerated on Fri May 16 2025 02:59:44