Files

142 lines
3.4 KiB
C++

#include "event.h"
#include "shared_file.h"
#include <new>
#include <stdexcept>
#include <sys/stat.h>
namespace raven {
std::optional<uint32_t> event_access(uint32_t requested)
{
if (requested & ~0xf21f0003U)
return std::nullopt;
uint32_t access = requested & 0x1f0003;
if (requested & 0x80000000U) access |= 0x120001;
if (requested & 0x40000000U) access |= 0x120002;
if (requested & 0x20000000U) access |= 0x120000;
if (requested & (0x10000000U | 0x02000000U)) access |= 0x1f0003;
return access;
}
struct EventObject::Shared {
std::atomic<uint32_t> signaled;
uint32_t type;
};
static_assert(std::atomic<uint32_t>::is_always_lock_free);
EventObject::EventObject(int descriptor) : type(0), local_signaled(false), descriptor(descriptor)
{
try {
struct stat information{};
if (descriptor < 0 || ::fstat(descriptor, &information) || information.st_size != sizeof(Shared))
throw std::runtime_error("invalid event storage");
void *mapped = ::mmap(nullptr, sizeof(Shared), PROT_READ | PROT_WRITE, MAP_SHARED, descriptor, 0);
if (mapped == MAP_FAILED)
throw std::runtime_error("cannot map event storage");
shared = static_cast<Shared *>(mapped);
if (shared->type > 1)
throw std::runtime_error("invalid event type");
type = shared->type;
listen_shared_changes();
++shared_object_count;
} catch (...) {
if (shared) ::munmap(shared, sizeof(Shared));
if (descriptor >= 0) ::close(descriptor);
throw;
}
}
EventObject::~EventObject()
{
if (shared) {
::munmap(shared, sizeof(Shared));
--shared_object_count;
}
if (descriptor >= 0) ::close(descriptor);
}
bool EventObject::describe(InheritedHandle &record) const
{
std::lock_guard lock(mutex);
if (!shared) {
const int created = ::memfd_create("raven-event", MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (created < 0)
return false;
if (::ftruncate(created, sizeof(Shared)) ||
::fcntl(created, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL)) {
::close(created);
return false;
}
void *mapped = ::mmap(nullptr, sizeof(Shared), PROT_READ | PROT_WRITE, MAP_SHARED, created, 0);
if (mapped == MAP_FAILED) {
::close(created);
return false;
}
shared = new (mapped) Shared{{uint32_t(local_signaled)}, type};
descriptor = created;
listen_shared_changes();
++shared_object_count;
changed.notify_all();
}
record.kind = "event";
record.descriptor = descriptor;
return true;
}
bool EventObject::is_set() const
{
return shared ? shared->signaled.load() != 0 : local_signaled;
}
bool EventObject::take()
{
if (type == 0)
return is_set();
if (shared)
return shared->signaled.exchange(0) != 0;
const bool previous = local_signaled;
local_signaled = false;
return previous;
}
int EventObject::signaled(uint32_t) const
{
std::lock_guard lock(mutex);
return is_set();
}
void EventObject::consume(uint32_t thread)
{
try_consume(thread);
}
bool EventObject::try_consume(uint32_t)
{
std::lock_guard lock(mutex);
return take();
}
int EventObject::set_state(bool signaled)
{
std::lock_guard lock(mutex);
const bool previous = shared ? shared->signaled.exchange(uint32_t(signaled)) != 0 : local_signaled;
local_signaled = signaled;
if (signaled) {
if (type == 0) {
for (const auto &waiter : waiters) waiter->ready = true;
waiters.clear();
} else if (!waiters.empty() && take()) {
waiters.front()->ready = true;
waiters.pop_front();
}
changed.notify_all();
}
if (shared)
announce_shared_change();
return previous;
}
}