1 // Copyright (c) 2013- PPSSPP Project.
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.
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.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
19 #include "ThreadEventQueueLR.h"
24 #include "native/base/mutex.h"
25 #include "Core/System.h"
26 #include "Core/CoreTiming.h"
29 template <typename B
, typename Event
, typename EventType
, EventType EVENT_INVALID
, EventType EVENT_SYNC
, EventType EVENT_FINISH
>
30 struct ThreadEventQueue
: public B
{
31 ThreadEventQueue() : threadEnabled_(false), eventsRunning_(false), eventsHaveRun_(false) {
34 void SetThreadEnabled(bool threadEnabled
) {
35 threadEnabled_
= threadEnabled
;
38 bool ThreadEnabled() {
39 return threadEnabled_
;
42 void ScheduleEvent(Event ev
) {
44 lock_guard
guard(eventsLock_
);
45 events_
.push_back(ev
);
46 eventsWait_
.notify_one();
48 events_
.push_back(ev
);
51 if (!threadEnabled_
) {
58 lock_guard
guard(eventsLock_
);
59 return !events_
.empty();
61 return !events_
.empty();
67 lock_guard
guard(eventsLock_
);
68 eventsDrain_
.notify_one();
72 Event
GetNextEvent() {
74 lock_guard
guard(eventsLock_
);
75 if (events_
.empty()) {
80 Event ev
= events_
.front();
84 if (events_
.empty()) {
87 Event ev
= events_
.front();
93 void RunEventsUntil(u64 globalticks
) {
94 if (!threadEnabled_
) {
96 for (Event ev
= GetNextEvent(); EventType(ev
) != EVENT_INVALID
; ev
= GetNextEvent()) {
97 ProcessEventIfApplicable(ev
, globalticks
);
99 } while (CoreTiming::GetTicks() < globalticks
);
103 lock_guard
guard(eventsLock_
);
104 eventsRunning_
= true;
105 eventsHaveRun_
= true;
107 while (!HasEvents() && !ShouldExitEventLoop()) {
108 eventsWait_
.wait(eventsLock_
);
110 // Quit the loop if the queue is drained and coreState has tripped, or threading is disabled.
115 for (Event ev
= GetNextEvent(); EventType(ev
) != EVENT_INVALID
; ev
= GetNextEvent()) {
116 eventsLock_
.unlock();
117 ProcessEventIfApplicable(ev
, globalticks
);
120 } while (CoreTiming::GetTicks() < globalticks
);
122 // This will force the waiter to check coreState, even if we didn't actually drain.
124 eventsRunning_
= false;
127 void SyncBeginFrame() {
128 if (threadEnabled_
) {
129 lock_guard
guard(eventsLock_
);
130 eventsHaveRun_
= false;
132 eventsHaveRun_
= false;
136 inline bool ShouldSyncThread(bool force
) {
139 if (coreState
!= CORE_RUNNING
&& !force
)
142 // Don't run if it's not running, but wait for startup.
143 if (!eventsRunning_
) {
144 if (eventsHaveRun_
|| coreState
== CORE_ERROR
|| coreState
== CORE_POWERDOWN
) {
152 // Force ignores coreState.
153 void SyncThread(bool force
= false) {
154 if (!threadEnabled_
) {
158 lock_guard
guard(eventsLock_
);
159 // While processing the last event, HasEvents() will be false even while not done.
160 // So we schedule a nothing event and wait for that to finish.
161 ScheduleEvent(EVENT_SYNC
);
162 while (ShouldSyncThread(force
)) {
163 eventsDrain_
.wait(eventsLock_
);
167 void FinishEventLoop() {
168 if (!threadEnabled_
) {
172 lock_guard
guard(eventsLock_
);
173 // Don't schedule a finish if it's not even running.
174 if (eventsRunning_
) {
175 ScheduleEvent(EVENT_FINISH
);
180 virtual void ProcessEvent(Event ev
) = 0;
181 virtual bool ShouldExitEventLoop() = 0;
183 inline void ProcessEventIfApplicable(Event
&ev
, u64
&globalticks
) {
184 switch (EventType(ev
)) {
191 // Nothing special to do, this event it just to wait on, see SyncThread.
203 std::deque
<Event
> events_
;
204 recursive_mutex eventsLock_
;
205 condition_variable eventsWait_
;
206 condition_variable eventsDrain_
;