--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 SUSE LINUX GmbH
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+*/
+
+#ifndef CEPH_RANDOM_H
+#define CEPH_RANDOM_H 1
+
+#include <mutex>
+#include <random>
+
+#include "boost/optional.hpp"
+
+#include "common/backport14.h"
+
+// Basic random number facility, adapted from N3551:
+namespace ceph {
+namespace util {
+
+inline namespace version_1_0 {
+
+namespace detail {
+
+template <typename EngineT>
+EngineT& engine();
+
+template <typename MutexT, typename EngineT>
+void randomize_rng(const int seed, MutexT& m, EngineT& e)
+{
+ std::lock_guard<MutexT> lg(m);
+ e.seed(seed);
+}
+
+template <typename MutexT, typename EngineT>
+void randomize_rng(MutexT& m, EngineT& e)
+{
+ thread_local std::random_device rd;
+
+ std::lock_guard<MutexT> lg(m);
+ e.seed(rd());
+}
+
+template <typename EngineT = std::default_random_engine>
+void randomize_rng(const int n)
+{
+ detail::engine<EngineT>().seed(n);
+}
+
+template <typename EngineT = std::default_random_engine>
+void randomize_rng()
+{
+ thread_local std::random_device rd;
+ detail::engine<EngineT>().seed(rd());
+}
+
+template <typename EngineT>
+EngineT& engine()
+{
+ thread_local boost::optional<EngineT> rng_engine;
+
+ if (!rng_engine) {
+ rng_engine.emplace(EngineT());
+ randomize_rng<EngineT>();
+ }
+
+ return *rng_engine;
+}
+
+} // namespace detail
+
+namespace detail {
+
+template <typename NumberT,
+ typename DistributionT,
+ typename EngineT>
+NumberT generate_random_number(const NumberT min, const NumberT max,
+ EngineT& e)
+{
+ thread_local DistributionT d { min, max };
+
+ using param_type = typename DistributionT::param_type;
+ return d(e, param_type { min, max });
+}
+
+template <typename NumberT,
+ typename MutexT,
+ typename DistributionT,
+ typename EngineT>
+NumberT generate_random_number(const NumberT min, const NumberT max,
+ MutexT& m, EngineT& e)
+{
+ thread_local DistributionT d { min, max };
+
+ using param_type = typename DistributionT::param_type;
+
+ std::lock_guard<MutexT> lg(m);
+ return d(e, param_type { min, max });
+}
+
+template <typename NumberT,
+ typename DistributionT,
+ typename EngineT>
+NumberT generate_random_number(const NumberT min, const NumberT max)
+{
+ return detail::generate_random_number<NumberT, DistributionT, EngineT>
+ (min, max, detail::engine<EngineT>());
+}
+
+template <typename MutexT, typename EngineT,
+ int min = 0,
+ int max = std::numeric_limits<int>::max(),
+ typename DistributionT = std::uniform_int_distribution<int>>
+int generate_random_number(MutexT& m, EngineT& e)
+{
+ return detail::generate_random_number<int, MutexT, DistributionT, EngineT>
+ (min, max, m, e);
+}
+
+} // namespace detail
+
+template <typename EngineT = std::default_random_engine>
+void randomize_rng()
+{
+ detail::randomize_rng<EngineT>();
+}
+
+template <int min = 0,
+ int max = std::numeric_limits<int>::max(),
+ typename DistributionT = std::uniform_int_distribution<int>,
+ typename EngineT = std::default_random_engine>
+int generate_random_number()
+{
+ return detail::generate_random_number<int, DistributionT, EngineT>
+ (min, max);
+}
+
+template <typename IntegerT>
+IntegerT generate_random_number(const IntegerT min, const IntegerT max,
+ ceph::enable_if_t<std::is_integral<IntegerT>::value>* = nullptr)
+{
+ return detail::generate_random_number<IntegerT,
+ std::uniform_int_distribution<IntegerT>,
+ std::default_random_engine>
+ (min, max);
+}
+
+namespace detail {
+
+template <typename IntegerT, typename MutexT, typename EngineT>
+int generate_random_number(const IntegerT min, const IntegerT max,
+ MutexT& m, EngineT& e,
+ ceph::enable_if_t<std::is_integral<IntegerT>::value>* = nullptr)
+{
+ return detail::generate_random_number<IntegerT, MutexT,
+ std::uniform_int_distribution<IntegerT>,
+ EngineT>
+ (min, max, m, e);
+}
+
+template <typename IntegerT, typename MutexT, typename EngineT>
+int generate_random_number(const IntegerT max,
+ MutexT& m, EngineT& e,
+ ceph::enable_if_t<std::is_integral<IntegerT>::value>* = nullptr)
+{
+ constexpr IntegerT zero = 0;
+ return generate_random_number(zero, max, m, e);
+}
+
+} // namespace detail
+
+template <typename IntegerT>
+int generate_random_number(const IntegerT max,
+ ceph::enable_if_t<std::is_integral<IntegerT>::value>* = nullptr)
+{
+ constexpr IntegerT zero = 0;
+ return generate_random_number(zero, max);
+}
+
+template <typename RealT>
+RealT generate_random_number(const RealT min, const RealT max,
+ ceph::enable_if_t<std::is_floating_point<RealT>::value>* = nullptr)
+{
+ return detail::generate_random_number<RealT,
+ std::uniform_real_distribution<RealT>,
+ std::default_random_engine>
+ (min, max);
+}
+
+namespace detail {
+
+template <typename RealT, typename MutexT>
+RealT generate_random_number(const RealT max, MutexT& m,
+ ceph::enable_if_t<std::is_floating_point<RealT>::value>* = nullptr)
+{
+ constexpr RealT zero = 0.0;
+ return generate_random_number(zero, max, m);
+}
+
+template <typename RealT, typename MutexT, typename EngineT>
+RealT generate_random_number(const RealT min, const RealT max, MutexT& m, EngineT& e,
+ ceph::enable_if_t<std::is_floating_point<RealT>::value>* = nullptr)
+{
+ return detail::generate_random_number<RealT, MutexT,
+ std::uniform_real_distribution<RealT>,
+ EngineT>
+ (min, max, m, e);
+}
+
+
+template <typename RealT, typename MutexT, typename EngineT>
+RealT generate_random_number(const RealT max, MutexT& m, EngineT& e,
+ ceph::enable_if_t<std::is_floating_point<RealT>::value>* = nullptr)
+{
+ constexpr RealT zero = 0.0;
+ return generate_random_number(zero, max, m, e);
+}
+
+} // namespace detail
+
+template <typename RealT>
+RealT generate_random_number(const RealT max,
+ ceph::enable_if_t<std::is_floating_point<RealT>::value>* = nullptr)
+{
+ constexpr RealT zero = 0.0;
+ return generate_random_number(zero, max);
+}
+
+// Function object:
+template <typename NumberT>
+class random_number_generator final
+{
+ std::mutex l;
+ std::random_device rd;
+ std::default_random_engine e;
+
+ public:
+ using number_type = NumberT;
+
+ public:
+ random_number_generator() {
+ detail::randomize_rng(l, e);
+ }
+
+ explicit random_number_generator(const int seed) {
+ detail::randomize_rng(seed, l, e);
+ }
+
+ random_number_generator(random_number_generator&& rhs)
+ : e(std::move(rhs.e))
+ {}
+
+ public:
+ random_number_generator(const random_number_generator&) = delete;
+ random_number_generator& operator=(const random_number_generator&) = delete;
+
+ public:
+ NumberT operator()() {
+ return detail::generate_random_number(l, e);
+ }
+
+ NumberT operator()(const NumberT max) {
+ return detail::generate_random_number(max, l, e);
+ }
+
+ NumberT operator()(const NumberT min, const NumberT max) {
+ return detail::generate_random_number(min, max, l, e);
+ }
+
+ public:
+ void seed(const int n) {
+ detail::randomize_rng(n, l, e);
+ }
+};
+
+} // inline namespace version_1_0
+
+}} // namespace ceph::util
+
+#endif
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2017 SUSE LINUX GmbH
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+*/
+
+#include <sstream>
+
+#include "include/random.h"
+
+#include "gtest/gtest.h"
+
+// Helper to see if calls compile with various types:
+template <typename T>
+T type_check_ok(const T min, const T max)
+{
+ return ceph::util::generate_random_number(min, max);
+}
+
+/* Help wrangle "unused variable" warnings: */
+template <typename X>
+void swallow_values(const X x)
+{
+ static_cast<void>(x);
+}
+
+template <typename X, typename ...XS>
+void swallow_values(const X x, const XS... xs)
+{
+ swallow_values(x), swallow_values(xs...);
+}
+
+// Mini-examples showing canonical usage:
+TEST(util, test_random_canonical)
+{
+ // Seed random number generation:
+ ceph::util::randomize_rng();
+
+ // Get a random int between 0 and max int:
+ auto a = ceph::util::generate_random_number();
+
+ // Get a random int between 0 and 20:
+ auto b = ceph::util::generate_random_number(20);
+
+ // Get a random int between 1 and 20:
+ auto c = ceph::util::generate_random_number(1, 20);
+
+ // Get a random float between 0.0 and 20.0:
+ auto d = ceph::util::generate_random_number(20.0);
+
+ // Get a random float between 0.001 and 0.991:
+ auto e = ceph::util::generate_random_number(0.001, 0.991);
+
+ // Make a function object RNG suitable for putting on its own thread:
+ auto gen_fn = ceph::util::random_number_generator<int>();
+ auto z = gen_fn();
+ gen_fn.seed(42); // re-seed
+
+ // Placate the compiler:
+ swallow_values(a, b, c, d, e, z);
+}
+
+TEST(util, test_random)
+{
+ /* The intent of this test is not to formally test random number generation,
+ but rather to casually check that "it works" and catch regressions: */
+
+ // The default overload should compile:
+ ceph::util::randomize_rng();
+
+ {
+ int a = ceph::util::generate_random_number();
+ int b = ceph::util::generate_random_number();
+
+ /* Technically, this can still collide and cause a false negative, but let's
+ be optimistic: */
+ if (std::numeric_limits<int>::max() > 32767) {
+ ASSERT_GT(a, -1);
+ ASSERT_GT(b, -1);
+
+ ASSERT_NE(a, b);
+ }
+ }
+
+ {
+ auto a = ceph::util::generate_random_number(1, std::numeric_limits<int>::max());
+ auto b = ceph::util::generate_random_number(1, std::numeric_limits<int>::max());
+
+ if (std::numeric_limits<int>::max() > 32767) {
+ ASSERT_GT(a, 0);
+ ASSERT_GT(b, 0);
+
+ ASSERT_NE(a, b);
+ }
+ }
+
+ for (auto n = 100000; n; --n) {
+ constexpr int min = 0, max = 6;
+ int a = ceph::util::generate_random_number<min, max>();
+ ASSERT_GT(a, -1);
+ ASSERT_LT(a, 7);
+ }
+
+ // Multiple types (integral):
+ {
+ int min = 0, max = 1;
+ type_check_ok(min, max);
+ }
+
+ {
+ long min = 0, max = 1l;
+ type_check_ok(min, max);
+ }
+
+ // Multiple types (floating point):
+ {
+ double min = 0.0, max = 1.0;
+ type_check_ok(min, max);
+ }
+
+ {
+ float min = 0.0, max = 1.0;
+ type_check_ok(min, max);
+ }
+
+ // min > max should not explode:
+ {
+ float min = 1.0, max = 0.0;
+ type_check_ok(min, max);
+ }
+}
+
+TEST(util, test_random_class_interface)
+{
+ ceph::util::random_number_generator<int> rng_i;
+ ceph::util::random_number_generator<float> rng_f;
+
+ // Other ctors:
+ {
+ ceph::util::random_number_generator<int> rng(1234); // seed
+ }
+
+ {
+ int a = rng_i();
+ int b = rng_i();
+
+ // Technically can fail, but should "almost never" happen:
+ ASSERT_NE(a, b);
+ }
+
+ {
+ int a = rng_i(10);
+ ASSERT_LE(a, 10);
+ ASSERT_GE(a, 0);
+ }
+
+ {
+ float a = rng_f(10.0);
+ ASSERT_LE(a, 10.0);
+ ASSERT_GE(a, 0.0);
+ }
+
+ {
+ int a = rng_i(10, 20);
+ ASSERT_LE(a, 20);
+ ASSERT_GE(a, 10);
+ }
+
+ {
+ float a = rng_f(10.0, 20.0);
+ ASSERT_LE(a, 20.0);
+ ASSERT_GE(a, 10.0);
+ }
+}
+