libos: Add Function<Args...> and use that to make EventLoop callbacks more versatile

This commit is contained in:
apio 2024-01-18 20:52:36 +01:00
parent d4d748e153
commit 1cd355a8e8
Signed by: apio
GPG Key ID: B8A7D06E42258954
3 changed files with 132 additions and 23 deletions

View File

@ -9,11 +9,12 @@
#pragma once
#include <luna/Heap.h>
#include <luna/SharedPtr.h>
namespace os
{
/**
* @brief Wrapper for callable objects.
* @brief Wrapper for callable objects with no arguments.
*
* Certain callable types such as capture lambdas don't have a named type, thus they can't be passed to functions as
* arguments. The Action type acts as a callable wrapper for such functions, allowing them to be passed as arguments
@ -40,7 +41,16 @@ namespace os
*/
Action(Action&& other) : m_action(other.m_action)
{
other.m_action = nullptr;
other.m_action = {};
}
/**
* @brief Construct a new Action object, copying it from another one.
*
* @param other The old Action object to copy from.
*/
Action(const Action& other) : m_action(other.m_action)
{
}
/**
@ -52,7 +62,8 @@ namespace os
*/
template <typename T> Action(T&& function)
{
m_action = new (std::nothrow) ActionImpl<T>(move(function));
m_action =
adopt_shared_if_nonnull((ActionBase*)new (std::nothrow) ActionImpl<T>(move(function))).release_value();
}
/**
@ -65,8 +76,8 @@ namespace os
*/
template <typename T> Action& operator=(T&& function)
{
if (m_action) delete m_action;
m_action = new (std::nothrow) ActionImpl<T>(move(function));
m_action =
adopt_shared_if_nonnull((ActionBase*)new (std::nothrow) ActionImpl<T>(move(function))).release_value();
return *this;
}
@ -79,13 +90,8 @@ namespace os
return m_action->call();
}
~Action()
{
if (m_action) delete m_action;
}
private:
class ActionBase
class ActionBase : public Shareable
{
public:
virtual void call() = 0;
@ -108,6 +114,110 @@ namespace os
T m_function;
};
ActionBase* m_action;
SharedPtr<ActionBase> m_action;
};
/**
* @brief Wrapper for callable objects.
*
* Certain callable types such as capture lambdas don't have a named type, thus they can't be passed to functions as
* arguments. The Function type acts as a callable wrapper for such functions, allowing them to be passed as
* arguments to any function.
*/
template <class... Args> class Function
{
public:
/**
* @brief Construct a new empty Function object.
*
* Since it is not wrapping any callable object, this object is invalid and attempting to invoke its
* (nonexistent) callable object will result in a crash at runtime.
*/
Function()
{
m_action = nullptr;
}
/**
* @brief Construct a new Function object, moving it from another one.
*
* @param other The old Function object to move from. This object will become invalid.
*/
Function(Function&& other) : m_action(other.m_action)
{
other.m_action = {};
}
/**
* @brief Construct a new Function object, copying it from another one.
*
* @param other The old Function object to copy from.
*/
Function(const Function& other) : m_action(other.m_action)
{
}
/**
* @brief Construct a new Function object from a callable object.
*
* @tparam T The type of the callable object. Since this can be guessed by template deduction, the object
* doesn't actually need a type name.
* @param function The callable object to wrap.
*/
template <typename T> Function(T&& function)
{
m_action = adopt_shared_if_nonnull((FunctionBase*)new (std::nothrow) FunctionImpl<T>(move(function)))
.release_value();
}
/**
* @brief Assign a new callable object to this Function.
*
* @tparam T The type of the callable object. Since this can be guessed by template deduction, the object
* doesn't actually need a type name.
* @param function The callable object to wrap.
* @return Action& A reference to this object, as required by operator=().
*/
template <typename T> Function& operator=(T&& function)
{
m_action = adopt_shared_if_nonnull((FunctionBase*)new (std::nothrow) FunctionImpl<T>(move(function)))
.release_value();
return *this;
}
/**
* @brief Call the underlying object.
*/
void operator()(Args... args)
{
expect(m_action, "os::Function called with no underlying callable object");
return m_action->call(args...);
}
private:
class FunctionBase : public Shareable
{
public:
virtual void call(Args... args) = 0;
virtual ~FunctionBase() = default;
};
template <typename T> class FunctionImpl final : public FunctionBase
{
public:
FunctionImpl(T&& function) : m_function(move(function))
{
}
void call(Args... args) override
{
return m_function(args...);
}
private:
T m_function;
};
SharedPtr<FunctionBase> m_action;
};
}

View File

@ -47,7 +47,7 @@ namespace os
* is the file descriptor registered, and the second argument is the type of event (POLLIN or POLLHUP)
* @return Result<void> Whether the operation succeeded.
*/
Result<void> register_fd_listener(int fd, void (*listener)(int, int));
Result<void> register_fd_listener(int fd, Function<int, int> listener);
/**
* @brief Register a new POSIX signal handler.
@ -59,7 +59,7 @@ namespace os
* @param handler The function to call when this signal is caught.
* @return Result<void> Whether the operation succeeded.
*/
Result<void> register_signal_handler(int sig, void (*handler)(int));
Result<void> register_signal_handler(int sig, Function<int> handler);
/**
* @brief Run the event loop until it is asked to quit.
@ -76,8 +76,8 @@ namespace os
void quit(int status = 0);
private:
HashMap<int, void (*)(int, int)> m_fd_listeners;
HashMap<int, void (*)(int)> m_signal_handlers;
HashMap<int, Function<int, int>> m_fd_listeners;
HashMap<int, Function<int>> m_signal_handlers;
Vector<struct pollfd> m_pollfds;
static void handle_signal(int sig);

View File

@ -44,15 +44,14 @@ namespace os
return *s_the;
}
// FIXME: Support lambdas (by using os::Action). This means that os::Action needs variable parameter support.
Result<void> EventLoop::register_fd_listener(int fd, void (*listener)(int, int))
Result<void> EventLoop::register_fd_listener(int fd, Function<int, int> listener)
{
TRY(m_fd_listeners.try_set(fd, listener));
TRY(m_fd_listeners.try_set(fd, move(listener)));
TRY(m_pollfds.try_append({ .fd = fd, .events = POLLIN, .revents = 0 }));
return {};
}
Result<void> EventLoop::register_signal_handler(int sig, void (*handler)(int))
Result<void> EventLoop::register_signal_handler(int sig, Function<int> handler)
{
struct sigaction sa;
sa.sa_handler = EventLoop::handle_signal;
@ -60,7 +59,7 @@ namespace os
sigemptyset(&sa.sa_mask);
check(sigaction(sig, &sa, nullptr) == 0);
TRY(m_signal_handlers.try_set(sig, handler));
TRY(m_signal_handlers.try_set(sig, move(handler)));
return {};
}
@ -84,7 +83,7 @@ namespace os
read(m_signal_pipe[0], &sig, sizeof(int));
auto handler = m_signal_handlers.try_get(sig);
if (handler.has_value()) { handler.value()(sig); }
if (handler.has_value()) { (*handler.value_ptr())(sig); }
}
for (usize i = 0; i < m_fd_listeners.size(); i++)
@ -93,7 +92,7 @@ namespace os
if (pfd.revents)
{
auto handler = m_fd_listeners.try_get(pfd.fd);
if (handler.has_value()) handler.value()(pfd.fd, pfd.revents);
if (handler.has_value()) (*handler.value_ptr())(pfd.fd, pfd.revents);
}
}
}