2016-05-13 16:01:48 +02:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
/* For internal use. Don't include. */
|
|
|
|
|
|
|
|
#include "types.h"
|
|
|
|
#include "Atomic.h"
|
2017-01-24 14:52:15 +01:00
|
|
|
#include "dynamic_library.h"
|
2016-05-13 16:01:48 +02:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
#include <Windows.h>
|
2017-01-24 14:52:15 +01:00
|
|
|
#include <time.h>
|
|
|
|
#elif __linux__
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
#include <linux/futex.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#else
|
|
|
|
#endif
|
Utilities: explicitly add more includes found by GCC
Utilities/Log.cpp: In member function 'void logs::file_writer::log(logs::level, const char*, std::size_t)':
Utilities/Log.cpp:559:9: error: 'memcpy' is not a member of 'std'
std::memcpy(pos, text, frag);
^~~~~~
Utilities/Log.cpp:559:9: note: suggested alternative: 'empty'
std::memcpy(pos, text, frag);
^~~~~~
empty
Utilities/Log.cpp:560:9: error: 'memcpy' is not a member of 'std'
std::memcpy(m_fptr, text + frag, size - frag);
^~~~~~
Utilities/Log.cpp:560:9: note: suggested alternative: 'empty'
std::memcpy(m_fptr, text + frag, size - frag);
^~~~~~
empty
Utilities/Log.cpp:564:9: error: 'memcpy' is not a member of 'std'
std::memcpy(pos, text, size);
^~~~~~
Utilities/Log.cpp:564:9: note: suggested alternative: 'empty'
std::memcpy(pos, text, size);
^~~~~~
empty
Utilities/sync.h: In member function 'int futex(int*, int, int, const timespec*, int*, int)::futex_map::operator()(int*, int, int, const timespec*, int*, uint)':
Utilities/sync.h:110:20: error: 'find' is not a member of 'std'
map.erase(std::find(map.find(uaddr), map.end(), ref));
^~~~
Utilities/sync.h:110:20: note: suggested alternative: 'rend'
map.erase(std::find(map.find(uaddr), map.end(), ref));
^~~~
rend
2018-08-30 19:39:44 +02:00
|
|
|
#include <algorithm>
|
2017-01-24 14:52:15 +01:00
|
|
|
#include <ctime>
|
|
|
|
#include <chrono>
|
|
|
|
#include <mutex>
|
|
|
|
#include <condition_variable>
|
|
|
|
#include <unordered_map>
|
2016-05-13 16:01:48 +02:00
|
|
|
|
2017-01-24 14:52:15 +01:00
|
|
|
#ifdef _WIN32
|
|
|
|
DYNAMIC_IMPORT("ntdll.dll", NtWaitForKeyedEvent, NTSTATUS(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout));
|
|
|
|
DYNAMIC_IMPORT("ntdll.dll", NtReleaseKeyedEvent, NTSTATUS(HANDLE Handle, PVOID Key, BOOLEAN Alertable, PLARGE_INTEGER Timeout));
|
|
|
|
DYNAMIC_IMPORT("ntdll.dll", NtDelayExecution, NTSTATUS(BOOLEAN Alertable, PLARGE_INTEGER DelayInterval));
|
2018-10-13 22:58:02 +02:00
|
|
|
inline utils::dynamic_import<BOOL(volatile VOID* Address, PVOID CompareAddress, SIZE_T AddressSize, DWORD dwMilliseconds)> OptWaitOnAddress("kernel32.dll", "WaitOnAddress");
|
|
|
|
inline utils::dynamic_import<VOID(PVOID Address)> OptWakeByAddressSingle("kernel32.dll", "WakeByAddressSingle");
|
|
|
|
inline utils::dynamic_import<VOID(PVOID Address)> OptWakeByAddressAll("kernel32.dll", "WakeByAddressAll");
|
2017-01-24 14:52:15 +01:00
|
|
|
#endif
|
2016-05-13 16:01:48 +02:00
|
|
|
|
2017-01-24 14:52:15 +01:00
|
|
|
#ifndef __linux__
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
FUTEX_PRIVATE_FLAG = 0,
|
|
|
|
FUTEX_WAIT = 0,
|
|
|
|
FUTEX_WAIT_PRIVATE = FUTEX_WAIT,
|
|
|
|
FUTEX_WAKE = 1,
|
|
|
|
FUTEX_WAKE_PRIVATE = FUTEX_WAKE,
|
|
|
|
FUTEX_BITSET = 2,
|
|
|
|
FUTEX_WAIT_BITSET = FUTEX_WAIT | FUTEX_BITSET,
|
|
|
|
FUTEX_WAIT_BITSET_PRIVATE = FUTEX_WAIT_BITSET,
|
|
|
|
FUTEX_WAKE_BITSET = FUTEX_WAKE | FUTEX_BITSET,
|
|
|
|
FUTEX_WAKE_BITSET_PRIVATE = FUTEX_WAKE_BITSET,
|
|
|
|
};
|
|
|
|
#endif
|
2016-05-13 16:01:48 +02:00
|
|
|
|
2018-11-01 11:23:09 +01:00
|
|
|
inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* timeout = nullptr, uint mask = 0)
|
2016-05-13 16:01:48 +02:00
|
|
|
{
|
2017-01-24 14:52:15 +01:00
|
|
|
#ifdef __linux__
|
2018-11-01 11:23:09 +01:00
|
|
|
return syscall(SYS_futex, uaddr, futex_op, static_cast<int>(val), timeout, nullptr, static_cast<int>(mask));
|
2017-01-24 14:52:15 +01:00
|
|
|
#else
|
2018-11-01 11:23:09 +01:00
|
|
|
static struct futex_manager
|
2016-05-13 16:01:48 +02:00
|
|
|
{
|
2017-01-24 14:52:15 +01:00
|
|
|
struct waiter
|
|
|
|
{
|
2018-11-01 11:23:09 +01:00
|
|
|
uint val;
|
2017-01-24 14:52:15 +01:00
|
|
|
uint mask;
|
|
|
|
std::condition_variable cv;
|
|
|
|
};
|
2016-05-13 16:01:48 +02:00
|
|
|
|
2017-01-24 14:52:15 +01:00
|
|
|
std::mutex mutex;
|
2018-11-01 11:23:09 +01:00
|
|
|
std::unordered_multimap<volatile void*, waiter*, pointer_hash<volatile void, alignof(int)>> map;
|
2016-05-13 16:01:48 +02:00
|
|
|
|
2018-11-01 11:23:09 +01:00
|
|
|
int operator()(volatile void* uaddr, int futex_op, uint val, const timespec* timeout, uint mask)
|
2016-05-13 16:01:48 +02:00
|
|
|
{
|
2018-09-03 21:28:33 +02:00
|
|
|
std::unique_lock lock(mutex);
|
2016-05-13 16:01:48 +02:00
|
|
|
|
2017-01-24 14:52:15 +01:00
|
|
|
switch (futex_op)
|
2016-05-13 16:01:48 +02:00
|
|
|
{
|
2017-01-24 14:52:15 +01:00
|
|
|
case FUTEX_WAIT:
|
|
|
|
{
|
2018-11-01 11:23:09 +01:00
|
|
|
mask = -1;
|
|
|
|
[[fallthrough]];
|
2017-01-24 14:52:15 +01:00
|
|
|
}
|
|
|
|
case FUTEX_WAIT_BITSET:
|
|
|
|
{
|
2018-11-01 11:23:09 +01:00
|
|
|
if (*reinterpret_cast<volatile uint*>(uaddr) != val)
|
2016-05-13 16:01:48 +02:00
|
|
|
{
|
2017-01-24 14:52:15 +01:00
|
|
|
errno = EAGAIN;
|
|
|
|
return -1;
|
2016-05-13 16:01:48 +02:00
|
|
|
}
|
|
|
|
|
2017-01-24 14:52:15 +01:00
|
|
|
waiter rec;
|
|
|
|
rec.val = val;
|
2018-11-01 11:23:09 +01:00
|
|
|
rec.mask = mask;
|
2017-01-24 14:52:15 +01:00
|
|
|
const auto& ref = *map.emplace(uaddr, &rec);
|
2016-05-13 16:01:48 +02:00
|
|
|
|
2017-01-24 14:52:15 +01:00
|
|
|
int res = 0;
|
2016-05-13 16:01:48 +02:00
|
|
|
|
2017-01-24 14:52:15 +01:00
|
|
|
if (!timeout)
|
|
|
|
{
|
|
|
|
rec.cv.wait(lock, [&] { return !rec.mask; });
|
|
|
|
}
|
|
|
|
else if (futex_op == FUTEX_WAIT)
|
|
|
|
{
|
|
|
|
const auto nsec = std::chrono::nanoseconds(timeout->tv_nsec + timeout->tv_sec * 1000000000ull);
|
2016-05-13 16:01:48 +02:00
|
|
|
|
2017-01-24 14:52:15 +01:00
|
|
|
if (!rec.cv.wait_for(lock, nsec, [&] { return !rec.mask; }))
|
|
|
|
{
|
|
|
|
res = -1;
|
|
|
|
errno = ETIMEDOUT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// TODO
|
|
|
|
}
|
2018-05-17 16:40:29 +02:00
|
|
|
|
2017-01-24 14:52:15 +01:00
|
|
|
map.erase(std::find(map.find(uaddr), map.end(), ref));
|
|
|
|
return res;
|
|
|
|
}
|
2016-05-13 16:01:48 +02:00
|
|
|
|
2017-01-24 14:52:15 +01:00
|
|
|
case FUTEX_WAKE:
|
|
|
|
{
|
2018-11-01 11:23:09 +01:00
|
|
|
mask = -1;
|
|
|
|
[[fallthrough]];
|
2017-01-24 14:52:15 +01:00
|
|
|
}
|
|
|
|
case FUTEX_WAKE_BITSET:
|
|
|
|
{
|
|
|
|
int res = 0;
|
2016-05-13 16:01:48 +02:00
|
|
|
|
2017-01-24 14:52:15 +01:00
|
|
|
for (auto range = map.equal_range(uaddr); val && range.first != range.second; range.first++)
|
|
|
|
{
|
|
|
|
auto& entry = *range.first->second;
|
2018-05-17 16:40:29 +02:00
|
|
|
|
2018-11-01 11:23:09 +01:00
|
|
|
if (entry.mask & mask)
|
2017-01-24 14:52:15 +01:00
|
|
|
{
|
|
|
|
entry.cv.notify_one();
|
|
|
|
entry.mask = 0;
|
|
|
|
res++;
|
|
|
|
val--;
|
|
|
|
}
|
|
|
|
}
|
2016-05-13 16:01:48 +02:00
|
|
|
|
2017-01-24 14:52:15 +01:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
}
|
2016-05-13 16:01:48 +02:00
|
|
|
|
2017-01-24 14:52:15 +01:00
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
2016-05-13 16:01:48 +02:00
|
|
|
}
|
2017-01-24 14:52:15 +01:00
|
|
|
} g_futex;
|
2016-05-13 16:01:48 +02:00
|
|
|
|
2018-11-01 11:23:09 +01:00
|
|
|
return g_futex(uaddr, futex_op, val, timeout, mask);
|
2016-05-13 16:01:48 +02:00
|
|
|
#endif
|
2017-01-24 14:52:15 +01:00
|
|
|
}
|