Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/HW/AsyncIOManager.h
3186 views
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#pragma once
19
20
#include <mutex>
21
#include <condition_variable>
22
#include <deque>
23
#include <map>
24
#include <set>
25
26
#include "Core/Core.h"
27
28
#include "Core/System.h"
29
#include "Core/CoreTiming.h"
30
31
enum AsyncIOEventType {
32
IO_EVENT_INVALID,
33
IO_EVENT_SYNC,
34
IO_EVENT_FINISH,
35
IO_EVENT_READ,
36
IO_EVENT_WRITE,
37
};
38
39
struct AsyncIOEvent {
40
AsyncIOEvent(AsyncIOEventType t) : type(t) {}
41
AsyncIOEventType type;
42
u32 handle;
43
u8 *buf;
44
size_t bytes;
45
u32 invalidateAddr;
46
47
operator AsyncIOEventType() const {
48
return type;
49
}
50
};
51
52
struct AsyncIOResult {
53
AsyncIOResult() : result(0), finishTicks(0), invalidateAddr(0) {}
54
55
explicit AsyncIOResult(s64 r) : result(r), finishTicks(0), invalidateAddr(0) {}
56
57
AsyncIOResult(s64 r, int usec, u32 addr = 0) : result(r), invalidateAddr(addr) {
58
finishTicks = CoreTiming::GetTicks() + usToCycles(usec);
59
}
60
61
void DoState(PointerWrap &p) {
62
auto s = p.Section("AsyncIOResult", 1, 2);
63
if (!s)
64
return;
65
66
Do(p, result);
67
Do(p, finishTicks);
68
if (s >= 2) {
69
Do(p, invalidateAddr);
70
} else {
71
invalidateAddr = 0;
72
}
73
}
74
75
s64 result;
76
u64 finishTicks;
77
u32 invalidateAddr;
78
};
79
80
class AsyncIOManager {
81
public:
82
void DoState(PointerWrap &p);
83
84
bool HasOperation(u32 handle);
85
void ScheduleOperation(const AsyncIOEvent &ev);
86
void Shutdown();
87
88
bool HasResult(u32 handle);
89
bool WaitResult(u32 handle, AsyncIOResult &result);
90
u64 ResultFinishTicks(u32 handle);
91
92
void SetThreadEnabled(bool threadEnabled) {
93
threadEnabled_ = threadEnabled;
94
}
95
96
bool ThreadEnabled() {
97
return threadEnabled_;
98
}
99
100
void ScheduleEvent(AsyncIOEvent ev) {
101
if (threadEnabled_) {
102
std::lock_guard<std::recursive_mutex> guard(eventsLock_);
103
events_.push_back(ev);
104
eventsWait_.notify_one();
105
} else {
106
events_.push_back(ev);
107
}
108
109
if (!threadEnabled_) {
110
RunEventsUntil(0);
111
}
112
}
113
114
bool HasEvents() {
115
if (threadEnabled_) {
116
std::lock_guard<std::recursive_mutex> guard(eventsLock_);
117
return !events_.empty();
118
} else {
119
return !events_.empty();
120
}
121
}
122
123
void NotifyDrain() {
124
if (threadEnabled_) {
125
std::lock_guard<std::recursive_mutex> guard(eventsLock_);
126
eventsDrain_.notify_one();
127
}
128
}
129
130
AsyncIOEvent GetNextEvent() {
131
if (threadEnabled_) {
132
std::lock_guard<std::recursive_mutex> guard(eventsLock_);
133
if (events_.empty()) {
134
NotifyDrain();
135
return IO_EVENT_INVALID;
136
}
137
138
AsyncIOEvent ev = events_.front();
139
events_.pop_front();
140
return ev;
141
} else {
142
if (events_.empty()) {
143
return IO_EVENT_INVALID;
144
}
145
AsyncIOEvent ev = events_.front();
146
events_.pop_front();
147
return ev;
148
}
149
}
150
151
// This is the threadfunc, really. Although it can also run on the main thread if threadEnabled_ is set.
152
// TODO: Remove threadEnabled_, always be on a thread.
153
void RunEventsUntil(u64 globalticks) {
154
if (!threadEnabled_) {
155
do {
156
for (AsyncIOEvent ev = GetNextEvent(); AsyncIOEventType(ev) != IO_EVENT_INVALID; ev = GetNextEvent()) {
157
ProcessEventIfApplicable(ev, globalticks);
158
}
159
} while (CoreTiming::GetTicks() < globalticks);
160
return;
161
}
162
163
std::unique_lock<std::recursive_mutex> guard(eventsLock_);
164
eventsRunning_ = true;
165
eventsHaveRun_ = true;
166
do {
167
while (events_.empty()) {
168
eventsWait_.wait(guard);
169
}
170
// Quit the loop if the queue is drained and coreState has tripped, or threading is disabled.
171
if (events_.empty()) {
172
break;
173
}
174
175
for (AsyncIOEvent ev = GetNextEvent(); AsyncIOEventType(ev) != IO_EVENT_INVALID; ev = GetNextEvent()) {
176
guard.unlock();
177
ProcessEventIfApplicable(ev, globalticks);
178
guard.lock();
179
}
180
} while (CoreTiming::GetTicks() < globalticks);
181
182
// This will force the waiter to check coreState, even if we didn't actually drain.
183
NotifyDrain();
184
eventsRunning_ = false;
185
}
186
187
void SyncBeginFrame() {
188
if (threadEnabled_) {
189
std::lock_guard<std::recursive_mutex> guard(eventsLock_);
190
eventsHaveRun_ = false;
191
} else {
192
eventsHaveRun_ = false;
193
}
194
}
195
196
inline bool ShouldSyncThread(bool force) {
197
if (!HasEvents())
198
return false;
199
if (coreState != CORE_RUNNING_CPU && !force)
200
return false;
201
202
// Don't run if it's not running, but wait for startup.
203
if (!eventsRunning_) {
204
if (eventsHaveRun_ || coreState == CORE_RUNTIME_ERROR || coreState == CORE_POWERDOWN) {
205
return false;
206
}
207
}
208
209
return true;
210
}
211
212
// Force ignores coreState.
213
void SyncThread(bool force = false) {
214
if (!threadEnabled_) {
215
return;
216
}
217
218
std::unique_lock<std::recursive_mutex> guard(eventsLock_);
219
// While processing the last event, HasEvents() will be false even while not done.
220
// So we schedule a nothing event and wait for that to finish.
221
ScheduleEvent(IO_EVENT_SYNC);
222
while (ShouldSyncThread(force)) {
223
eventsDrain_.wait(guard);
224
}
225
}
226
227
void FinishEventLoop() {
228
if (!threadEnabled_) {
229
return;
230
}
231
232
std::lock_guard<std::recursive_mutex> guard(eventsLock_);
233
// Don't schedule a finish if it's not even running.
234
if (eventsRunning_) {
235
ScheduleEvent(IO_EVENT_FINISH);
236
}
237
}
238
239
protected:
240
void ProcessEvent(AsyncIOEvent ref);
241
242
inline void ProcessEventIfApplicable(AsyncIOEvent &ev, u64 &globalticks) {
243
switch (AsyncIOEventType(ev)) {
244
case IO_EVENT_FINISH:
245
// Stop waiting.
246
globalticks = 0;
247
break;
248
249
case IO_EVENT_SYNC:
250
// Nothing special to do, this event it just to wait on, see SyncThread.
251
break;
252
253
default:
254
ProcessEvent(ev);
255
}
256
}
257
258
private:
259
bool PopResult(u32 handle, AsyncIOResult &result);
260
bool ReadResult(u32 handle, AsyncIOResult &result);
261
void Read(u32 handle, u8 *buf, size_t bytes, u32 invalidateAddr);
262
void Write(u32 handle, const u8 *buf, size_t bytes);
263
264
void EventResult(u32 handle, const AsyncIOResult &result);
265
266
bool threadEnabled_ = false;
267
bool eventsRunning_ = false;
268
bool eventsHaveRun_ = false;
269
std::deque<AsyncIOEvent> events_;
270
std::recursive_mutex eventsLock_; // TODO: Should really make this non-recursive - condition_variable_any is dangerous
271
std::condition_variable_any eventsWait_;
272
std::condition_variable_any eventsDrain_;
273
274
std::mutex resultsLock_;
275
std::condition_variable resultsWait_;
276
std::set<u32> resultsPending_;
277
std::map<u32, AsyncIOResult> results_;
278
};
279
280