Initial commit
This commit is contained in:
71
src/Helpers/BiMap.hpp
Normal file
71
src/Helpers/BiMap.hpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
namespace Bloom
|
||||
{
|
||||
/**
|
||||
* Simple bidirectional map
|
||||
*
|
||||
* This should only be used for small maps, with small elements (enums, string literals, etc).
|
||||
*
|
||||
* TODO: This needs some work - was written as a quick implementation with minimal requirements.
|
||||
* TODO: Add support for inserting/deleting elements (outside of construction).
|
||||
*
|
||||
* @tparam TypeA
|
||||
* @tparam TypeB
|
||||
*/
|
||||
template<typename TypeA, typename TypeB>
|
||||
class BiMap
|
||||
{
|
||||
private:
|
||||
std::unordered_map<TypeA, TypeB> map;
|
||||
std::unordered_map<TypeB, typename std::unordered_map<TypeA, TypeB>::iterator> flippedMap;
|
||||
|
||||
public:
|
||||
BiMap(std::initializer_list<std::pair<TypeA, TypeB>> elements) {
|
||||
for (auto it = elements.begin(); it != elements.end(); ++it) {
|
||||
auto insertResultPair = this->map.insert(std::pair<TypeA, TypeB>{it->first, it->second});
|
||||
this->flippedMap.insert(
|
||||
std::pair<TypeB, typename std::unordered_map<TypeA, TypeB>::iterator>{
|
||||
it->second,
|
||||
insertResultPair.first
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool contains(TypeA key) const {
|
||||
return this->map.find(key) != this->map.end();
|
||||
}
|
||||
|
||||
bool contains(TypeB key) const {
|
||||
return this->flippedMap.find(key) != this->flippedMap.end();
|
||||
}
|
||||
|
||||
std::optional<TypeB> valueAt(TypeA key) const {
|
||||
std::optional<TypeB> output;
|
||||
|
||||
if (this->contains(key)) {
|
||||
output = this->map.find(key)->second;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::optional<TypeA> valueAt(TypeB key) const {
|
||||
std::optional<TypeA> output;
|
||||
|
||||
if (this->contains(key)) {
|
||||
output = this->flippedMap.find(key)->second->first;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::unordered_map<TypeA, TypeB> getMap() {
|
||||
return this->map;
|
||||
}
|
||||
};
|
||||
}
|
||||
42
src/Helpers/DateTime.hpp
Normal file
42
src/Helpers/DateTime.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <QDateTime>
|
||||
|
||||
namespace Bloom
|
||||
{
|
||||
/**
|
||||
* Some (maybe all) QDateTime static functions are not thread-safe and thus can result in data races.
|
||||
* This trivial helper class wraps some of these functions and employs a mutex to prevent data races.
|
||||
*/
|
||||
class DateTime
|
||||
{
|
||||
private:
|
||||
static inline std::mutex currentDateTimeMutex;
|
||||
|
||||
public:
|
||||
/**
|
||||
* The QDateTime::currentDateTime() static function is not thread-safe. This may be caused by the
|
||||
* underlying interfacing with the system clock.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static QDateTime currentDateTime() {
|
||||
auto lock = std::unique_lock(DateTime::currentDateTimeMutex);
|
||||
return QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* The QDateTime::timeZoneAbbreviation() is a non-static member function but it may still interface with the
|
||||
* system clock. This can result in race conditions when called simultaneously to QDateTime::currentDateTime(),
|
||||
* and so any calls to it must require possession of the mutex.
|
||||
*
|
||||
* @param dateTime
|
||||
* @return
|
||||
*/
|
||||
static QString getTimeZoneAbbreviation(const QDateTime& dateTime) {
|
||||
auto lock = std::unique_lock(DateTime::currentDateTimeMutex);
|
||||
return dateTime.timeZoneAbbreviation();
|
||||
}
|
||||
};
|
||||
}
|
||||
65
src/Helpers/EventNotifier.hpp
Normal file
65
src/Helpers/EventNotifier.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/eventfd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "src/Exceptions/Exception.hpp"
|
||||
|
||||
namespace Bloom
|
||||
{
|
||||
using namespace Exceptions;
|
||||
|
||||
/**
|
||||
* The EventNotifier class provides a means to interrupt a thread that is blocked by an IO call.
|
||||
*
|
||||
* It's implementation is rather simple: It uses a Linux event file descriptor (sys/eventfd.h), which should be used
|
||||
* along side epoll() to interrupt a blocking call that is waiting on the epoll file descriptor.
|
||||
*
|
||||
* The EventListener can hold an instance to EventNotifier, where it will invoke EventNotifier::notify() everytime
|
||||
* a new event is registered on the listener.
|
||||
*
|
||||
* @TODO: This could do with some cleaning. It's a bit hacky. Also, maybe add the ability to register the event
|
||||
* file descriptor to an epoll instance within a public method (instead of relying on the caller to do this
|
||||
* themselves via EventNotifier::getFileDescriptor()).
|
||||
*/
|
||||
class EventNotifier
|
||||
{
|
||||
private:
|
||||
int fileDescriptor = -1;
|
||||
|
||||
public:
|
||||
EventNotifier() = default;
|
||||
|
||||
void init() {
|
||||
this->fileDescriptor = ::eventfd(0, EFD_NONBLOCK);
|
||||
|
||||
if (this->fileDescriptor < -1) {
|
||||
throw Exception("Failed to create new eventfd object - error number: " + std::to_string(errno));
|
||||
}
|
||||
}
|
||||
|
||||
int getFileDescriptor() {
|
||||
return this->fileDescriptor;
|
||||
}
|
||||
|
||||
void notify() {
|
||||
if (::eventfd_write(this->fileDescriptor, 1) < 0) {
|
||||
throw Exception("Failed to increment eventfd counter - error number: " + std::to_string(errno));
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
eventfd_t counter;
|
||||
if (::eventfd_read(this->fileDescriptor, &counter) < 0 && errno != EAGAIN) {
|
||||
throw Exception("Failed to clear EventNotifier object - eventfd_read failed - error number: "
|
||||
+ std::to_string(errno));
|
||||
}
|
||||
}
|
||||
|
||||
void close() {
|
||||
::close(this->fileDescriptor);
|
||||
}
|
||||
};
|
||||
}
|
||||
55
src/Helpers/SyncSafe.hpp
Normal file
55
src/Helpers/SyncSafe.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace Bloom
|
||||
{
|
||||
/**
|
||||
* Template for synchronization safe types.
|
||||
*
|
||||
* Just a convenient template that allows us to create thread safe types without having to write
|
||||
* the bloat of mutexes, unique_locks, etc etc.
|
||||
*
|
||||
* @TODO Might be an idea to use an off-the-shelf solution for this, as there are a few available.
|
||||
*
|
||||
* @tparam Type
|
||||
*/
|
||||
template<typename Type>
|
||||
class SyncSafe
|
||||
{
|
||||
private:
|
||||
std::mutex mutex;
|
||||
Type value;
|
||||
public:
|
||||
SyncSafe() = default;
|
||||
|
||||
explicit SyncSafe(Type value): value(value) {
|
||||
};
|
||||
|
||||
void setValue(Type value) {
|
||||
auto lock = std::unique_lock(this->mutex);
|
||||
this->value = value;
|
||||
};
|
||||
|
||||
Type getValue() {
|
||||
auto lock = std::unique_lock(this->mutex);
|
||||
return this->value;
|
||||
};
|
||||
|
||||
Type& getReference() {
|
||||
return this->value;
|
||||
};
|
||||
|
||||
void lock() {
|
||||
this->mutex.lock();
|
||||
};
|
||||
|
||||
void unlock() {
|
||||
this->mutex.unlock();
|
||||
};
|
||||
|
||||
std::unique_lock<std::mutex> acquireLock() {
|
||||
return std::unique_lock(this->mutex);
|
||||
};
|
||||
};
|
||||
}
|
||||
50
src/Helpers/Thread.hpp
Normal file
50
src/Helpers/Thread.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <csignal>
|
||||
#include <cassert>
|
||||
|
||||
#include "SyncSafe.hpp"
|
||||
|
||||
namespace Bloom
|
||||
{
|
||||
enum class ThreadState
|
||||
{
|
||||
UNINITIALISED,
|
||||
READY,
|
||||
STOPPED,
|
||||
STARTING,
|
||||
SHUTDOWN_INITIATED,
|
||||
};
|
||||
|
||||
class Thread
|
||||
{
|
||||
private:
|
||||
SyncSafe<ThreadState> state = SyncSafe<ThreadState>(ThreadState::UNINITIALISED);
|
||||
|
||||
protected:
|
||||
virtual void setState(ThreadState state) {
|
||||
this->state.setValue(state);
|
||||
};
|
||||
|
||||
/**
|
||||
* Disables signal interrupts on current thread.
|
||||
*/
|
||||
void blockAllSignalsOnCurrentThread() {
|
||||
sigset_t set = {};
|
||||
sigfillset(&set);
|
||||
sigprocmask(SIG_SETMASK, &set, NULL);
|
||||
};
|
||||
|
||||
void setName(std::string name) {
|
||||
// POSIX thread names cannot exceed 16 characters, including the terminating null byte.
|
||||
assert(name.size() <= 15);
|
||||
|
||||
pthread_setname_np(pthread_self(), name.c_str());
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ThreadState getState() {
|
||||
return this->state.getValue();
|
||||
};
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user