mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 21:34:46 +02:00
Simplify websocket::detail::prng:
- Use a regular function pointer for dynamic dispatch. - Remove `prng::ref` - it did not benefit the default case (TLS avaialable) and actually made the no-TLS case slower, because the time spent in the generator is dominated by mutex locking. Signed-off-by: Damian Jarek <damian.jarek93@gmail.com>
This commit is contained in:
committed by
Vinnie Falco
parent
a7e932b13c
commit
5f0939e771
@@ -3,6 +3,7 @@ Version 261:
|
||||
* Deduplicate `websocket::read_size_hint` definition
|
||||
* Fix UB in websocket read tests
|
||||
* Remove redundant includes in websocket
|
||||
* Simplify websocket::detail::prng
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@@ -53,6 +53,14 @@ jobs:
|
||||
B2_FLAGS: <boost.beast.separate-compilation>off <boost.beast.allow-deprecated>off
|
||||
CXXSTD: 11
|
||||
B2_TARGETS: libs/beast/test//run-fat-tests
|
||||
GCC 9 C++11 NO_DEPRECATED NO_TLS:
|
||||
TOOLSET: gcc
|
||||
CXX: g++-9
|
||||
PACKAGES: g++-9
|
||||
VARIANT: release
|
||||
B2_FLAGS: <boost.beast.allow-deprecated>off <define>BOOST_NO_CXX11_THREAD_LOCAL
|
||||
CXXSTD: 11
|
||||
B2_TARGETS: libs/beast/test//run-fat-tests
|
||||
GCC 9 C++11 UBASAN:
|
||||
TOOLSET: gcc
|
||||
CXX: g++-9
|
||||
|
@@ -13,7 +13,6 @@
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <random>
|
||||
|
||||
namespace boost {
|
||||
@@ -21,66 +20,7 @@ namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
// Type-erased UniformRandomBitGenerator
|
||||
// with 32-bit unsigned integer value_type
|
||||
//
|
||||
class prng
|
||||
{
|
||||
protected:
|
||||
virtual ~prng() = default;
|
||||
virtual prng& acquire() noexcept = 0;
|
||||
virtual void release() noexcept = 0;
|
||||
virtual std::uint32_t operator()() noexcept = 0;
|
||||
|
||||
public:
|
||||
using value_type = std::uint32_t;
|
||||
|
||||
class ref
|
||||
{
|
||||
prng& p_;
|
||||
|
||||
public:
|
||||
ref& operator=(ref const&) = delete;
|
||||
|
||||
~ref()
|
||||
{
|
||||
p_.release();
|
||||
}
|
||||
|
||||
explicit
|
||||
ref(prng& p) noexcept
|
||||
: p_(p.acquire())
|
||||
{
|
||||
}
|
||||
|
||||
ref(ref const& other) noexcept
|
||||
: p_(other.p_.acquire())
|
||||
{
|
||||
}
|
||||
|
||||
value_type
|
||||
operator()() noexcept
|
||||
{
|
||||
return p_();
|
||||
}
|
||||
|
||||
static
|
||||
value_type constexpr
|
||||
min BOOST_PREVENT_MACRO_SUBSTITUTION () noexcept
|
||||
{
|
||||
return (std::numeric_limits<
|
||||
value_type>::min)();
|
||||
}
|
||||
|
||||
static
|
||||
value_type constexpr
|
||||
max BOOST_PREVENT_MACRO_SUBSTITUTION () noexcept
|
||||
{
|
||||
return (std::numeric_limits<
|
||||
value_type>::max)();
|
||||
}
|
||||
};
|
||||
};
|
||||
using generator = std::uint32_t(*)();
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -91,25 +31,11 @@ BOOST_BEAST_DECL
|
||||
std::uint32_t const*
|
||||
prng_seed(std::seed_seq* ss = nullptr);
|
||||
|
||||
// Acquire a PRNG using the no-TLS implementation
|
||||
//
|
||||
BOOST_BEAST_DECL
|
||||
prng::ref
|
||||
make_prng_no_tls(bool secure);
|
||||
|
||||
// Acquire a PRNG using the TLS implementation
|
||||
//
|
||||
#ifndef BOOST_NO_CXX11_THREAD_LOCAL
|
||||
BOOST_BEAST_DECL
|
||||
prng::ref
|
||||
make_prng_tls(bool secure);
|
||||
#endif
|
||||
|
||||
// Acquire a PRNG using the TLS implementation if it
|
||||
// is available, otherwise using the no-TLS implementation.
|
||||
//
|
||||
BOOST_BEAST_DECL
|
||||
prng::ref
|
||||
generator
|
||||
make_prng(bool secure);
|
||||
|
||||
} // detail
|
||||
|
@@ -13,14 +13,10 @@
|
||||
#include <boost/beast/websocket/detail/prng.hpp>
|
||||
#include <boost/beast/core/detail/chacha.hpp>
|
||||
#include <boost/beast/core/detail/pcg.hpp>
|
||||
#include <boost/align/aligned_alloc.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <random>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
@@ -59,264 +55,92 @@ prng_seed(std::seed_seq* ss)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class T>
|
||||
class prng_pool
|
||||
inline
|
||||
std::uint32_t
|
||||
make_nonce()
|
||||
{
|
||||
std::mutex m_;
|
||||
T* head_ = nullptr;
|
||||
|
||||
public:
|
||||
static
|
||||
prng_pool&
|
||||
instance()
|
||||
{
|
||||
static prng_pool p;
|
||||
return p;
|
||||
}
|
||||
|
||||
~prng_pool()
|
||||
{
|
||||
for(auto p = head_; p;)
|
||||
{
|
||||
auto next = p->next;
|
||||
p->~T();
|
||||
boost::alignment::aligned_free(p);
|
||||
p = next;
|
||||
}
|
||||
}
|
||||
|
||||
prng::ref
|
||||
acquire()
|
||||
{
|
||||
{
|
||||
std::lock_guard<
|
||||
std::mutex> g(m_);
|
||||
if(head_)
|
||||
{
|
||||
auto p = head_;
|
||||
head_ = head_->next;
|
||||
return prng::ref(*p);
|
||||
}
|
||||
}
|
||||
auto p = boost::alignment::aligned_alloc(
|
||||
16, sizeof(T));
|
||||
if(! p)
|
||||
BOOST_THROW_EXCEPTION(std::bad_alloc{});
|
||||
return prng::ref(*(::new(p) T()));
|
||||
}
|
||||
|
||||
void
|
||||
release(T& t)
|
||||
{
|
||||
std::lock_guard<
|
||||
std::mutex> g(m_);
|
||||
t.next = head_;
|
||||
head_ = &t;
|
||||
}
|
||||
};
|
||||
|
||||
prng::ref
|
||||
make_prng_no_tls(bool secure)
|
||||
{
|
||||
class fast_prng final : public prng
|
||||
{
|
||||
int refs_ = 0;
|
||||
beast::detail::pcg r_;
|
||||
|
||||
public:
|
||||
fast_prng* next = nullptr;
|
||||
|
||||
fast_prng()
|
||||
: r_(
|
||||
[]{
|
||||
auto const pv = prng_seed();
|
||||
return
|
||||
((static_cast<std::uint64_t>(pv[0])<<32)+pv[1]) ^
|
||||
((static_cast<std::uint64_t>(pv[2])<<32)+pv[3]) ^
|
||||
((static_cast<std::uint64_t>(pv[4])<<32)+pv[5]) ^
|
||||
((static_cast<std::uint64_t>(pv[6])<<32)+pv[7]);
|
||||
}(),
|
||||
[]{
|
||||
static std::atomic<
|
||||
std::uint32_t> nonce{0};
|
||||
return ++nonce;
|
||||
}())
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
prng&
|
||||
acquire() noexcept override
|
||||
{
|
||||
++refs_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
release() noexcept override
|
||||
{
|
||||
if(--refs_ == 0)
|
||||
prng_pool<fast_prng>::instance().release(*this);
|
||||
}
|
||||
|
||||
value_type
|
||||
operator()() noexcept override
|
||||
{
|
||||
return r_();
|
||||
}
|
||||
};
|
||||
|
||||
class secure_prng final : public prng
|
||||
{
|
||||
int refs_ = 0;
|
||||
beast::detail::chacha<20> r_;
|
||||
|
||||
public:
|
||||
secure_prng* next = nullptr;
|
||||
|
||||
secure_prng()
|
||||
: r_(prng_seed(), []
|
||||
{
|
||||
static std::atomic<
|
||||
std::uint64_t> nonce{0};
|
||||
return ++nonce;
|
||||
}())
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
prng&
|
||||
acquire() noexcept override
|
||||
{
|
||||
++refs_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
release() noexcept override
|
||||
{
|
||||
if(--refs_ == 0)
|
||||
prng_pool<secure_prng>::instance().release(*this);
|
||||
}
|
||||
|
||||
value_type
|
||||
operator()() noexcept override
|
||||
{
|
||||
return r_();
|
||||
}
|
||||
};
|
||||
|
||||
if(secure)
|
||||
return prng_pool<secure_prng>::instance().acquire();
|
||||
|
||||
return prng_pool<fast_prng>::instance().acquire();
|
||||
static std::atomic<std::uint32_t> nonce{0};
|
||||
return ++nonce;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifndef BOOST_NO_CXX11_THREAD_LOCAL
|
||||
|
||||
prng::ref
|
||||
make_prng_tls(bool secure)
|
||||
inline
|
||||
beast::detail::pcg make_pcg()
|
||||
{
|
||||
class fast_prng final : public prng
|
||||
auto const pv = prng_seed();
|
||||
return beast::detail::pcg{
|
||||
((static_cast<std::uint64_t>(pv[0])<<32)+pv[1]) ^
|
||||
((static_cast<std::uint64_t>(pv[2])<<32)+pv[3]) ^
|
||||
((static_cast<std::uint64_t>(pv[4])<<32)+pv[5]) ^
|
||||
((static_cast<std::uint64_t>(pv[6])<<32)+pv[7]), make_nonce()};
|
||||
}
|
||||
|
||||
#ifdef BOOST_NO_CXX11_THREAD_LOCAL
|
||||
|
||||
inline
|
||||
std::uint32_t
|
||||
secure_generate()
|
||||
{
|
||||
struct generator
|
||||
{
|
||||
beast::detail::pcg r_;
|
||||
|
||||
public:
|
||||
fast_prng()
|
||||
: r_(
|
||||
[]{
|
||||
auto const pv = prng_seed();
|
||||
return
|
||||
((static_cast<std::uint64_t>(pv[0])<<32)+pv[1]) ^
|
||||
((static_cast<std::uint64_t>(pv[2])<<32)+pv[3]) ^
|
||||
((static_cast<std::uint64_t>(pv[4])<<32)+pv[5]) ^
|
||||
((static_cast<std::uint64_t>(pv[6])<<32)+pv[7]);
|
||||
}(),
|
||||
[]{
|
||||
static std::atomic<
|
||||
std::uint32_t> nonce{0};
|
||||
return ++nonce;
|
||||
}())
|
||||
std::uint32_t operator()()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard{mtx};
|
||||
return gen();
|
||||
}
|
||||
|
||||
protected:
|
||||
prng&
|
||||
acquire() noexcept override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
release() noexcept override
|
||||
{
|
||||
}
|
||||
|
||||
value_type
|
||||
operator()() noexcept override
|
||||
{
|
||||
return r_();
|
||||
}
|
||||
beast::detail::chacha<20> gen;
|
||||
std::mutex mtx;
|
||||
};
|
||||
static generator gen{beast::detail::chacha<20>{prng_seed(), make_nonce()}};
|
||||
return gen();
|
||||
}
|
||||
|
||||
class secure_prng final : public prng
|
||||
inline
|
||||
std::uint32_t
|
||||
fast_generate()
|
||||
{
|
||||
struct generator
|
||||
{
|
||||
beast::detail::chacha<20> r_;
|
||||
|
||||
public:
|
||||
secure_prng()
|
||||
: r_(prng_seed(), []
|
||||
{
|
||||
static std::atomic<
|
||||
std::uint64_t> nonce{0};
|
||||
return ++nonce;
|
||||
}())
|
||||
std::uint32_t operator()()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard{mtx};
|
||||
return gen();
|
||||
}
|
||||
|
||||
protected:
|
||||
prng&
|
||||
acquire() noexcept override
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
release() noexcept override
|
||||
{
|
||||
}
|
||||
|
||||
value_type
|
||||
operator()() noexcept override
|
||||
{
|
||||
return r_();
|
||||
}
|
||||
beast::detail::pcg gen;
|
||||
std::mutex mtx;
|
||||
};
|
||||
static generator gen{make_pcg()};
|
||||
return gen();
|
||||
}
|
||||
|
||||
if(secure)
|
||||
{
|
||||
thread_local secure_prng sp;
|
||||
return prng::ref(sp);
|
||||
}
|
||||
#else
|
||||
|
||||
thread_local fast_prng fp;
|
||||
return prng::ref(fp);
|
||||
inline
|
||||
std::uint32_t
|
||||
secure_generate()
|
||||
{
|
||||
thread_local static beast::detail::chacha<20> gen{prng_seed(), make_nonce()};
|
||||
return gen();
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint32_t
|
||||
fast_generate()
|
||||
{
|
||||
thread_local static beast::detail::pcg gen{make_pcg()};
|
||||
return gen();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
prng::ref
|
||||
generator
|
||||
make_prng(bool secure)
|
||||
{
|
||||
#ifdef BOOST_NO_CXX11_THREAD_LOCAL
|
||||
return make_prng_no_tls(secure);
|
||||
#else
|
||||
return make_prng_tls(secure);
|
||||
#endif
|
||||
if (secure)
|
||||
return &secure_generate;
|
||||
else
|
||||
return &fast_generate;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
@@ -42,17 +42,20 @@ public:
|
||||
void
|
||||
testPrng(F const& f)
|
||||
{
|
||||
auto const min = std::numeric_limits<std::uint32_t>::min();
|
||||
auto const max = std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
{
|
||||
auto v = f()();
|
||||
BEAST_EXPECT(
|
||||
v >= (prng::ref::min)() &&
|
||||
v <= (prng::ref::max)());
|
||||
v >= min &&
|
||||
v <= max);
|
||||
}
|
||||
{
|
||||
auto v = f()();
|
||||
BEAST_EXPECT(
|
||||
v >= (prng::ref::min)() &&
|
||||
v <= (prng::ref::max)());
|
||||
v >= min &&
|
||||
v <= max);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,12 +64,6 @@ public:
|
||||
{
|
||||
testPrng([]{ return make_prng(true); });
|
||||
testPrng([]{ return make_prng(false); });
|
||||
testPrng([]{ return make_prng_no_tls(true); });
|
||||
testPrng([]{ return make_prng_no_tls(false); });
|
||||
#ifndef BOOST_NO_CXX11_THREAD_LOCAL
|
||||
testPrng([]{ return make_prng_tls(true); });
|
||||
testPrng([]{ return make_prng_tls(false); });
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user