libos: Add Function<Args...> and use that to make EventLoop callbacks more versatile
This commit is contained in:
parent
d4d748e153
commit
1cd355a8e8
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user