/[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 31 - (show annotations) (download)
Tue Sep 7 03:24:11 2010 UTC (10 years, 1 month ago) by william
File size: 8829 byte(s)
committing r3113 initial commit again...
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( IsSelf() || !IsRunning() ) return;
85
86 // shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued
87 // from Resume or OnResumeReady code.
88 if( m_ExecMode == ExecMode_Closed ) return;
89
90 {
91 ScopedLock locker( m_ExecModeMutex );
92
93 switch( m_ExecMode )
94 {
95 // Check again -- status could have changed since above.
96 case ExecMode_Closed: return;
97
98 case ExecMode_Pausing:
99 case ExecMode_Paused:
100 if( !isBlocking )
101 throw Exception::CancelEvent( "Cannot suspend in non-blocking fashion: Another thread is pausing the VM state." );
102
103 m_ExecMode = ExecMode_Closing;
104 m_sem_Resume.Post();
105 m_sem_ChangingExecMode.Wait();
106 break;
107
108 case ExecMode_Opened:
109 m_ExecMode = ExecMode_Closing;
110 break;
111 }
112
113 pxAssumeDev( m_ExecMode == ExecMode_Closing, "ExecMode should be nothing other than Closing..." );
114 m_sem_event.Post();
115 }
116
117 if( isBlocking )
118 m_RunningLock.Wait();
119 }
120
121 // Returns:
122 // The previous suspension state; true if the thread was running or false if it was
123 // closed, not running, or paused.
124 //
125 void SysThreadBase::Pause()
126 {
127 if( IsSelf() || !IsRunning() ) return;
128
129 // shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued
130 // from Resume or OnResumeReady code.
131 if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return;
132
133 {
134 ScopedLock locker( m_ExecModeMutex );
135
136 // Check again -- status could have changed since above.
137 if( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused) ) return;
138
139 if( m_ExecMode == ExecMode_Opened )
140 m_ExecMode = ExecMode_Pausing;
141
142 pxAssumeDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." );
143
144 m_sem_event.Post();
145 }
146
147 m_RunningLock.Wait();
148 }
149
150 // Resumes the core execution state, or does nothing is the core is already running. If
151 // settings were changed, resets will be performed as needed and emulation state resumed from
152 // memory savestates.
153 //
154 // Note that this is considered a non-blocking action. Most times the state is safely resumed
155 // on return, but in the case of re-entrant or nested message handling the function may return
156 // before the thread has resumed. If you need explicit behavior tied to the completion of the
157 // Resume, you'll need to bind callbacks to either OnResumeReady or OnResumeInThread.
158 //
159 // Exceptions:
160 // PluginInitError - thrown if a plugin fails init (init is performed on the current thread
161 // on the first time the thread is resumed from it's initial idle state)
162 // ThreadCreationError - Insufficient system resources to create thread.
163 //
164 void SysThreadBase::Resume()
165 {
166 if( IsSelf() ) return;
167 if( m_ExecMode == ExecMode_Opened ) return;
168
169 ScopedLock locker( m_ExecModeMutex );
170
171 // Implementation Note:
172 // The entire state coming out of a Wait is indeterminate because of user input
173 // and pending messages being handled. So after each call we do some seemingly redundant
174 // sanity checks against m_ExecMode/m_Running status, and if something doesn't feel
175 // right, we should abort; the user may have canceled the action before it even finished.
176
177 switch( m_ExecMode )
178 {
179 case ExecMode_Opened: return;
180
181 case ExecMode_NoThreadYet:
182 {
183 Start();
184 if( !m_running || (m_ExecMode == ExecMode_NoThreadYet) )
185 throw Exception::ThreadCreationError();
186 if( m_ExecMode == ExecMode_Opened ) return;
187 }
188 // fall through...
189
190 case ExecMode_Closing:
191 case ExecMode_Pausing:
192 // we need to make sure and wait for the emuThread to enter a fully suspended
193 // state before continuing...
194
195 m_RunningLock.Wait();
196 if( !m_running ) return;
197 if( (m_ExecMode != ExecMode_Closed) && (m_ExecMode != ExecMode_Paused) ) return;
198 if( !GetCorePlugins().AreLoaded() ) return;
199 break;
200 }
201
202 pxAssertDev( (m_ExecMode == ExecMode_Closed) || (m_ExecMode == ExecMode_Paused),
203 "SysThreadBase is not in a closed/paused state? wtf!" );
204
205 OnResumeReady();
206 m_ExecMode = ExecMode_Opened;
207 m_sem_Resume.Post();
208 }
209
210
211 // --------------------------------------------------------------------------------------
212 // SysThreadBase *Worker* Implementations
213 // (Called from the context of this thread only)
214 // --------------------------------------------------------------------------------------
215
216 void SysThreadBase::OnStartInThread()
217 {
218 m_RunningLock.Acquire();
219 _parent::OnStartInThread();
220 m_ExecMode = ExecMode_Closing;
221 }
222
223 void SysThreadBase::OnCleanupInThread()
224 {
225 m_ExecMode = ExecMode_NoThreadYet;
226 _parent::OnCleanupInThread();
227 m_RunningLock.Release();
228 }
229
230 void SysThreadBase::OnSuspendInThread() {}
231 void SysThreadBase::OnResumeInThread( bool isSuspended ) {}
232
233 void SysThreadBase::StateCheckInThread()
234 {
235 switch( m_ExecMode )
236 {
237
238 #ifdef PCSX2_DEVBUILD // optimize out handlers for these cases in release builds.
239 case ExecMode_NoThreadYet:
240 // threads should never have this state set while the thread is in any way
241 // active or alive. (for obvious reasons!!)
242 pxFailDev( "Invalid execution state detected." );
243 break;
244 #endif
245
246 case ExecMode_Opened:
247 // Yup, need this a second time. Variable state could have changed while we
248 // were trying to acquire the lock above.
249 TestCancel();
250 break;
251
252 // -------------------------------------
253 case ExecMode_Pausing:
254 {
255 OnPauseInThread();
256 m_ExecMode = ExecMode_Paused;
257 m_RunningLock.Release();
258 }
259 // fallthrough...
260
261 case ExecMode_Paused:
262 while( m_ExecMode == ExecMode_Paused )
263 m_sem_Resume.WaitWithoutYield();
264
265 m_RunningLock.Acquire();
266 if( m_ExecMode != ExecMode_Closing )
267 {
268 OnResumeInThread( false );
269 break;
270 }
271 m_sem_ChangingExecMode.Post();
272
273 // fallthrough if we're switching to closing state...
274
275 // -------------------------------------
276 case ExecMode_Closing:
277 {
278 OnSuspendInThread();
279 m_ExecMode = ExecMode_Closed;
280 m_RunningLock.Release();
281 }
282 // fallthrough...
283
284 case ExecMode_Closed:
285 while( m_ExecMode == ExecMode_Closed )
286 m_sem_Resume.WaitWithoutYield();
287
288 m_RunningLock.Acquire();
289 OnResumeInThread( true );
290 break;
291
292 jNO_DEFAULT;
293 }
294 }

  ViewVC Help
Powered by ViewVC 1.1.22