Initial commit

This commit is contained in:
Nav
2021-04-04 21:04:12 +01:00
commit a29c5e1fec
549 changed files with 441216 additions and 0 deletions

71
src/Helpers/BiMap.hpp Normal file
View 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
View 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();
}
};
}

View 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
View 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
View 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();
};
};
}