Repository navigation
Expand file tree
/
Copy pathSyncOnAddress.cpp
More file actions
133 lines (114 loc) · 4.48 KB
/
Copy pathSyncOnAddress.cpp
File metadata and controls
133 lines (114 loc) · 4.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <atomic>
#include <chrono>
#include <cstdint>
#include <list>
#include <mutex>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
#include "prx/libkernel/KernelErrors.hpp"
#include "prx/libkernel/Time/include/Time.hpp"
#include "prx/libkernel/Time/include/TimedWait.hpp"
namespace {
constexpr int SYNC_ON_ADDRESS_OK = 0;
struct AddressWaiter {
TimedWait::Condition condition;
bool woken = false;
};
std::mutex g_waitersLock;
std::unordered_map<std::uintptr_t, std::list<AddressWaiter*>> g_waiters;
template <class TValue>
bool IsAlignedAddress(std::uintptr_t address) {
return address != 0 && address % alignof(TValue) == 0;
}
template <class TValue>
int WaitOnAddress(TValue* address, TValue expected, const KernelUseconds* timeout, const void* caller) {
const auto key = reinterpret_cast<std::uintptr_t>(address);
std::unique_lock<std::mutex> lock(g_waitersLock);
if (std::atomic_ref<TValue>(*address).load() != expected) {
return SYNC_ON_ADDRESS_OK;
}
AddressWaiter waiter;
auto& queue = g_waiters[key];
const auto position = queue.insert(queue.end(), &waiter);
const auto isWoken = [&] { return waiter.woken; };
const auto waitStart = std::chrono::steady_clock::now();
if (timeout == nullptr) {
waiter.condition.Wait(lock, isWoken);
} else {
waiter.condition.WaitUntil(lock, TimedWait::DeadlineNanos(*timeout), isWoken);
}
const auto waited = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - waitStart);
const bool woken = waiter.woken;
if (!woken) {
queue.erase(position);
if (queue.empty()) {
g_waiters.erase(key);
}
}
lock.unlock();
KernelTraceWait_nid_postfix("addr", caller, static_cast<std::uint64_t>(waited.count()), !woken);
return woken ? SYNC_ON_ADDRESS_OK : SCE_KERNEL_ERROR_ETIMEDOUT;
}
} // namespace
extern "C" {
int APS5_VABI sceKernelSyncOnAddressWait(std::uint32_t* address, std::uint32_t expected, const KernelUseconds* timeout, const char* name) {
(void)name;
if (!IsAlignedAddress<std::uint32_t>(reinterpret_cast<std::uintptr_t>(address))) {
APS5_INVALID_ARG_EX;
}
return WaitOnAddress(address, expected, timeout, __builtin_return_address(0));
}
int APS5_VABI sceKernelSyncOnAddressWait8(std::uint8_t* address, std::uint8_t expected, const KernelUseconds* timeout, const char* name) {
(void)name;
if (!IsAlignedAddress<std::uint8_t>(reinterpret_cast<std::uintptr_t>(address))) {
APS5_INVALID_ARG_EX;
}
return WaitOnAddress(address, expected, timeout, __builtin_return_address(0));
}
int APS5_VABI sceKernelSyncOnAddressWait16(std::uint16_t* address, std::uint16_t expected, const KernelUseconds* timeout, const char* name) {
(void)name;
if (!IsAlignedAddress<std::uint16_t>(reinterpret_cast<std::uintptr_t>(address))) {
APS5_INVALID_ARG_EX;
}
return WaitOnAddress(address, expected, timeout, __builtin_return_address(0));
}
int APS5_VABI sceKernelSyncOnAddressWait32(std::uint32_t* address, std::uint32_t expected, const KernelUseconds* timeout, const char* name) {
(void)name;
if (!IsAlignedAddress<std::uint32_t>(reinterpret_cast<std::uintptr_t>(address))) {
APS5_INVALID_ARG_EX;
}
return WaitOnAddress(address, expected, timeout, __builtin_return_address(0));
}
int APS5_VABI sceKernelSyncOnAddressWait64(std::uint64_t* address, std::uint64_t expected, const KernelUseconds* timeout, const char* name) {
(void)name;
if (!IsAlignedAddress<std::uint64_t>(reinterpret_cast<std::uintptr_t>(address))) {
APS5_INVALID_ARG_EX;
}
return WaitOnAddress(address, expected, timeout, __builtin_return_address(0));
}
int APS5_VABI sceKernelSyncOnAddressWake(void* address, std::int32_t count) {
const auto key = reinterpret_cast<std::uintptr_t>(address);
if (!IsAlignedAddress<std::uint8_t>(key) || count < 0) {
APS5_INVALID_ARG_EX;
}
std::lock_guard<std::mutex> lock(g_waitersLock);
const auto entry = g_waiters.find(key);
if (entry == g_waiters.end()) {
return SYNC_ON_ADDRESS_OK;
}
auto& queue = entry->second;
for (; count > 0 && !queue.empty(); --count) {
AddressWaiter* waiter = queue.front();
queue.pop_front();
waiter->woken = true;
waiter->condition.NotifyOne();
}
if (queue.empty()) {
g_waiters.erase(entry);
}
return SYNC_ON_ADDRESS_OK;
}
}