Luna/libos/src/IPC.cpp
apio 53f8a583dc
Some checks failed
Build and test / build (push) Failing after 1m21s
libluna+libos+libui: Move Action to libluna and make it usable in the kernel
This commit adds an error-propagating constructor for Action and Function, which makes them usable in the kernel.
2024-10-19 21:25:17 +02:00

150 lines
3.7 KiB
C++

/**
* @file IPC.cpp
* @author apio (cloudapio.eu)
* @brief Inter-process communication primitives.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <errno.h>
#include <fcntl.h>
#include <luna/String.h>
#include <os/IPC.h>
#include <stdlib.h>
#include <sys/poll.h>
#include <unistd.h>
namespace os::IPC
{
Result<OwnedPtr<ClientConnection>> ClientConnection::adopt_connection(LocalServer::Client&& connection)
{
return adopt_owned_if_nonnull(new (std::nothrow) ClientConnection(move(connection)));
}
ClientConnection::ClientConnection(LocalServer::Client&& connection) : m_connection(move(connection))
{
}
Result<void> ClientConnection::check_for_messages()
{
if (m_ipc_in_progress)
{
m_ipc_in_progress = false;
m_message_handler(*this, m_ipc_saved_id, m_arg);
return {};
}
u8 id;
auto rc = m_connection.recv_typed(id);
if (rc.has_error())
{
if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block.
if (rc.error() == EINTR)
return {}; // Let the caller check for anything having happened because a signal handler ran.
return rc.release_error();
}
m_message_handler(*this, id, m_arg);
return {};
}
Result<void> ClientConnection::send_error(int error)
{
u8 id = 0;
TRY(m_connection.send_typed(id));
TRY(m_connection.send_typed(error));
return {};
}
Result<OwnedPtr<Client>> Client::connect(StringView path, bool blocking)
{
auto connection = TRY(LocalClient::connect(path, blocking));
return adopt_owned_if_nonnull(new (std::nothrow) Client(move(connection)));
}
Result<void> Client::check_for_messages()
{
if (m_ipc_in_progress)
{
m_ipc_in_progress = false;
m_message_handler(*this, m_ipc_saved_id, m_arg);
return {};
}
u8 id;
auto rc = m_connection->recv_typed(id);
if (rc.has_error())
{
if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block.
if (rc.error() == EINTR)
return {}; // Let the caller check for anything having happened because a signal handler ran.
return rc.release_error();
}
m_message_handler(*this, id, m_arg);
return {};
}
Client::Client(OwnedPtr<LocalClient>&& connection) : m_connection(move(connection))
{
}
Notifier Notifier::create()
{
Notifier result;
pipe(result.pfds);
fcntl(result.pfds[0], F_SETFD, FD_CLOEXEC);
return result;
}
void Notifier::hook()
{
auto value = String::format("%d"_sv, pfds[1]).release_value();
setenv("LUNA_NOTIFY_FD", value.chars(), 1);
}
void Notifier::unhook()
{
unsetenv("LUNA_NOTIFY_FD");
}
bool Notifier::wait(int timeout)
{
close(pfds[1]);
struct pollfd fds[] = { { .fd = pfds[0], .events = POLLIN, .revents = 0 } };
poll:
int result = poll(fds, 1, timeout);
if (result < 0 && errno == EINTR) goto poll;
close(pfds[0]);
return result > 0;
}
bool Notifier::run_and_wait(Action&& action, int timeout)
{
auto notifier = create();
notifier.hook();
action();
notifier.unhook();
return notifier.wait(timeout);
}
void notify_parent()
{
char* fd_string = getenv("LUNA_NOTIFY_FD");
if (!fd_string) return;
int fd = atoi(fd_string);
u8 data = 0;
write(fd, &data, 1);
close(fd);
unsetenv("LUNA_NOTIFY_FD");
}
}