/[pcsx2_0.9.7]/trunk/pcsx2/System/SysThreadBase.cpp
ViewVC logotype

Contents of /trunk/pcsx2/System/SysThreadBase.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 62 - (show annotations) (download)
Tue Sep 7 11:08:22 2010 UTC (10 years, 1 month ago) by william
File size: 9492 byte(s)
Auto Commited Import of: pcsx2-0.9.7-r3738-debug in ./trunk
1 /* PCSX2 - PS2 Emulator for PCs
2 * Copyright (C) 2002-2010 PCSX2 Dev Team
3 *
4 * PCSX2 is free software: you can redistribute it and/or modify it under the terms
5 * of the GNU Lesser General Public License as published by the Free Software Found-
6 * ation, either version 3 of the License, or (at your option) any later version.
7 *
8 * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10 * PURPOSE. See the GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License along with PCSX2.
13 * If not, see <http://www.gnu.org/licenses/>.
14 */
15
16 #include "PrecompiledHeader.h"
17
18 #include "System.h"
19 #include "SysThreads.h"
20
21 // --------------------------------------------------------------------------------------
22 // SysThreadBase *External Thread* Implementations
23 // (Called form outside the context of this thread)
24 // --------------------------------------------------------------------------------------
25
26 SysThreadBase::SysThreadBase() :
27 m_ExecMode( ExecMode_NoThreadYet )
28 , m_ExecModeMutex()
29 {
30 }
31
32 SysThreadBase::~SysThreadBase() throw()
33 {
34 }
35
36 void SysThreadBase::Start()
37 {
38 _parent::Start();
39
40 Sleep( 1 );
41
42 pxAssertDev( (m_ExecMode == ExecMode_Closing) || (m_ExecMode == ExecMode_Closed),
43 "Unexpected thread status during SysThread startup."
44 );
45
46 m_sem_event.Post();
47 }
48
49
50 void SysThreadBase::OnStart()
51 {
52 if( !pxAssertDev( m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode" ) ) return;
53
54 m_sem_Resume.Reset();
55 m_sem_ChangingExecMode.Reset();
56
57 FrankenMutex( m_ExecModeMutex );
58 FrankenMutex( m_RunningLock );
59
60 _parent::OnStart();
61 }
62
63 // Suspends emulation and closes the emulation state (including plugins) at the next PS2 vsync,
64 // and returns control to the calling thread; or does nothing if the core is already suspended.
65 //
66 // Parameters:
67 // isNonblocking - if set to true then the function will not block for emulation suspension.
68 // Defaults to false if parameter is not specified. Performing non-blocking suspension
69 // is mostly useful for starting certain non-Emu related gui activities (improves gui
70 // responsiveness).
71 //
72 // Returns:
73 // The previous suspension state; true if the thread was running or false if it was
74 // suspended.
75 //
76 // Exceptions:
77 // CancelEvent - thrown if the thread is already in a Paused or Closing state. Because
78 // actions that pause emulation typically rely on plugins remaining loaded/active,
79 // Suspension must cancel itself forcefully or risk crashing whatever other action is
80 // in progress.
81 //
82 void SysThreadBase::Suspend( bool isBlocking )
83 {
84 if (!pxAssertDev(!IsSelf(),"Suspend/Resume are not allowed from this thread.")) return;
85 if (!IsRunning()) return;
86
87 // shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued
88 // from Resume or OnResumeReady code.
89 if( m_ExecMode == ExecMode_Closed ) return;
90
91 {
92 ScopedLock locker( m_ExecModeMutex );
93
94 switch( m_ExecMode )
95 {
96 // Check again -- status could have changed since above.
97 case ExecMode_Closed: return;
98
99 case ExecMode_Pausing:
100 case ExecMode_Paused:
101 if( !isBlocking )
102 throw Exception::CancelEvent( L"Cannot suspend in non-blocking fashion: Another thread is pausing the VM state." );
103
104 m_ExecMode = ExecMode_Closing;
105 m_sem_Resume.Post();
106 m_sem_ChangingExecMode.Wait();
107 break;
108
109 case ExecMode_Opened:
110 m_ExecMode = ExecMode_Closing;
111 break;
112 }
113
114 pxAssumeDev( m_ExecMode == ExecMode_Closing, "ExecMode should be nothing other than Closing..." );
115 m_sem_event.Post();
116 }
117
118 if( isBlocking )
119 m_RunningLock.Wait();
120 }
121
122 // Returns:
123 // The previous suspension state; true if the thread was running or false if it was
124 // closed, not running, or paused.
125 //
126 void SysThreadBase::Pause()
127 {
128 if( IsSelf() || !IsRunning() ) return;
129
130 // shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued
131 // from Resume or OnResumeReady code.
132 if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return;
133
134 {
135 ScopedLock locker( m_ExecModeMutex );
136
137 // Check again -- status could have changed since above.
138 if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return;
139
140 if( m_ExecMode == ExecMode_Opened )
141 m_ExecMode = ExecMode_Pausing;
142
143 pxAssumeDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." );
144
145 m_sem_event.Post();
146 }
147
148 m_RunningLock.Wait();
149 }
150
151 // Resumes the core execution state, or does nothing is the core is already running. If
152 // settings were changed, resets will be performed as needed and emulation state resumed from
153 // memory savestates.
154 //
155 // Note that this is considered a non-blocking action. Most times the state is safely resumed
156 // on return, but in the case of re-entrant or nested message handling the function may return
157 // before the thread has resumed. If you need explicit behavior tied to the completion of the
158 // Resume, you'll need to bind callbacks to either OnResumeReady or OnResumeInThread.
159 //
160 // Exceptions:
161 // PluginInitError - thrown if a plugin fails init (init is performed on the current thread
162 // on the first time the thread is resumed from it's initial idle state)
163 // ThreadCreationError - Insufficient system resources to create thread.
164 //
165 void SysThreadBase::Resume()
166 {
167 if( IsSelf() ) return;
168 if( m_ExecMode == ExecMode_Opened ) return;
169
170 ScopedLock locker( m_ExecModeMutex );
171
172 // Implementation Note:
173 // The entire state coming out of a Wait is indeterminate because of user input
174 // and pending messages being handled. So after each call we do some seemingly redundant
175 // sanity checks against m_ExecMode/m_Running status, and if something doesn't feel
176 // right, we should abort; the user may have canceled the action before it even finished.
177
178 switch( m_ExecMode )
179 {
180 case ExecMode_Opened: return;
181
182 case ExecMode_NoThreadYet:
183 {
184 Start();
185 if( !m_running || (m_ExecMode == ExecMode_NoThreadYet) )
186 throw Exception::ThreadCreationError(this);
187 if( m_ExecMode == ExecMode_Opened ) return;
188 }
189 // fall through...
190
191 case ExecMode_Closing:
192 case ExecMode_Pausing:
193 // we need to make sure and wait for the emuThread to enter a fully suspended
194 // state before continuing...
195
196 m_RunningLock.Wait();
197 if( !m_running ) return;
198 if( (m_ExecMode != ExecMode_Closed) && (m_ExecMode != ExecMode_Paused) ) return;
199 if( !GetCorePlugins().AreLoaded() ) return;
200 break;
201 }
202
203 pxAssertDev( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused),
204 "SysThreadBase is not in a closed/paused state? wtf!" );
205
206 OnResumeReady();
207 m_ExecMode = ExecMode_Opened;
208 m_sem_Resume.Post();
209 }
210
211
212 // --------------------------------------------------------------------------------------
213 // SysThreadBase *Worker* Implementations
214 // (Called from the context of this thread only)
215 // --------------------------------------------------------------------------------------
216
217 void SysThreadBase::OnStartInThread()
218 {
219 m_RunningLock.Acquire();
220 _parent::OnStartInThread();
221 m_ExecMode = ExecMode_Closing;
222 }
223
224 void SysThreadBase::OnCleanupInThread()
225 {
226 m_ExecMode = ExecMode_NoThreadYet;
227 _parent::OnCleanupInThread();
228 m_RunningLock.Release();
229 }
230
231 void SysThreadBase::OnSuspendInThread() {}
232 void SysThreadBase::OnResumeInThread( bool isSuspended ) {}
233
234 // Tests for Pause and Suspend/Close requests. If the thread is trying to be paused or
235 // closed, it will enter a wait/holding pattern here in this method until the managing
236 // thread releases it. Use the return value to detect if changes to the thread's state
237 // may have been changed (based on the rule that other threads are not allowed to modify
238 // this thread's state without pausing or closing it first, to prevent race conditions).
239 //
240 // Return value:
241 // TRUE if the thread was paused or closed; FALSE if the thread
242 // continued execution unimpeded.
243 bool SysThreadBase::StateCheckInThread()
244 {
245 switch( m_ExecMode )
246 {
247
248 #ifdef PCSX2_DEVBUILD // optimize out handlers for these cases in release builds.
249 case ExecMode_NoThreadYet:
250 // threads should never have this state set while the thread is in any way
251 // active or alive. (for obvious reasons!!)
252 pxFailDev( "Invalid execution state detected." );
253 return false;
254 #endif
255
256 case ExecMode_Opened:
257 // Other cases don't need TestCancel() because its built into the various
258 // threading wait/signal actions.
259 TestCancel();
260 return false;
261
262 // -------------------------------------
263 case ExecMode_Pausing:
264 {
265 OnPauseInThread();
266 m_ExecMode = ExecMode_Paused;
267 m_RunningLock.Release();
268 }
269 // fallthrough...
270
271 case ExecMode_Paused:
272 while( m_ExecMode == ExecMode_Paused )
273 m_sem_Resume.WaitWithoutYield();
274
275 m_RunningLock.Acquire();
276 if( m_ExecMode != ExecMode_Closing )
277 {
278 OnResumeInThread( false );
279 break;
280 }
281 m_sem_ChangingExecMode.Post();
282
283 // fallthrough if we're switching to closing state...
284
285 // -------------------------------------
286 case ExecMode_Closing:
287 {
288 OnSuspendInThread();
289 m_ExecMode = ExecMode_Closed;
290 m_RunningLock.Release();
291 }
292 // fallthrough...
293
294 case ExecMode_Closed:
295 while( m_ExecMode == ExecMode_Closed )
296 m_sem_Resume.WaitWithoutYield();
297
298 m_RunningLock.Acquire();
299 OnResumeInThread( true );
300 break;
301
302 jNO_DEFAULT;
303 }
304
305 return true;
306 }

  ViewVC Help
Powered by ViewVC 1.1.22