-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added dlsm::Memory::checkAlignment<>() and dlsm::Signal
- Loading branch information
1 parent
2e02818
commit e2532fb
Showing
7 changed files
with
259 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |