| /*
|
| * LegacyClonk
|
| *
|
| * Copyright (c) 2020-2021, The LegacyClonk Team and contributors
|
| *
|
| * Distributed under the terms of the ISC license; see accompanying file
|
| * "COPYING" for details.
|
| *
|
| * "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
| * See accompanying file "TRADEMARK" for details.
|
| *
|
| * To redistribute this file separately, substitute the full license texts
|
| * for the above references.
|
| */
|
|
|
| #include "C4NtSync.h"
|
|
|
| #include <fcntl.h>
|
| #include <asm/ioctl.h>
|
| #include <linux/ntsync.h>
|
| #include <sys/ioctl.h>
|
| #include <unistd.h>
|
|
|
| class C4NtSync
|
| {
|
| public:
|
| C4NtSync()
|
| {
|
| fd = open("/dev/ntsync", O_CLOEXEC);
|
|
|
| if (fd == -1)
|
| {
|
| throw std::runtime_error{"ntsync not available"};
|
| }
|
| }
|
|
|
| ~C4NtSync()
|
| {
|
| close(fd);
|
| }
|
|
|
| C4NtSync(const C4NtSync &) = delete;
|
| C4NtSync &operator=(const C4NtSync &) = delete;
|
| C4NtSync(C4NtSync &&) = delete;
|
| C4NtSync &operator=(C4NtSync &&) = delete;
|
|
|
| public:
|
| template<typename... Args>
|
| int IoControl(Args &&...args)
|
| {
|
| return ioctl(fd, std::forward<Args>(args)...);
|
| }
|
|
|
| int get() const noexcept { return fd; }
|
|
|
| private:
|
| int fd;
|
| };
|
|
|
| static C4NtSync NtSync;
|
|
|
| static HANDLE FdToHandle(const int fd)
|
| {
|
| return reinterpret_cast<HANDLE>(static_cast<std::uintptr_t>(static_cast<unsigned int>(fd)));
|
| }
|
|
|
| static int HandleToFd(const HANDLE handle)
|
| {
|
| return static_cast<int>(static_cast<unsigned int>(reinterpret_cast<std::uintptr_t>(handle)));
|
| }
|
|
|
| static int ThrowIfFailed(const int result, const char *const message)
|
| {
|
| if (result == -1)
|
| {
|
| throw std::runtime_error{std::format("{} failed: {}", message, std::strerror(errno))};
|
| }
|
|
|
| return result;
|
| }
|
|
|
| HANDLE CreateEvent(std::nullptr_t, bool manualReset, bool initialState, std::nullptr_t)
|
| {
|
| const ntsync_event_args args{
|
| .manual = manualReset,
|
| .signaled = initialState
|
| };
|
|
|
| return FdToHandle(ThrowIfFailed(NtSync.IoControl(NTSYNC_IOC_CREATE_EVENT, &args), "CreateEvent"));
|
| }
|
|
|
| bool SetEvent(const HANDLE handle)
|
| {
|
| const int fd{HandleToFd(handle)};
|
| __u32 previous;
|
| return ioctl(fd, NTSYNC_IOC_EVENT_SET, &previous) == 0;
|
| }
|
|
|
| bool ResetEvent(const HANDLE handle)
|
| {
|
| const int fd{HandleToFd(handle)};
|
| __u32 previous;
|
| return ioctl(fd, NTSYNC_IOC_EVENT_RESET, &previous) == 0;
|
| }
|
|
|
| bool CloseHandle(const HANDLE handle)
|
| {
|
| return close(HandleToFd(handle)) == 0;
|
| }
|
|
|
| std::uint32_t WaitForSingleObject(const HANDLE handle, const std::uint32_t milliseconds)
|
| {
|
| return WaitForMultipleObjects(1, &handle, false, milliseconds);
|
| }
|
|
|
| #include <sys/eventfd.h>
|
|
|
| std::uint32_t WaitForMultipleObjects(const std::uint32_t count, const HANDLE *const handles, const bool waitAll, const std::uint32_t milliseconds)
|
| {
|
| if (count > NTSYNC_MAX_WAIT_COUNT)
|
| {
|
| errno = EINVAL;
|
| return WAIT_FAILED;
|
| }
|
|
|
| std::array<int, NTSYNC_MAX_WAIT_COUNT> objs{};
|
| std::ranges::copy(std::span{handles, count} | std::views::transform(&HandleToFd), objs.begin());
|
|
|
| std::uint64_t nanoseconds;
|
|
|
| if (milliseconds == std::numeric_limits<std::uint32_t>::max())
|
| {
|
| nanoseconds = std::numeric_limits<__u64>::max();
|
| }
|
| else
|
| {
|
| nanoseconds = static_cast<__u64>(milliseconds) * 1'000'000;
|
| }
|
|
|
| int efd = eventfd(1, EFD_CLOEXEC);
|
|
|
| ntsync_wait_args args{
|
| .timeout = nanoseconds,
|
| .objs = static_cast<__u64>(reinterpret_cast<std::uintptr_t>(objs.data())),
|
| .count = count,
|
| .owner = static_cast<__u32>(gettid()),
|
| .alert = (__u32) efd
|
| };
|
|
|
| for (;;)
|
| {
|
| if (NtSync.IoControl(waitAll ? NTSYNC_IOC_WAIT_ALL : NTSYNC_IOC_WAIT_ANY, &args) == 0)
|
| {
|
| return WAIT_OBJECT_0 + args.index;
|
| }
|
| else
|
| {
|
| switch (errno)
|
| {
|
| case EINTR:
|
| continue;
|
|
|
| case ETIMEDOUT:
|
| return WAIT_TIMEOUT;
|
|
|
| case EOWNERDEAD:
|
| return WAIT_ABANDONED_0 + args.index;
|
|
|
| default:
|
| return WAIT_FAILED;
|
| }
|
| }
|
| }
|
| }
|