Added new random pool library and FixedSizeData:CreatePseudoRandom() uses it. This generates pseudorandom data in another thread and keeps a pool instead of calling the pseudorandom algorithm each time. Since that algorithm needs to be mutexed, that caused quite a lot of locking before
This commit is contained in:
parent
02d89fa721
commit
c51e2c115a
@ -7,7 +7,7 @@
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include "../assert/assert.h"
|
||||
#include "ThreadsafePseudoRandomPool.h"
|
||||
#include "../random/RandomPool.h"
|
||||
|
||||
namespace cpputils {
|
||||
|
||||
@ -35,7 +35,6 @@ public:
|
||||
|
||||
private:
|
||||
FixedSizeData() {}
|
||||
static ThreadsafePseudoRandomPool &PseudoRandomPool();
|
||||
|
||||
unsigned char _data[BINARY_LENGTH];
|
||||
};
|
||||
@ -48,16 +47,10 @@ template<unsigned int SIZE> bool operator!=(const FixedSizeData<SIZE> &lhs, cons
|
||||
template<unsigned int SIZE> constexpr unsigned int FixedSizeData<SIZE>::BINARY_LENGTH;
|
||||
template<unsigned int SIZE> constexpr unsigned int FixedSizeData<SIZE>::STRING_LENGTH;
|
||||
|
||||
template<unsigned int SIZE>
|
||||
ThreadsafePseudoRandomPool &FixedSizeData<SIZE>::PseudoRandomPool() {
|
||||
static ThreadsafePseudoRandomPool singleton;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
template<unsigned int SIZE>
|
||||
FixedSizeData<SIZE> FixedSizeData<SIZE>::CreatePseudoRandom() {
|
||||
FixedSizeData<SIZE> result;
|
||||
PseudoRandomPool().GenerateBlock(result._data, BINARY_LENGTH);
|
||||
RandomPool::get(result._data, BINARY_LENGTH);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef MESSMER_CPPUTILS_DATA_THREADSAFEPSEUDORANDOMPOOL_H
|
||||
#define MESSMER_CPPUTILS_DATA_THREADSAFEPSEUDORANDOMPOOL_H
|
||||
|
||||
#include "../macros.h"
|
||||
#include <mutex>
|
||||
#include <cryptopp/cryptopp/osrng.h>
|
||||
|
||||
namespace cpputils {
|
||||
|
||||
//TODO Create more complete random library around CryptoPP (also offering OS_Random for example) and use it in FixedSizeDate::CreateRandom()/CreateOSRandom()
|
||||
//TODO Store a static CryptoPP::AutoSeededRandomPool (or multiple ones) and make constructor of
|
||||
// ThreadsafeRandomPool() be lightweight (i.e. not do seeding), so it can be called on each callsite.
|
||||
// Alternatively, use a singleton factory.
|
||||
|
||||
//TODO Test
|
||||
class ThreadsafePseudoRandomPool {
|
||||
public:
|
||||
ThreadsafePseudoRandomPool() { }
|
||||
|
||||
void GenerateBlock(byte *data, size_t size) {
|
||||
// TODO Provide multiple randomPools for parallelity instead of locking the only available one
|
||||
std::unique_lock <std::mutex> lock(_mutex);
|
||||
_pool.GenerateBlock(data, size);
|
||||
}
|
||||
|
||||
private:
|
||||
//TODO Make seeding use blocking=true (aka /dev/random instead of /dev/urandom) or offer a configuration option?
|
||||
CryptoPP::AutoSeededRandomPool _pool;
|
||||
std::mutex _mutex;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ThreadsafePseudoRandomPool);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
35
random/LoopThread.cpp
Normal file
35
random/LoopThread.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
#include "LoopThread.h"
|
||||
|
||||
namespace cpputils {
|
||||
|
||||
LoopThread::LoopThread(): _thread() {}
|
||||
|
||||
LoopThread::~LoopThread() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void LoopThread::start() {
|
||||
_thread = boost::thread(std::bind(&LoopThread::main, this));
|
||||
}
|
||||
|
||||
void LoopThread::stop() {
|
||||
_thread.interrupt();
|
||||
_thread.join();
|
||||
}
|
||||
|
||||
void LoopThread::main() {
|
||||
try {
|
||||
while(true) {
|
||||
loopIteration();
|
||||
}
|
||||
} catch (const boost::thread_interrupted &e) {
|
||||
//Do nothing, exit thread.
|
||||
} catch (const std::exception &e) {
|
||||
//TODO Think about logging
|
||||
std::cerr << "LoopThread crashed: " << e.what() << std::endl;
|
||||
} catch (...) {
|
||||
//TODO Think about logging
|
||||
std::cerr << "LoopThread crashed" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
25
random/LoopThread.h
Normal file
25
random/LoopThread.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#ifndef MESSMER_CPPUTILS_RANDOM_LOOPTHREAD_H
|
||||
#define MESSMER_CPPUTILS_RANDOM_LOOPTHREAD_H
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
namespace cpputils {
|
||||
//TODO Test
|
||||
//TODO Move out of "random" folder into own library folder
|
||||
class LoopThread {
|
||||
public:
|
||||
LoopThread();
|
||||
virtual ~LoopThread();
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
virtual void loopIteration() = 0;
|
||||
|
||||
private:
|
||||
void main();
|
||||
boost::thread _thread;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
1
random/RandomDataBuffer.cpp
Normal file
1
random/RandomDataBuffer.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "RandomDataBuffer.h"
|
50
random/RandomDataBuffer.h
Normal file
50
random/RandomDataBuffer.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#ifndef MESSMER_CPPUTILS_RANDOM_RANDOMDATABUFFER_H
|
||||
#define MESSMER_CPPUTILS_RANDOM_RANDOMDATABUFFER_H
|
||||
|
||||
#include "../data/Data.h"
|
||||
#include "../assert/assert.h"
|
||||
|
||||
namespace cpputils {
|
||||
//TODO Test
|
||||
class RandomDataBuffer {
|
||||
public:
|
||||
RandomDataBuffer();
|
||||
|
||||
size_t size() const;
|
||||
|
||||
void get(void *target, size_t bytes);
|
||||
|
||||
void add(Data data);
|
||||
|
||||
private:
|
||||
size_t _usedUntil;
|
||||
Data _data;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RandomDataBuffer);
|
||||
};
|
||||
|
||||
inline RandomDataBuffer::RandomDataBuffer() : _data(0) {
|
||||
}
|
||||
|
||||
inline size_t RandomDataBuffer::size() const {
|
||||
return _data.size() - _usedUntil;
|
||||
}
|
||||
|
||||
inline void RandomDataBuffer::get(void *target, size_t numBytes) {
|
||||
ASSERT(size() >= numBytes, "Too many bytes requested. Buffer is smaller.");
|
||||
std::memcpy(target, _data.dataOffset(_usedUntil), numBytes);
|
||||
_usedUntil += numBytes;
|
||||
}
|
||||
|
||||
inline void RandomDataBuffer::add(Data data) {
|
||||
// Concatenate old and new random data
|
||||
Data newdata(_data.size() + data.size());
|
||||
std::memcpy(newdata.data(), _data.data(), _data.size());
|
||||
std::memcpy(newdata.dataOffset(_data.size()), data.data(), data.size());
|
||||
_data = std::move(newdata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
26
random/RandomGeneratorThread.cpp
Normal file
26
random/RandomGeneratorThread.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "RandomGeneratorThread.h"
|
||||
|
||||
namespace cpputils {
|
||||
|
||||
RandomGeneratorThread::RandomGeneratorThread(ThreadsafeRandomDataBuffer *buffer, size_t minSize, size_t maxSize)
|
||||
: _randomGenerator(), _buffer(buffer), _minSize(minSize), _maxSize(maxSize) {
|
||||
ASSERT(_maxSize >= _minSize, "Invalid parameters");
|
||||
}
|
||||
|
||||
void RandomGeneratorThread::loopIteration() {
|
||||
_buffer->waitUntilSizeIsLessThan(_minSize);
|
||||
size_t neededRandomDataSize = _maxSize - _buffer->size();
|
||||
ASSERT(_maxSize > _buffer->size(), "This could theoretically fail if another thread refilled the buffer. But we should be the only refilling thread.");
|
||||
Data randomData = _generateRandomData(neededRandomDataSize);
|
||||
_buffer->add(std::move(randomData));
|
||||
}
|
||||
|
||||
Data RandomGeneratorThread::_generateRandomData(size_t size) {
|
||||
std::cout << "Generate " << static_cast<double>(size)/1024 << " KB" << std::endl;
|
||||
Data newRandom(size);
|
||||
_randomGenerator.GenerateBlock(static_cast<byte*>(newRandom.data()), size);
|
||||
std::cout << "Generate " << static_cast<double>(size)/1024 << " KB done" << std::endl;
|
||||
return newRandom;
|
||||
}
|
||||
|
||||
}
|
28
random/RandomGeneratorThread.h
Normal file
28
random/RandomGeneratorThread.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#ifndef MESSMER_CPPUTILS_RANDOM_RANDOMGENERATORTHREAD_H
|
||||
#define MESSMER_CPPUTILS_RANDOM_RANDOMGENERATORTHREAD_H
|
||||
|
||||
#include "LoopThread.h"
|
||||
#include "ThreadsafeRandomDataBuffer.h"
|
||||
#include <cryptopp/cryptopp/osrng.h>
|
||||
|
||||
namespace cpputils {
|
||||
//TODO Test
|
||||
class RandomGeneratorThread: public LoopThread {
|
||||
public:
|
||||
RandomGeneratorThread(ThreadsafeRandomDataBuffer *buffer, size_t minSize, size_t maxSize);
|
||||
void loopIteration() override;
|
||||
|
||||
private:
|
||||
Data _generateRandomData(size_t size);
|
||||
|
||||
CryptoPP::AutoSeededRandomPool _randomGenerator;
|
||||
ThreadsafeRandomDataBuffer *_buffer;
|
||||
size_t _minSize;
|
||||
size_t _maxSize;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RandomGeneratorThread);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
8
random/RandomPool.cpp
Normal file
8
random/RandomPool.cpp
Normal file
@ -0,0 +1,8 @@
|
||||
#include "RandomPool.h"
|
||||
|
||||
namespace cpputils {
|
||||
|
||||
constexpr size_t RandomPool::MIN_BUFFER_SIZE;
|
||||
constexpr size_t RandomPool::MAX_BUFFER_SIZE;
|
||||
std::mutex RandomPool::_mutex;
|
||||
}
|
44
random/RandomPool.h
Normal file
44
random/RandomPool.h
Normal file
@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#ifndef MESSMER_CPPUTILS_RANDOM_RANDOMPOOL_H
|
||||
#define MESSMER_CPPUTILS_RANDOM_RANDOMPOOL_H
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
#include "ThreadsafeRandomDataBuffer.h"
|
||||
#include "RandomGeneratorThread.h"
|
||||
#include <mutex>
|
||||
|
||||
namespace cpputils {
|
||||
//TODO Test
|
||||
class RandomPool final {
|
||||
public:
|
||||
static void get(void *target, size_t bytes);
|
||||
|
||||
private:
|
||||
static constexpr size_t MIN_BUFFER_SIZE = 1*1024*1024; // 1MB
|
||||
static constexpr size_t MAX_BUFFER_SIZE = 2*1024*1024; // 2MB
|
||||
|
||||
RandomPool();
|
||||
static RandomPool &singleton();
|
||||
static std::mutex _mutex;
|
||||
|
||||
ThreadsafeRandomDataBuffer _buffer;
|
||||
RandomGeneratorThread _refillThread;
|
||||
DISALLOW_COPY_AND_ASSIGN(RandomPool);
|
||||
};
|
||||
|
||||
inline RandomPool &RandomPool::singleton() {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
static RandomPool singleton;
|
||||
return singleton;
|
||||
}
|
||||
|
||||
inline void RandomPool::get(void *target, size_t bytes) {
|
||||
singleton()._buffer.get(target, bytes);
|
||||
}
|
||||
|
||||
inline RandomPool::RandomPool(): _buffer(), _refillThread(&_buffer, MIN_BUFFER_SIZE, MAX_BUFFER_SIZE) {
|
||||
_refillThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
80
random/ThreadsafeRandomDataBuffer.h
Normal file
80
random/ThreadsafeRandomDataBuffer.h
Normal file
@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
#ifndef MESSMER_CPPUTILS_RANDOM_THREADSAFERANDOMDATABUFFER_H
|
||||
#define MESSMER_CPPUTILS_RANDOM_THREADSAFERANDOMDATABUFFER_H
|
||||
|
||||
#include "../data/Data.h"
|
||||
#include "../assert/assert.h"
|
||||
#include "RandomDataBuffer.h"
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
namespace cpputils {
|
||||
//TODO Test
|
||||
class ThreadsafeRandomDataBuffer {
|
||||
public:
|
||||
ThreadsafeRandomDataBuffer();
|
||||
|
||||
size_t size() const;
|
||||
|
||||
void get(void *target, size_t numBytes);
|
||||
|
||||
void add(Data data);
|
||||
|
||||
void waitUntilSizeIsLessThan(size_t numBytes);
|
||||
|
||||
private:
|
||||
size_t _get(void *target, size_t bytes);
|
||||
|
||||
RandomDataBuffer _buffer;
|
||||
mutable boost::mutex _mutex;
|
||||
boost::condition_variable _dataAddedCv;
|
||||
// _dataGottenCv needs to be boost::condition_variable and not std::condition_variable, because the
|
||||
// RandomGeneratorThread calling ThreadsafeRandomDataBuffer::waitUntilSizeIsLessThan() needs the waiting to be
|
||||
// interruptible to stop the thread in RandomGeneratorThread::stop() or in the RandomGeneratorThread destructor.
|
||||
boost::condition_variable _dataGottenCv;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ThreadsafeRandomDataBuffer);
|
||||
};
|
||||
|
||||
inline ThreadsafeRandomDataBuffer::ThreadsafeRandomDataBuffer() {
|
||||
}
|
||||
|
||||
inline size_t ThreadsafeRandomDataBuffer::size() const {
|
||||
boost::unique_lock<boost::mutex> lock(_mutex);
|
||||
return _buffer.size();
|
||||
}
|
||||
|
||||
inline void ThreadsafeRandomDataBuffer::get(void *target, size_t numBytes) {
|
||||
size_t alreadyGotten = 0;
|
||||
while (alreadyGotten < numBytes) {
|
||||
size_t got = _get(static_cast<uint8_t*>(target)+alreadyGotten, numBytes);
|
||||
alreadyGotten += got;
|
||||
ASSERT(alreadyGotten <= numBytes, "Got too many bytes");
|
||||
}
|
||||
}
|
||||
|
||||
inline size_t ThreadsafeRandomDataBuffer::_get(void *target, size_t numBytes) {
|
||||
boost::unique_lock<boost::mutex> lock(_mutex);
|
||||
_dataAddedCv.wait(lock, [this, numBytes] {
|
||||
return _buffer.size() > 0;
|
||||
});
|
||||
size_t gettableBytes = std::min(_buffer.size(), numBytes);
|
||||
_buffer.get(target, gettableBytes);
|
||||
_dataGottenCv.notify_all();
|
||||
return gettableBytes;
|
||||
}
|
||||
|
||||
inline void ThreadsafeRandomDataBuffer::add(Data data) {
|
||||
boost::unique_lock<boost::mutex> lock(_mutex);
|
||||
_buffer.add(std::move(data));
|
||||
_dataAddedCv.notify_all();
|
||||
}
|
||||
|
||||
inline void ThreadsafeRandomDataBuffer::waitUntilSizeIsLessThan(size_t numBytes) {
|
||||
boost::unique_lock<boost::mutex> lock(_mutex);
|
||||
_dataGottenCv.wait(lock, [this, numBytes] {
|
||||
return _buffer.size() < numBytes;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user