apio
53f8a583dc
Some checks failed
Build and test / build (push) Failing after 1m21s
This commit adds an error-propagating constructor for Action and Function, which makes them usable in the kernel.
150 lines
3.7 KiB
C++
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");
|
|
}
|
|
}
|