Optimize Service Function Lookup

This moves from using std::function with a this pointer binding (which would likely cause a heap allocation) to returning the this pointer in a structure which implements operator() to do the call with it. It also moves to using const char* for strings from std::string_view which was pointless in this scenario due to it's usage being limited to being a C-string for the most part, it also integrates the class name directly into the string which allows us to avoid runtime string concatenation in libfmt and RTTI for finding the class name.
This commit is contained in:
PixelyIon 2021-03-20 01:19:55 +05:30 committed by ◱ Mark
parent 2b4a094cd8
commit 33f1a3e1b3
3 changed files with 38 additions and 20 deletions

View File

@ -20,9 +20,6 @@ namespace skyline::service::am {
*/ */
Result GetApplicationFunctions(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); Result GetApplicationFunctions(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
//#undef SFUNC_BASE
//#define SFUNC_BASE(id, Class, BaseClass, Function) std::pair<u32, std::pair<std::function<Result(Class*, type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>, std::string_view>>{id, {&CallBaseFunction<Class, BaseClass, BaseClass::Function>, #Function}}
SERVICE_DECL( SERVICE_DECL(
SFUNC_BASE(0x0, IApplicationProxy, BaseProxy, GetCommonStateGetter), SFUNC_BASE(0x0, IApplicationProxy, BaseProxy, GetCommonStateGetter),
SFUNC_BASE(0x1, IApplicationProxy, BaseProxy, GetSelfController), SFUNC_BASE(0x1, IApplicationProxy, BaseProxy, GetSelfController),

View File

@ -19,18 +19,18 @@ namespace skyline::service {
} }
Result service::BaseService::HandleRequest(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result service::BaseService::HandleRequest(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
std::pair<std::function<Result(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>, std::string_view> function; ServiceFunctionDescriptor function;
try { try {
function = GetServiceFunction(request.payload->value); function = GetServiceFunction(request.payload->value);
state.logger->DebugNoPrefix("Service: {} @ {}", function.second, GetName()); state.logger->DebugNoPrefix("Service: {}", function.name);
} catch (const std::out_of_range &) { } catch (const std::out_of_range &) {
state.logger->Warn("Cannot find function in service '{0}': 0x{1:X} ({1})", GetName(), static_cast<u32>(request.payload->value)); state.logger->Warn("Cannot find function in service '{0}': 0x{1:X} ({1})", GetName(), static_cast<u32>(request.payload->value));
return {}; return {};
} }
try { try {
return function.first(session, request, response); return function(session, request, response);
} catch (const std::exception &e) { } catch (const std::exception &e) {
throw exception("{} (Service: {} @ {})", e.what(), function.second, GetName()); throw exception("{} (Service: {})", e.what(), function.name);
} }
} }
} }

View File

@ -5,14 +5,25 @@
#include <kernel/ipc.h> #include <kernel/ipc.h>
#define SFUNC(id, Class, Function) std::pair<u32, std::pair<std::function<Result(Class*, type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>, std::string_view>>{id, {&Class::Function, #Function}} #define SERVICE_STRINGIFY(string) #string
#define SFUNC_BASE(id, Class, BaseClass, Function) std::pair<u32, std::pair<std::function<Result(Class*, type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>, std::string_view>>{id, {&CallBaseFunction<Class, BaseClass, decltype(&BaseClass::Function), &BaseClass::Function>, #Function}} #define SFUNC(id, Class, Function) std::pair<u32, std::pair<Result(Class::*)(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &), const char*>>{id, {&Class::Function, SERVICE_STRINGIFY(Class::Function)}}
#define SFUNC_BASE(id, Class, BaseClass, Function) std::pair<u32, std::pair<Result(Class::*)(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &), const char*>>{id, {&Class::CallBaseFunction<BaseClass, decltype(&BaseClass::Function), &BaseClass::Function>, SERVICE_STRINGIFY(Class::Function)}}
#define SERVICE_DECL_AUTO(name, value) decltype(value) name = value #define SERVICE_DECL_AUTO(name, value) decltype(value) name = value
#define SERVICE_DECL(...) \ #define SERVICE_DECL(...) \
private: \
template<typename BaseClass, typename BaseFunctionType, BaseFunctionType BaseFunction> \
Result CallBaseFunction(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { \
return (static_cast<BaseClass *>(this)->*BaseFunction)(session, request, response); \
} \
SERVICE_DECL_AUTO(functions, frz::make_unordered_map({__VA_ARGS__})); \ SERVICE_DECL_AUTO(functions, frz::make_unordered_map({__VA_ARGS__})); \
std::pair<std::function<Result(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>, std::string_view> GetServiceFunction(u32 id) override { \ protected: \
ServiceFunctionDescriptor GetServiceFunction(u32 id) override { \
auto& function{functions.at(id)}; \ auto& function{functions.at(id)}; \
return std::make_pair(std::bind(function.first, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), function.second); \ return ServiceFunctionDescriptor{ \
reinterpret_cast<DerivedService*>(this), \
reinterpret_cast<decltype(ServiceFunctionDescriptor::function)>(function.first), \
function.second \
}; \
} }
#define SRVREG(class, ...) std::make_shared<class>(state, manager, ##__VA_ARGS__) #define SRVREG(class, ...) std::make_shared<class>(state, manager, ##__VA_ARGS__)
@ -37,10 +48,20 @@ namespace skyline::service {
const DeviceState &state; const DeviceState &state;
ServiceManager &manager; ServiceManager &manager;
template<typename Class, typename BaseClass, typename BaseFunctionType, BaseFunctionType BaseFunction> class DerivedService; //!< A placeholder derived class which is used for class function semantics
static constexpr Result CallBaseFunction(Class *clazz, type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
return (static_cast<BaseClass *>(clazz)->*BaseFunction)(session, request, response); /**
* @brief A per-service-function descriptor with it's name and a function pointer to it
*/
struct ServiceFunctionDescriptor {
DerivedService *clazz; //!< A pointer to the class that this was derived from, it's used as the 'this' pointer for the function
Result (DerivedService::*function)(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &); //!< A function pointer to a HLE implementation of the service function
const char *name; //!< A pointer to a static string in the format "Class::Function" for the specific service class/function
constexpr Result operator()(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
return (clazz->*function)(session, request, response);
} }
};
public: public:
BaseService(const DeviceState &state, ServiceManager &manager) : state(state), manager(manager) {} BaseService(const DeviceState &state, ServiceManager &manager) : state(state), manager(manager) {}
@ -50,12 +71,12 @@ namespace skyline::service {
*/ */
virtual ~BaseService() = default; virtual ~BaseService() = default;
virtual std::pair<std::function<Result(type::KSession &, ipc::IpcRequest &, ipc::IpcResponse &)>, std::string_view> GetServiceFunction(u32 id) { virtual ServiceFunctionDescriptor GetServiceFunction(u32 id) {
throw std::out_of_range("GetServiceFunction not implemented"); throw std::out_of_range("GetServiceFunction not implemented");
} }
/** /**
* @return The name of the class * @return A string with the name of the service class
* @note The lifetime of the returned string is tied to that of the class * @note The lifetime of the returned string is tied to that of the class
*/ */
const std::string &GetName(); const std::string &GetName();