libos: Add basic IPC message framework
This commit is contained in:
parent
56269f5187
commit
a2fd838f5d
@ -16,6 +16,7 @@ set(SOURCES
|
|||||||
src/Security.cpp
|
src/Security.cpp
|
||||||
src/LocalServer.cpp
|
src/LocalServer.cpp
|
||||||
src/LocalClient.cpp
|
src/LocalClient.cpp
|
||||||
|
src/IPC.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(os ${SOURCES})
|
add_library(os ${SOURCES})
|
||||||
|
157
libos/include/os/IPC.h
Normal file
157
libos/include/os/IPC.h
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
/**
|
||||||
|
* @file IPC.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Inter-process communication primitives.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <os/LocalClient.h>
|
||||||
|
#include <os/LocalServer.h>
|
||||||
|
|
||||||
|
#define IPC_ENUM_SERVER(name) __##name##_SERVER_ERROR = 0
|
||||||
|
#define IPC_ENUM_CLIENT(name) __##name##_CLIENT_ERROR = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called to handle IPC events (client-side).
|
||||||
|
*
|
||||||
|
* @param conn The connection object being used.
|
||||||
|
* @param id The ID of the message.
|
||||||
|
* @return Result<void> Whether the operation succeded.
|
||||||
|
*/
|
||||||
|
extern Result<void> handle_ipc_client_event(os::LocalClient& conn, u8 id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Called to handle IPC events (server-side).
|
||||||
|
*
|
||||||
|
* @param conn The connection object being used.
|
||||||
|
* @param id The ID of the message.
|
||||||
|
* @return Result<void> Whether the operation succeded.
|
||||||
|
*/
|
||||||
|
extern Result<void> handle_ipc_server_event(os::LocalServer::Client& conn, u8 id);
|
||||||
|
|
||||||
|
namespace os
|
||||||
|
{
|
||||||
|
namespace IPC
|
||||||
|
{
|
||||||
|
static constexpr usize IPC_STRING_LENGTH = 256;
|
||||||
|
|
||||||
|
#define IPC_STRING(name) char name[os::IPC::IPC_STRING_LENGTH];
|
||||||
|
#define COPY_IPC_STRING(name) \
|
||||||
|
TRY(String::from_string_view(StringView::from_fixed_size_cstring(name, os::IPC::IPC_STRING_LENGTH)))
|
||||||
|
#define SET_IPC_STRING(name, value) strlcpy(name, value, os::IPC::IPC_STRING_LENGTH)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sends an IPC message without waiting for a reply.
|
||||||
|
*
|
||||||
|
* @tparam Client The type of the client interface being used to communicate.
|
||||||
|
* @tparam T The type of the message.
|
||||||
|
* @param client The connection object being used to communicate.
|
||||||
|
* @param message The IPC message.
|
||||||
|
* @return Result<void> Whether the operation succeded.
|
||||||
|
*/
|
||||||
|
template <typename Client, typename T> Result<void> send_async(Client& client, const T& message)
|
||||||
|
{
|
||||||
|
u8 id = T::id;
|
||||||
|
TRY(client.send_typed(id));
|
||||||
|
TRY(client.send_typed(message));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sends an error result to the IPC connection, indicating that an operation could not be performed.
|
||||||
|
*
|
||||||
|
* @tparam Client The type of the client interface being used to communicate.
|
||||||
|
* @param client The connection object being used to communicate.
|
||||||
|
* @param error The error code.
|
||||||
|
* @return Result<void> Whether the operation succeded.
|
||||||
|
*/
|
||||||
|
template <typename Client> Result<void> send_error(Client& client, int error)
|
||||||
|
{
|
||||||
|
u8 id = 0;
|
||||||
|
TRY(client.send_typed(id));
|
||||||
|
TRY(client.send_typed(error));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sends an IPC message and waits for a reply (client-only).
|
||||||
|
*
|
||||||
|
* @tparam ResponseType The type of the response.
|
||||||
|
* @tparam T The type of the message.
|
||||||
|
* @param client The connection object being used to communicate.
|
||||||
|
* @param message The IPC message.
|
||||||
|
* @param handler The function used to handle messages that do not match the reply.
|
||||||
|
* @return Result<ResponseType> An error, or the response.
|
||||||
|
*/
|
||||||
|
template <typename ResponseType, typename T>
|
||||||
|
Result<ResponseType> send_sync(os::LocalClient& client, const T& message,
|
||||||
|
decltype(handle_ipc_client_event) handler = handle_ipc_client_event)
|
||||||
|
{
|
||||||
|
u8 id = T::id;
|
||||||
|
TRY(client.send_typed(id));
|
||||||
|
TRY(client.send_typed(message));
|
||||||
|
|
||||||
|
// We allow receiving 5 messages of different types, but if those have passed and we still don't have a
|
||||||
|
// reply, fail with ENOMSG.
|
||||||
|
int max_other_messages = 5;
|
||||||
|
|
||||||
|
while (max_other_messages)
|
||||||
|
{
|
||||||
|
u8 response_id;
|
||||||
|
auto rc = client.recv_typed(response_id);
|
||||||
|
if (rc.has_error() && rc.error() == EAGAIN) continue;
|
||||||
|
|
||||||
|
if (response_id == 0) // Error result
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int code;
|
||||||
|
rc = client.recv_typed(code);
|
||||||
|
if (rc.has_error() && rc.error() == EAGAIN) continue;
|
||||||
|
return err(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response_id != ResponseType::id)
|
||||||
|
{
|
||||||
|
TRY(handler(client, response_id));
|
||||||
|
max_other_messages--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
ResponseType response;
|
||||||
|
rc = client.recv_typed(response);
|
||||||
|
if (rc.has_error() && rc.error() == EAGAIN) continue;
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err(ENOMSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check for new IPC messages on a connection and handle them appropriately.
|
||||||
|
*
|
||||||
|
* @param client The client connection.
|
||||||
|
* @param handler The function used to handle messages.
|
||||||
|
* @return Result<void> Whether the operation succeded.
|
||||||
|
*/
|
||||||
|
Result<void> check_for_messages(os::LocalClient& client,
|
||||||
|
decltype(handle_ipc_client_event) handler = handle_ipc_client_event);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check for new IPC messages on a connection and handle them appropriately.
|
||||||
|
*
|
||||||
|
* @param server The server connection.
|
||||||
|
* @param handler The function used to handle messages.
|
||||||
|
* @return Result<void> Whether the operation succeded.
|
||||||
|
*/
|
||||||
|
Result<void> check_for_messages(os::LocalServer::Client& server,
|
||||||
|
decltype(handle_ipc_server_event) handler = handle_ipc_server_event);
|
||||||
|
}
|
||||||
|
}
|
39
libos/src/IPC.cpp
Normal file
39
libos/src/IPC.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* @file IPC.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Inter-process communication primitives.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2023, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <os/IPC.h>
|
||||||
|
|
||||||
|
namespace os::IPC
|
||||||
|
{
|
||||||
|
Result<void> check_for_messages(os::LocalClient& client, decltype(handle_ipc_client_event) handler)
|
||||||
|
{
|
||||||
|
u8 id;
|
||||||
|
auto rc = client.recv_typed(id);
|
||||||
|
if (rc.has_error())
|
||||||
|
{
|
||||||
|
if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block.
|
||||||
|
return rc.release_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler(client, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> check_for_messages(os::LocalServer::Client& client, decltype(handle_ipc_server_event) handler)
|
||||||
|
{
|
||||||
|
u8 id;
|
||||||
|
auto rc = client.recv_typed(id);
|
||||||
|
if (rc.has_error())
|
||||||
|
{
|
||||||
|
if (rc.error() == EAGAIN) return {}; // No messages, and the caller does not want us to block.
|
||||||
|
return rc.release_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler(client, id);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user