/[pcsx2_0.9.7]/branch/debug/0.X/0.9.X/0.9.7/ramdump-lateset/pcsx2/gui/ExecutorThread.cpp
ViewVC logotype

Contents of /branch/debug/0.X/0.9.X/0.9.7/ramdump-lateset/pcsx2/gui/ExecutorThread.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 330 - (show annotations) (download)
Tue Dec 28 04:24:23 2010 UTC (9 years, 9 months ago) by william
File size: 16708 byte(s)
merged upstream r4154-r4160
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 #include "App.h"
18
19 using namespace pxSizerFlags;
20
21 // --------------------------------------------------------------------------------------
22 // ConsoleLogSource_Event (implementations)
23 // --------------------------------------------------------------------------------------
24
25 bool ConsoleLogSource_Event::Write( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ) {
26 return _parent::Write( pxsFmt(L"(%s:%s) ", evtHandler->GetEventHandlerName().c_str(), evt->GetEventName().c_str()) + msg );
27 }
28 bool ConsoleLogSource_Event::Warn( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ) {
29 return _parent::Write( pxsFmt(L"(%s:%s) ", evtHandler->GetEventHandlerName().c_str(), evt->GetEventName().c_str()) + msg );
30 }
31 bool ConsoleLogSource_Event::Error( const pxEvtQueue* evtHandler, const SysExecEvent* evt, const wxChar* msg ) {
32 return _parent::Write( pxsFmt(L"(%s:%s) ", evtHandler->GetEventHandlerName().c_str(), evt->GetEventName().c_str()) + msg );
33 }
34
35 ConsoleLogSource_Event::ConsoleLogSource_Event()
36 {
37 static const TraceLogDescriptor myDesc =
38 {
39 L"SysEvents", L"SysVM Control Events",
40 pxLt("Logs events as they are passed to the PS2 virtual machine."),
41 };
42
43 m_Descriptor = &myDesc;
44 }
45
46 ConsoleLogSource_Event pxConLog_Event;
47
48 // --------------------------------------------------------------------------------------
49 // SysExecEvent (implementations)
50 // --------------------------------------------------------------------------------------
51 wxString SysExecEvent::GetEventName() const
52 {
53 pxFail( "Warning: Unnamed SysExecutor Event! Please overload GetEventName() in all SysExecEvent derived classes." );
54 return wxEmptyString;
55 }
56
57 wxString SysExecEvent::GetEventMessage() const
58 {
59 return GetEventName();
60 }
61
62 // This is called from _DoInvokeEvent after various affinity and state checks have verified the
63 // message as executable. Override this when possible. Only override _DoInvokeEvent if you
64 // need some kind of additional low-level ability.
65 void SysExecEvent::InvokeEvent()
66 {
67 }
68
69 // This is called by _DoInvokeEvent *always* -- even when exceptions occur during InvokeEvent(),
70 // making this function a bit like a C# 'finally' block (try/catch/finally -- a nice feature lacking
71 // from C++ prior to the new C++0x standard).
72 //
73 // This function calls PostResult by default, and should be invoked by derived classes overriding
74 // CleanupEvent(), unless you want to change the PostResult behavior.
75 void SysExecEvent::CleanupEvent()
76 {
77 PostResult();
78 }
79
80 // Transports the specified exception to the thread/context that invoked this event.
81 // Events are run from a try/catch block in the event handler that automatically transports
82 // exceptions as neeeded, so there shouldn't be much need to use this method directly.
83 void SysExecEvent::SetException( BaseException* ex )
84 {
85 if( !ex ) return;
86
87 ex->DiagMsg() += pxsFmt(L"(%s) ", GetEventName().c_str());
88 //ex->UserMsg() = prefix + ex->UserMsg();
89
90 if( m_sync )
91 {
92 // Throws the exception inline with the message handler (this makes the exception
93 // look like it was thrown quite naturally).
94 m_sync->SetException( ex );
95 }
96 else
97 {
98 // transport the exception to the main thread, since the message is fully
99 // asynchronous, or has already entered an asynchronous state. Message is sent
100 // as a non-blocking action since proper handling of user errors on async messages
101 // is *usually* to log/ignore it (hah), or to suspend emulation and issue a dialog
102 // box to the user.
103
104 wxGetApp().PostEvent( pxExceptionEvent( ex ) );
105 }
106 }
107
108 void SysExecEvent::SetException( const BaseException& ex )
109 {
110 SetException( ex.Clone() );
111 }
112
113
114 // This method calls _DoInvoke after performing some setup, exception handling, and
115 // affinity checks. For implementing behavior of your event, override _DoInvoke
116 // instead, which is the intended method of implementing derived class invocation.
117 void SysExecEvent::_DoInvokeEvent()
118 {
119 AffinityAssert_AllowFrom_SysExecutor();
120
121 try {
122 InvokeEvent();
123 }
124 catch( BaseException& ex )
125 {
126 SetException( ex );
127 }
128 catch( std::runtime_error& ex )
129 {
130 SetException( new Exception::RuntimeError(ex) );
131 }
132
133 // Cleanup Execution -- performed regardless of exception or not above.
134 try {
135 CleanupEvent();
136 }
137 catch( BaseException& ex )
138 {
139 SetException( ex );
140 }
141 catch( std::runtime_error& ex )
142 {
143 SetException( new Exception::RuntimeError(ex) );
144 }
145 }
146
147 // Posts an empty result to the invoking context/thread of this message, if one exists.
148 // If the invoking thread posted the event in non-blocking fashion then no action is
149 // taken.
150 void SysExecEvent::PostResult() const
151 {
152 if( m_sync ) m_sync->PostResult();
153 }
154
155 // --------------------------------------------------------------------------------------
156 // pxEvtQueue Implementations
157 // --------------------------------------------------------------------------------------
158 pxEvtQueue::pxEvtQueue()
159 {
160 AtomicExchange( m_Quitting, false );
161 m_qpc_Start = 0;
162 }
163
164 // Puts the event queue into Shutdown mode, which does *not* immediately stop nor cancel
165 // the queue's processing. Instead it marks the queue inaccessible to all new events
166 // and continues procesing queued events for critical events that should not be ignored.
167 // (typically these are shutdown events critical to closing the app cleanly). Once
168 // all such events have been processed, the thread is stopped.
169 //
170 void pxEvtQueue::ShutdownQueue()
171 {
172 if( m_Quitting ) return;
173 AtomicExchange( m_Quitting, true );
174 m_wakeup.Post();
175 }
176
177 struct ScopedThreadCancelDisable
178 {
179 ScopedThreadCancelDisable()
180 {
181 int oldstate;
182 pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
183 }
184
185 ~ScopedThreadCancelDisable() throw()
186 {
187 int oldstate;
188 pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate );
189 }
190 };
191
192 // isIdle - parameter is useful for logging only (currently)
193 void pxEvtQueue::ProcessEvents( pxEvtList& list, bool isIdle )
194 {
195 ScopedLock synclock( m_mtx_pending );
196
197 pxEvtList::iterator node;
198 while( node = list.begin(), node != list.end() )
199 {
200 ScopedPtr<SysExecEvent> deleteMe(*node);
201
202 list.erase( node );
203 if( !m_Quitting || deleteMe->IsCriticalEvent() )
204 {
205 // Some messages can be blocking, so we should release the mutex lock while
206 // processing, to avoid having cases where the main thread deadlocks simply
207 // trying to add a message to the queue due to the basic mutex acquire needed.
208
209 m_qpc_Start = GetCPUTicks();
210
211 synclock.Release();
212
213 pxEvtLog.Write( this, deleteMe, wxsFormat(L"Executing... [%s]%s",
214 deleteMe->AllowCancelOnExit() ? L"Cancelable" : L"Noncancelable", isIdle ? L"(Idle)" : wxEmptyString)
215 );
216
217 if( deleteMe->AllowCancelOnExit() )
218 deleteMe->_DoInvokeEvent();
219 else
220 {
221 ScopedThreadCancelDisable thr_cancel_scope;
222 deleteMe->_DoInvokeEvent();
223 }
224
225 u64 qpc_end = GetCPUTicks();
226 pxEvtLog.Write( this, deleteMe, wxsFormat(L"Event completed in %ums",
227 (u32)(((qpc_end-m_qpc_Start)*1000) / GetTickFrequency()))
228 );
229
230 synclock.Acquire();
231 m_qpc_Start = 0; // lets the main thread know the message completed.
232 }
233 else
234 {
235 pxEvtLog.Write( this, deleteMe, L"Skipping Event: %s" );
236 deleteMe->PostResult();
237 }
238 }
239 }
240
241 void pxEvtQueue::ProcessIdleEvents()
242 {
243 ProcessEvents( m_idleEvents, true );
244 }
245
246 void pxEvtQueue::ProcessPendingEvents()
247 {
248 ProcessEvents( m_pendingEvents );
249 }
250
251 // This method is provided for wxWidgets API conformance. I like to use PostEvent instead
252 // since it's reminiscent of PostMessage in Windows (and behaves rather similarly).
253 void pxEvtQueue::AddPendingEvent( SysExecEvent& evt )
254 {
255 PostEvent( evt );
256 }
257
258 // Adds an event to the event queue in non-blocking fashion. The thread executing this
259 // event queue will be woken up if it's idle/sleeping.
260 // IMPORTANT: The pointer version of this function will *DELETE* the event object passed
261 // to it automatically when the event has been executed. If you are using a scoped event
262 // you should use the Reference/Handle overload instead!
263 //
264 void pxEvtQueue::PostEvent( SysExecEvent* evt )
265 {
266 ScopedPtr<SysExecEvent> sevt( evt );
267 if( !sevt ) return;
268
269 if( m_Quitting )
270 {
271 sevt->PostResult();
272 return;
273 }
274
275 ScopedLock synclock( m_mtx_pending );
276
277 pxEvtLog.Write( this, evt, pxsFmt(L"Posting event! (pending=%d, idle=%d)", m_pendingEvents.size(), m_idleEvents.size()) );
278
279 m_pendingEvents.push_back( sevt.DetachPtr() );
280 if( m_pendingEvents.size() == 1)
281 m_wakeup.Post();
282 }
283
284 void pxEvtQueue::PostEvent( const SysExecEvent& evt )
285 {
286 PostEvent( evt.Clone() );
287 }
288
289 void pxEvtQueue::PostIdleEvent( SysExecEvent* evt )
290 {
291 if( !evt ) return;
292
293 if( m_Quitting )
294 {
295 evt->PostResult();
296 return;
297 }
298
299 ScopedLock synclock( m_mtx_pending );
300
301 pxEvtLog.Write( this, evt, pxsFmt(L"Posting event! (pending=%d, idle=%d) [idle]", m_pendingEvents.size(), m_idleEvents.size()) );
302
303 if( m_pendingEvents.size() == 0)
304 {
305 m_pendingEvents.push_back( evt );
306 m_wakeup.Post();
307 }
308 else
309 m_idleEvents.push_back( evt );
310 }
311
312 void pxEvtQueue::PostIdleEvent( const SysExecEvent& evt )
313 {
314 PostIdleEvent( evt.Clone() );
315 }
316
317 void pxEvtQueue::ProcessEvent( SysExecEvent& evt )
318 {
319 if( wxThread::GetCurrentId() != m_OwnerThreadId )
320 {
321 SynchronousActionState sync;
322 evt.SetSyncState( sync );
323 PostEvent( evt );
324 sync.WaitForResult();
325 }
326 else
327 evt._DoInvokeEvent();
328 }
329
330 void pxEvtQueue::ProcessEvent( SysExecEvent* evt )
331 {
332 if( !evt ) return;
333
334 if( wxThread::GetCurrentId() != m_OwnerThreadId )
335 {
336 SynchronousActionState sync;
337 evt->SetSyncState( sync );
338 PostEvent( evt );
339 sync.WaitForResult();
340 }
341 else
342 {
343 ScopedPtr<SysExecEvent> deleteMe( evt );
344 deleteMe->_DoInvokeEvent();
345 }
346 }
347
348 bool pxEvtQueue::Rpc_TryInvokeAsync( FnType_Void* method, const wxChar* traceName )
349 {
350 if( wxThread::GetCurrentId() != m_OwnerThreadId )
351 {
352 PostEvent( new SysExecEvent_MethodVoid(method, traceName) );
353 return true;
354 }
355
356 return false;
357 }
358
359 bool pxEvtQueue::Rpc_TryInvoke( FnType_Void* method, const wxChar* traceName )
360 {
361 if( wxThread::GetCurrentId() != m_OwnerThreadId )
362 {
363 SynchronousActionState sync;
364 SysExecEvent_MethodVoid evt(method);
365 evt.Critical();
366 evt.SetSyncState( sync );
367 PostEvent( evt );
368 sync.WaitForResult();
369
370 return true;
371 }
372
373 return false;
374 }
375
376 // This method invokes the derived class Idle implementations (if any) and then enters
377 // the sleep state until such time that new messages are received.
378 //
379 // Extending: Derived classes should override _DoIdle instead, unless it is necessary
380 // to implement post-wakeup behavior.
381 //
382 void pxEvtQueue::Idle()
383 {
384 ProcessIdleEvents();
385 _DoIdle();
386 m_wakeup.WaitWithoutYield();
387 }
388
389 void pxEvtQueue::SetActiveThread()
390 {
391 m_OwnerThreadId = wxThread::GetCurrentId();
392 }
393
394 // --------------------------------------------------------------------------------------
395 // WaitingForThreadedTaskDialog
396 // --------------------------------------------------------------------------------------
397 // Note: currently unused (legacy code). May be utilized at a later date, so I'm leaving
398 // it in (for now!)
399 //
400 class WaitingForThreadedTaskDialog
401 : public wxDialogWithHelpers
402 {
403 private:
404 typedef wxDialogWithHelpers _parent;
405
406 protected:
407 pxThread* m_thread;
408
409 public:
410 WaitingForThreadedTaskDialog( pxThread* thr, wxWindow* parent, const wxString& title, const wxString& content );
411 virtual ~WaitingForThreadedTaskDialog() throw() {}
412
413 protected:
414 void OnCancel_Clicked( wxCommandEvent& evt );
415 void OnTerminateApp_Clicked( wxCommandEvent& evt );
416 };
417
418 // --------------------------------------------------------------------------------------
419 // WaitingForThreadedTaskDialog Implementations
420 // --------------------------------------------------------------------------------------
421 WaitingForThreadedTaskDialog::WaitingForThreadedTaskDialog( pxThread* thr, wxWindow* parent, const wxString& title, const wxString& content )
422 : wxDialogWithHelpers( parent, title )
423 {
424 SetMinWidth( 500 );
425
426 m_thread = thr;
427
428 *this += Text( content ) | StdExpand();
429 *this += 15;
430 *this += Heading(_("Press Cancel to attempt to cancel the action."));
431 *this += Heading(AddAppName(_("Press Terminate to kill %s immediately.")));
432
433 *this += new wxButton( this, wxID_CANCEL );
434 *this += new wxButton( this, wxID_ANY, _("Terminate App") );
435 }
436
437 void WaitingForThreadedTaskDialog::OnCancel_Clicked( wxCommandEvent& evt )
438 {
439 evt.Skip();
440 if( !m_thread ) return;
441 m_thread->Cancel( false );
442
443 if( wxWindow* cancel = FindWindowById( wxID_CANCEL ) ) cancel->Disable();
444 }
445
446 void WaitingForThreadedTaskDialog::OnTerminateApp_Clicked( wxCommandEvent& evt )
447 {
448 // (note: SIGTERM is a "handled" kill that performs shutdown stuff, which typically just crashes anyway)
449 wxKill( wxGetProcessId(), wxSIGKILL );
450 }
451
452 // --------------------------------------------------------------------------------------
453 // ExecutorThread Implementations
454 // --------------------------------------------------------------------------------------
455 ExecutorThread::ExecutorThread( pxEvtQueue* evthandler )
456 {
457 m_EvtHandler = evthandler;
458 }
459
460 bool ExecutorThread::IsRunning() const
461 {
462 if( !m_EvtHandler ) return false;
463 return( !m_EvtHandler->IsShuttingDown() );
464 }
465
466 // Exposes the internal pxEvtQueue::ShutdownQueue API. See pxEvtQueue for details.
467 void ExecutorThread::ShutdownQueue()
468 {
469 if( !m_EvtHandler ) return;
470
471 if( !m_EvtHandler->IsShuttingDown() )
472 m_EvtHandler->ShutdownQueue();
473
474 Block();
475 }
476
477 // Exposes the internal pxEvtQueue::PostEvent API. See pxEvtQueue for details.
478 void ExecutorThread::PostEvent( SysExecEvent* evt )
479 {
480 if( !pxAssert( m_EvtHandler ) ) { delete evt; return; }
481 m_EvtHandler->PostEvent( evt );
482 }
483
484 void ExecutorThread::PostEvent( const SysExecEvent& evt )
485 {
486 if( !pxAssert( m_EvtHandler ) ) return;
487 m_EvtHandler->PostEvent( evt );
488 }
489
490 // Exposes the internal pxEvtQueue::PostIdleEvent API. See pxEvtQueue for details.
491 void ExecutorThread::PostIdleEvent( SysExecEvent* evt )
492 {
493 if( !pxAssert( m_EvtHandler ) ) return;
494 m_EvtHandler->PostIdleEvent( evt );
495 }
496
497 void ExecutorThread::PostIdleEvent( const SysExecEvent& evt )
498 {
499 if( !pxAssert( m_EvtHandler ) ) return;
500 m_EvtHandler->PostIdleEvent( evt );
501 }
502
503 // Exposes the internal pxEvtQueue::ProcessEvent API. See pxEvtQueue for details.
504 void ExecutorThread::ProcessEvent( SysExecEvent* evt )
505 {
506 if( m_EvtHandler )
507 m_EvtHandler->ProcessEvent( evt );
508 else
509 {
510 ScopedPtr<SysExecEvent> deleteMe( evt );
511 deleteMe->_DoInvokeEvent();
512 }
513 }
514
515 void ExecutorThread::ProcessEvent( SysExecEvent& evt )
516 {
517 if( m_EvtHandler )
518 m_EvtHandler->ProcessEvent( evt );
519 else
520 evt._DoInvokeEvent();
521 }
522
523 void ExecutorThread::OnStart()
524 {
525 //if( !m_ExecutorTimer )
526 // m_ExecutorTimer = new wxTimer( wxTheApp, pxEvt_ThreadTaskTimeout_SysExec );
527
528 m_name = L"SysExecutor";
529 _parent::OnStart();
530 }
531
532 void ExecutorThread::ExecuteTaskInThread()
533 {
534 if( !pxAssertDev( m_EvtHandler, "Gimme a damn Event Handler first, object whore." ) ) return;
535
536 m_EvtHandler->SetActiveThread();
537
538 while( true )
539 {
540 if( !pxAssertDev( m_EvtHandler, "Event handler has been deallocated during SysExecutor thread execution." ) ) return;
541
542 m_EvtHandler->Idle();
543 m_EvtHandler->ProcessPendingEvents();
544 if( m_EvtHandler->IsShuttingDown() ) break;
545 }
546 }
547
548 void ExecutorThread::OnCleanupInThread()
549 {
550 //wxGetApp().PostCommand( m_task, pxEvt_ThreadTaskComplete );
551 _parent::OnCleanupInThread();
552 }
553
554 // --------------------------------------------------------------------------------------
555 // Threaded Event Handlers (Pcsx2App)
556 // --------------------------------------------------------------------------------------
557
558 // This event is called when the SysExecutorThread's timer triggers, which means the
559 // VM/system task has taken an oddly long period of time to complete. The task is able
560 // to invoke a modal dialog from here that will grant the user some options for handling
561 // the unresponsive task.
562 void Pcsx2App::OnSysExecutorTaskTimeout( wxTimerEvent& evt )
563 {
564 if( !SysExecutorThread.IsRunning() ) return;
565
566 //BaseThreadedInvocation* task = SysExecutorThread.GetTask();
567 //if( !task ) return;
568
569 //task->ShowModalStatus();
570 }

  ViewVC Help
Powered by ViewVC 1.1.22