Skip to content

Commit

Permalink
Added dlsm::Memory::checkAlignment<>() and dlsm::Signal
Browse files Browse the repository at this point in the history
  • Loading branch information
pkarneliuk committed Apr 21, 2024
1 parent 2e02818 commit e2532fb
Show file tree
Hide file tree
Showing 7 changed files with 259 additions and 0 deletions.
29 changes: 29 additions & 0 deletions include/impl/Memory.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include <bit>
#include <format>
#include <memory>
#include <stdexcept>
#include <type_traits>

#include "impl/Str.hpp"

namespace dlsm::Memory {

constexpr std::size_t alignmentOf(const void* address) noexcept {
if (address == nullptr) return 0;
return 1 << std::countr_zero(std::bit_cast<std::uintptr_t>(address));
}

template <typename T>
constexpr void checkAlignment(const T* value, const std::size_t expected = alignof(std::decay<T>)) {
if (!std::has_single_bit(expected))
throw std::invalid_argument{std::format("Alignment must be power of two:{}", expected)};
const auto address = std::bit_cast<const void*>(value);
const auto alignment = alignmentOf(address);
if (alignment < expected)
throw std::runtime_error{std::format("Adderss: {} (alignment:{}) of '{}' is not aligned to:{}", address,
alignment, dlsm::Str::typenameOf<T>(), expected)};
}

} // namespace dlsm::Memory
78 changes: 78 additions & 0 deletions include/impl/Signal.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#pragma once

#include <signal.h>

#include <array>
#include <atomic>
#include <string>

namespace dlsm::Signal {

enum Type : int {
NONE = 0,
BUS = SIGBUS,
INT = SIGINT,
TERM = SIGTERM,
HUP = SIGHUP,
KILL = SIGKILL,
ABORT = SIGABRT,
CHLD = SIGCHLD,
};

std::string str(const Type signal);
std::string str(const siginfo_t& info);
void send(const Type signal);

using Handler = void (*)(int signal, siginfo_t* info, void* ucontext);

struct Guard {
Guard(const Type signal, const Handler callback);
Guard(Guard&& that) noexcept;
~Guard() noexcept(false);

bool restoring() const { return restore_; }

private:
struct sigaction set(const Type signal, const Handler callback);

struct sigaction previous_ = {};
const Type signal_{Type::NONE};
bool restore_{true};
};

template <Type... signals>
struct WatcherOf {
WatcherOf() : guards_{Guard{signals, handler}...} {}
~WatcherOf() { instance().type.store(Type::NONE); }

void wait() const noexcept { instance().type.wait(Type::NONE); }

operator bool() const noexcept { return instance().type.load() != Type::NONE; }

static Type value() { return instance().type.load(); }
static siginfo_t info() { return instance().info.load(); }

private:
struct Data {
std::atomic<Type> type{Type::NONE};
std::atomic<siginfo_t> info = {};
};

static Data& instance() {
static Data instance;
return instance;
}

static void handler(int signal, siginfo_t* info, void*) noexcept {
auto& data = instance();
data.info.store(*info);
data.type.store(static_cast<Type>(signal));
data.type.notify_all();
}

std::array<Guard, sizeof...(signals)> guards_;
};

using Termination = WatcherOf<Type::TERM, Type::INT>;

} // namespace dlsm::Signal
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ target_include_directories(dlsm PUBLIC
target_sources (dlsm PRIVATE
Logger.cpp
SharedMemory.cpp
Signal.cpp
Str.cpp
Thread.cpp
Transport.cpp
Expand Down
55 changes: 55 additions & 0 deletions src/Signal.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include "impl/Signal.hpp"

#include <cstring>
#include <format>
#include <system_error>

namespace dlsm::Signal {

std::string str(const Type signal) {
const auto description = ::sigdescr_np(signal);
// C++23 std::to_underlying()
if (description == nullptr) return "Unknown signal " + std::to_string(static_cast<int>(signal));
return description;
}

std::string str(const siginfo_t& i) { return std::format("PID:{} signal:{} addr:{}", i.si_pid, i.si_signo, i.si_addr); }

void send(const Type signal) {
if (0 != ::raise(signal)) {
throw std::system_error(errno, std::generic_category(), "Signal::send(" + std::to_string(signal) + ") failed");
}
}

Guard::Guard(const Type signal, const Handler callback) : previous_{set(signal, callback)}, signal_{signal} {}
Guard::Guard(Guard&& that) noexcept : previous_{that.previous_}, signal_{that.signal_}, restore_{that.restore_} {
that.restore_ = false;
struct sigaction empty = {};
that.previous_ = empty;
}
Guard::~Guard() noexcept(false) {
if (!restore_) return;
if (0 != ::sigaction(signal_, &previous_, nullptr)) {
throw std::system_error(errno, std::generic_category(),
"sigaction() failed to restore callback for signal:" + str(signal_));
}
}

struct sigaction Guard::set(const Type signal, const Handler callback) {
struct sigaction action = {};
if (0 != ::sigemptyset(&action.sa_mask)) {
throw std::system_error(errno, std::generic_category(), "sigemptyset()");
}

action.sa_flags = SA_SIGINFO;
action.sa_sigaction = callback;

struct sigaction previous = {};
if (0 != ::sigaction(signal, &action, &previous)) {
throw std::system_error(errno, std::generic_category(), "sigaction() failed to set callback: " + str(signal));
}

return previous;
}

} // namespace dlsm::Signal
2 changes: 2 additions & 0 deletions tests/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ target_sources (unit PRIVATE
TestClock.cpp
TestLock.cpp
TestLogger.cpp
TestMemory.cpp
TestSharedMemory.cpp
TestSignal.cpp
TestStr.cpp
TestThread.cpp
TestTransport.cpp
Expand Down
29 changes: 29 additions & 0 deletions tests/unit/TestMemory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include "impl/Memory.hpp"
#include "unit/Unit.hpp"

using namespace dlsm::Memory;

TEST(Memory, alignmentOf) {
EXPECT_EQ(alignmentOf(reinterpret_cast<void*>(0UL)), 0);
EXPECT_EQ(alignmentOf(reinterpret_cast<void*>(1UL)), 1);
EXPECT_EQ(alignmentOf(reinterpret_cast<void*>(2UL)), 2);
EXPECT_EQ(alignmentOf(reinterpret_cast<void*>(3UL)), 1);
EXPECT_EQ(alignmentOf(reinterpret_cast<void*>(4UL)), 4);
EXPECT_EQ(alignmentOf(reinterpret_cast<void*>(5UL)), 1);
EXPECT_EQ(alignmentOf(reinterpret_cast<void*>(6UL)), 2);
EXPECT_EQ(alignmentOf(reinterpret_cast<void*>(7UL)), 1);
EXPECT_EQ(alignmentOf(reinterpret_cast<void*>(8UL)), 8);
EXPECT_EQ(alignmentOf(reinterpret_cast<void*>(64UL)), 64);
}

TEST(Memory, checkAlignment) {
int value = 0;
EXPECT_THAT([&] { checkAlignment(&value, 3); },
ThrowsMessage<std::invalid_argument>("Alignment must be power of two:3"));

int* pointer = reinterpret_cast<int*>(1);
EXPECT_THAT([&] { checkAlignment(pointer, 64); },
ThrowsMessage<std::runtime_error>("Adderss: 0x1 (alignment:1) of 'int' is not aligned to:64"));

EXPECT_NO_THROW(checkAlignment(&value));
}
65 changes: 65 additions & 0 deletions tests/unit/TestSignal.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include <barrier>
#include <thread>

#include "impl/Signal.hpp"
#include "unit/Unit.hpp"

namespace {
volatile int handler_called = 0; // NOLINT
}

TEST(Signal, Type) {
EXPECT_EQ(dlsm::Signal::str(dlsm::Signal::NONE), "Unknown signal 0");
EXPECT_EQ(dlsm::Signal::str(dlsm::Signal::BUS), "Bus error");

EXPECT_NO_THROW(dlsm::Signal::send(dlsm::Signal::NONE););

EXPECT_THAT([] { dlsm::Signal::send(static_cast<dlsm::Signal::Type>(-1)); },
ThrowsMessage<std::runtime_error>(StartsWith("Signal::send(-1) failed: Invalid argument")));
}

TEST(Signal, Guard) {
EXPECT_THAT([] { dlsm::Signal::Guard(dlsm::Signal::NONE, nullptr); },
ThrowsMessage<std::runtime_error>(StartsWith("sigaction() failed to set callback: Unknown signal 0")));

EXPECT_NO_THROW(dlsm::Signal::Guard(dlsm::Signal::BUS, nullptr));

dlsm::Signal::Handler bus = [](int signal, siginfo_t*, void*) { handler_called = signal; };
dlsm::Signal::Guard A(dlsm::Signal::BUS, bus);
EXPECT_TRUE(A.restoring());
dlsm::Signal::Guard B{std::move(A)};
EXPECT_TRUE(B.restoring());

EXPECT_EQ(handler_called, 0);
dlsm::Signal::send(dlsm::Signal::BUS);
EXPECT_EQ(handler_called, dlsm::Signal::BUS);
}

TEST(Signal, Termintaion) {
{
dlsm::Signal::Termination watcher;
EXPECT_FALSE(watcher);
dlsm::Signal::send(dlsm::Signal::TERM);
EXPECT_TRUE(watcher);
}

{
std::barrier sync(2);
dlsm::Signal::Termination watcher;
std::jthread t([&] {
sync.arrive_and_wait();
dlsm::Signal::send(dlsm::Signal::TERM);
});

EXPECT_FALSE(watcher);
sync.arrive_and_wait();
watcher.wait();
EXPECT_TRUE(watcher);
EXPECT_EQ(watcher.value(), dlsm::Signal::TERM);
// EXPECT_EQ(dlsm::Signal::str(watcher.info()), "");

EXPECT_THAT(dlsm::Signal::str(watcher.info()), MatchesRegex(R"(^PID\:\d+ signal\:\d+ addr:0x[0-9a-fA-F]+$)"));
}

EXPECT_EQ(dlsm::Signal::Termination::value(), dlsm::Signal::NONE);
}

0 comments on commit e2532fb

Please sign in to comment.