From 90ed5bbae9ba387e9a0b00c1bfb9b034d3499962 Mon Sep 17 00:00:00 2001 From: Albert S Date: Sat, 29 Jan 2022 23:05:27 +0100 Subject: [PATCH] Begin C++ API: Add exile.hpp with exile_launch() wrappers --- exile.hpp | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 exile.hpp diff --git a/exile.hpp b/exile.hpp new file mode 100644 index 0000000..0f83661 --- /dev/null +++ b/exile.hpp @@ -0,0 +1,175 @@ +#include "exile.h" +#include +#include +#include +#include +#include +#include + +#ifndef EXILE_MMAP_SIZE +#define EXILE_MMAP_SIZE 128 * 1024 * 1024 //128MB +#endif + + +template +class launch_arg +{ + static_assert(std::is_trivially_copyable_v); + static_assert(!std::is_pointer_v); + +public: + struct exile_policy *policy; + T *result_shm; + U fn; + std::tuple args; + + launch_arg(struct exile_policy *policy, T *result_shm, U fn, Args && ... args) : policy(policy), + result_shm(result_shm), fn(fn), args(std::forward(args)...) {} + +}; + +template +class launch_arg_serializer +{ + static_assert(std::is_copy_constructible_v); + +public: + struct exile_policy *policy; + char *serialize_buffer; + size_t n; + U fn; + std::tuple args; + + const std::function &serializer; + const std::function &deserializer; + + launch_arg_serializer(struct exile_policy *policy, char *serialize_buffer, size_t n, const std::function &serializer, const std::function &deserializer, U fn, Args && ... args) : policy(policy), serialize_buffer(serialize_buffer), n(n), fn(fn), args(std::forward(args)...), serializer(serializer), deserializer(deserializer) {} +}; + +template +int exile_clone_handle_trivial(void * arg) +{ + static_assert(std::is_trivially_copyable_v); + static_assert(!std::is_pointer_v); + + launch_arg *launchargs = (launch_arg *) arg; + int ret = exile_enable_policy(launchargs->policy); + if(ret != 0) + { + EXILE_LOG_ERROR("exile_enable_policy() failed: %s\n", strerror(errno)); + return 1; + } + T result = std::apply(launchargs->fn, launchargs->args); + std::cout << result; + memcpy(launchargs->result_shm, &result, sizeof(T)); + return 0; +} + +template +int exile_clone_handle_serializer(void * arg) +{ + static_assert(std::is_copy_constructible_v); + + launch_arg_serializer *launchargs = (launch_arg_serializer *) arg; + int ret = exile_enable_policy(launchargs->policy); + if(ret != 0) + { + EXILE_LOG_ERROR("exile_enable_policy() failed: %s\n", strerror(errno)); + return 1; + } + T result = std::apply(launchargs->fn, launchargs->args); + /* TODO: exception handling */ + /* TODO: ugly :S */ + char *target = launchargs->serialize_buffer + sizeof(size_t); + size_t n = launchargs->n - sizeof(size_t); + + size_t size = launchargs->serializer(result, target, n); + memcpy(launchargs->serialize_buffer, &size, sizeof(size_t)); + + return 0; +} + +static int do_clone(int (*clonefn)(void *), void *launcharg) +{ + struct rlimit rlimit; + int ret = getrlimit(RLIMIT_STACK, &rlimit); + if(ret != 0) + { + EXILE_LOG_ERROR("Failed to get stack size: %s\n", strerror(errno)); + return ret; + } + size_t size = rlimit.rlim_cur; + char *stack = (char *) calloc(1, size); + if(stack == NULL) + { + EXILE_LOG_ERROR("Failed to allocate stack memory for child\n"); + return 1; + } + stack += size; + + ret = clone(clonefn, stack, 17 /* SIGCHLD */, launcharg); + int status = 0; + waitpid(ret, &status, __WALL); + if(WIFEXITED(status)) + { + return WEXITSTATUS(status); + } + /* TODO: exception or what? */ + return 23; +} + +template +T exile_launch_trivial(struct exile_policy *policy, U fn, Args && ... args) +{ + size_t mapsize = sizeof(T); + T * sharedbuf = (T *) mmap(NULL, mapsize , PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); + if(sharedbuf == NULL) + { + throw std::runtime_error(std::string("mmap failed: ") + strerror(errno)); + } + + std::shared_ptr deleter(nullptr, [sharedbuf, mapsize](...){ munmap(sharedbuf, mapsize); }); + launch_arg launcharg(policy, sharedbuf, fn, std::forward(args)...); + + int (*clonefn)(void *) = &exile_clone_handle_trivial; + /* TODO: exception or what? */ + int ret = do_clone(clonefn, &launcharg); + if(ret == 0) + { + return *sharedbuf; + } + throw std::runtime_error(std::string("clone() failed: " + std::to_string(ret))); + return T(); +} + + + +template +T exile_launch(struct exile_policy *policy, const std::function &serializer, const std::function &deserializer, U fn, Args && ... args) +{ + size_t mapsize = EXILE_MMAP_SIZE; + char *sharedbuf = (char *) mmap(NULL, mapsize , PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); + if(sharedbuf == NULL) + { + throw std::runtime_error(std::string("mmap failed: ") + strerror(errno)); + } + std::shared_ptr deleter(nullptr, [sharedbuf, mapsize](...){ munmap(sharedbuf, mapsize); }); + + + launch_arg_serializer launcharg(policy, sharedbuf, mapsize, serializer, deserializer, fn, std::forward(args)...); + + int (*clonefn)(void *) = &exile_clone_handle_serializer; + /* TODO: exception or what? */ + int ret = do_clone(clonefn, &launcharg); + if(ret == 0) + { + size_t size = 0; + memcpy(&size, sharedbuf, sizeof(size)); + + return deserializer(sharedbuf + sizeof(size_t), size); + } + throw std::runtime_error(std::string("clone() failed: " + std::to_string(ret))); + return T(); + +} +