/[pcsx2_0.9.7]/trunk/common/src/Utilities/wxAppWithHelpers.cpp
ViewVC logotype

Contents of /trunk/common/src/Utilities/wxAppWithHelpers.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 31 - (show annotations) (download)
Tue Sep 7 03:24:11 2010 UTC (9 years, 11 months ago) by william
File size: 17428 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 #include "wxAppWithHelpers.h"
18
19 #include "ThreadingInternal.h"
20 #include "PersistentThread.h"
21
22 DEFINE_EVENT_TYPE( pxEvt_DeleteObject );
23 DEFINE_EVENT_TYPE( pxEvt_DeleteThread );
24 DEFINE_EVENT_TYPE( pxEvt_StartIdleEventTimer );
25 DEFINE_EVENT_TYPE( pxEvt_InvokeAction );
26 DEFINE_EVENT_TYPE( pxEvt_SynchronousCommand );
27
28
29 IMPLEMENT_DYNAMIC_CLASS( pxSimpleEvent, wxEvent )
30
31
32 void BaseDeletableObject::DoDeletion()
33 {
34 wxAppWithHelpers* app = wxDynamicCast( wxApp::GetInstance(), wxAppWithHelpers );
35 pxAssume( app != NULL );
36 app->DeleteObject( *this );
37 }
38
39
40 // --------------------------------------------------------------------------------------
41 // SynchronousActionState Implementations
42 // --------------------------------------------------------------------------------------
43
44 void SynchronousActionState::SetException( const BaseException& ex )
45 {
46 m_exception = ex.Clone();
47 }
48
49 void SynchronousActionState::SetException( BaseException* ex )
50 {
51 if( !m_posted )
52 {
53 m_exception = ex;
54 }
55 else if( wxTheApp )
56 {
57 // transport the exception to the main thread, since the message is fully
58 // asynchronous, or has already entered an asynchronous state. Message is sent
59 // as a non-blocking action since proper handling of user errors on async messages
60 // is *usually* to log/ignore it (hah), or to suspend emulation and issue a dialog
61 // box to the user.
62
63 pxExceptionEvent ev( ex );
64 wxTheApp->AddPendingEvent( ev );
65 }
66 }
67
68 void SynchronousActionState::RethrowException() const
69 {
70 if( m_exception ) m_exception->Rethrow();
71 }
72
73 int SynchronousActionState::WaitForResult()
74 {
75 m_sema.WaitNoCancel();
76 RethrowException();
77 return return_value;
78 }
79
80 int SynchronousActionState::WaitForResult_NoExceptions()
81 {
82 m_sema.WaitNoCancel();
83 return return_value;
84 }
85
86 void SynchronousActionState::PostResult( int res )
87 {
88 return_value = res;
89 PostResult();
90 }
91
92 void SynchronousActionState::ClearResult()
93 {
94 m_posted = false;
95 m_exception = NULL;
96 }
97
98 void SynchronousActionState::PostResult()
99 {
100 if( m_posted ) return;
101 m_posted = true;
102 m_sema.Post();
103 }
104
105 // --------------------------------------------------------------------------------------
106 // pxInvokeActionEvent Implementations
107 // --------------------------------------------------------------------------------------
108
109 IMPLEMENT_DYNAMIC_CLASS( pxInvokeActionEvent, wxEvent )
110
111 pxInvokeActionEvent::pxInvokeActionEvent( SynchronousActionState* sema, int msgtype )
112 : wxEvent( 0, msgtype )
113 {
114 m_state = sema;
115 }
116
117 pxInvokeActionEvent::pxInvokeActionEvent( SynchronousActionState& sema, int msgtype )
118 : wxEvent( 0, msgtype )
119 {
120 m_state = &sema;
121 }
122
123 pxInvokeActionEvent::pxInvokeActionEvent( const pxInvokeActionEvent& src )
124 : wxEvent( src )
125 {
126 m_state = src.m_state;
127 }
128
129 void pxInvokeActionEvent::SetException( const BaseException& ex )
130 {
131 SetException( ex.Clone() );
132 }
133
134 void pxInvokeActionEvent::SetException( BaseException* ex )
135 {
136 const wxString& prefix( wxsFormat(L"(%s) ", GetClassInfo()->GetClassName()) );
137 ex->DiagMsg() = prefix + ex->DiagMsg();
138
139 if( !m_state )
140 {
141 ScopedPtr<BaseException> exptr( ex ); // auto-delete it after handling.
142 ex->Rethrow();
143 }
144
145 m_state->SetException( ex );
146 }
147
148 // --------------------------------------------------------------------------------------
149 // pxSynchronousCommandEvent
150 // --------------------------------------------------------------------------------------
151 IMPLEMENT_DYNAMIC_CLASS( pxSynchronousCommandEvent, wxCommandEvent )
152
153 pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState* sema, wxEventType commandType, int winid)
154 : wxCommandEvent( pxEvt_SynchronousCommand, winid )
155 {
156 m_sync = sema;
157 m_realEvent = commandType;
158 }
159
160 pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState& sema, wxEventType commandType, int winid)
161 : wxCommandEvent( pxEvt_SynchronousCommand )
162 {
163 m_sync = &sema;
164 m_realEvent = commandType;
165 }
166
167 pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState* sema, const wxCommandEvent& evt )
168 : wxCommandEvent( evt )
169 {
170 m_sync = sema;
171 m_realEvent = evt.GetEventType();
172 SetEventType( pxEvt_SynchronousCommand );
173 }
174
175 pxSynchronousCommandEvent::pxSynchronousCommandEvent(SynchronousActionState& sema, const wxCommandEvent& evt )
176 : wxCommandEvent( evt )
177 {
178 m_sync = &sema;
179 m_realEvent = evt.GetEventType();
180 SetEventType( pxEvt_SynchronousCommand );
181 }
182
183 pxSynchronousCommandEvent::pxSynchronousCommandEvent( const pxSynchronousCommandEvent& src )
184 : wxCommandEvent( src )
185 {
186 m_sync = src.m_sync;
187 m_realEvent = src.m_realEvent;
188 }
189
190 void pxSynchronousCommandEvent::SetException( const BaseException& ex )
191 {
192 if( !m_sync ) ex.Rethrow();
193 m_sync->SetException( ex );
194 }
195
196 void pxSynchronousCommandEvent::SetException( BaseException* ex )
197 {
198 if( !m_sync )
199 {
200 ScopedPtr<BaseException> exptr( ex ); // auto-delete it after handling.
201 ex->Rethrow();
202 }
203
204 m_sync->SetException( ex );
205 }
206
207 // --------------------------------------------------------------------------------------
208 // pxInvokeMethodEvent
209 // --------------------------------------------------------------------------------------
210 // Unlike pxPingEvent, the Semaphore belonging to this event is typically posted when the
211 // invoked method is completed. If the method can be executed in non-blocking fashion then
212 // it should leave the semaphore postback NULL.
213 //
214 class pxInvokeMethodEvent : public pxInvokeActionEvent
215 {
216 DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxInvokeMethodEvent)
217
218 typedef pxInvokeActionEvent _parent;
219
220 protected:
221 void (*m_Method)();
222
223 public:
224 virtual ~pxInvokeMethodEvent() throw() { }
225 virtual pxInvokeMethodEvent *Clone() const { return new pxInvokeMethodEvent(*this); }
226
227 explicit pxInvokeMethodEvent( void (*method)()=NULL, SynchronousActionState* sema=NULL )
228 : pxInvokeActionEvent( sema )
229 {
230 m_Method = method;
231 }
232
233 explicit pxInvokeMethodEvent( void (*method)(), SynchronousActionState& sema )
234 : pxInvokeActionEvent( sema )
235 {
236 m_Method = method;
237 }
238
239 pxInvokeMethodEvent( const pxInvokeMethodEvent& src )
240 : pxInvokeActionEvent( src )
241 {
242 m_Method = src.m_Method;
243 }
244
245 void SetMethod( void (*method)() )
246 {
247 m_Method = method;
248 }
249
250 protected:
251 void InvokeEvent()
252 {
253 if( m_Method ) m_Method();
254 }
255 };
256
257 IMPLEMENT_DYNAMIC_CLASS( pxInvokeMethodEvent, pxInvokeActionEvent )
258
259 // --------------------------------------------------------------------------------------
260 // pxExceptionEvent implementations
261 // --------------------------------------------------------------------------------------
262 pxExceptionEvent::pxExceptionEvent( const BaseException& ex )
263 {
264 m_except = ex.Clone();
265 }
266
267 void pxExceptionEvent::InvokeEvent()
268 {
269 ScopedPtr<BaseException> deleteMe( m_except );
270 if( deleteMe ) deleteMe->Rethrow();
271 }
272
273 // --------------------------------------------------------------------------------------
274 // wxAppWithHelpers Implementation
275 // --------------------------------------------------------------------------------------
276 //
277 // TODO : Ping dispatch and IdleEvent dispatch can be unified into a single dispatch, which
278 // would mean checking only one list of events per idle event, instead of two. (ie, ping
279 // events can be appended to the idle event list, instead of into their own custom list).
280 //
281 IMPLEMENT_DYNAMIC_CLASS( wxAppWithHelpers, wxApp )
282
283
284 // Posts a method to the main thread; non-blocking. Post occurs even when called from the
285 // main thread.
286 void wxAppWithHelpers::PostMethod( FnType_Void* method )
287 {
288 PostEvent( pxInvokeMethodEvent( method ) );
289 }
290
291 // Posts a method to the main thread; non-blocking. Post occurs even when called from the
292 // main thread.
293 void wxAppWithHelpers::PostIdleMethod( FnType_Void* method )
294 {
295 pxInvokeMethodEvent evt( method );
296 AddIdleEvent( evt );
297 }
298
299 bool wxAppWithHelpers::PostMethodMyself( void (*method)() )
300 {
301 if( wxThread::IsMain() ) return false;
302 PostEvent( pxInvokeMethodEvent( method ) );
303 return true;
304 }
305
306 void wxAppWithHelpers::ProcessMethod( void (*method)() )
307 {
308 if( wxThread::IsMain() )
309 {
310 method();
311 return;
312 }
313
314 SynchronousActionState sync;
315 PostEvent( pxInvokeMethodEvent( method, sync ) );
316 sync.WaitForResult();
317 }
318
319 void wxAppWithHelpers::PostEvent( const wxEvent& evt )
320 {
321 // Const Cast is OK!
322 // Truth is, AddPendingEvent should be a const-qualified parameter, as
323 // it makes an immediate clone copy of the event -- but wxWidgets
324 // fails again in structured C/C++ design design. So I'm forcing it as such
325 // here. -- air
326
327 _parent::AddPendingEvent( const_cast<wxEvent&>(evt) );
328 }
329
330 bool wxAppWithHelpers::ProcessEvent( wxEvent& evt )
331 {
332 // Note: We can't do an automatic blocking post of the message here, because wxWidgets
333 // isn't really designed for it (some events return data to the caller via the event
334 // struct, and posting the event would require a temporary clone, where changes would
335 // be lost).
336
337 AffinityAssert_AllowFrom_MainUI();
338 return _parent::ProcessEvent( evt );
339 }
340
341 bool wxAppWithHelpers::ProcessEvent( wxEvent* evt )
342 {
343 AffinityAssert_AllowFrom_MainUI();
344 ScopedPtr<wxEvent> deleteMe( evt );
345 return _parent::ProcessEvent( *deleteMe );
346 }
347
348 bool wxAppWithHelpers::ProcessEvent( pxInvokeActionEvent& evt )
349 {
350 if( wxThread::IsMain() )
351 return _parent::ProcessEvent( evt );
352 else
353 {
354 SynchronousActionState sync;
355 evt.SetSyncState( sync );
356 AddPendingEvent( evt );
357 sync.WaitForResult();
358 return true;
359 }
360 }
361
362 bool wxAppWithHelpers::ProcessEvent( pxInvokeActionEvent* evt )
363 {
364 if( wxThread::IsMain() )
365 {
366 ScopedPtr<wxEvent> deleteMe( evt );
367 return _parent::ProcessEvent( *deleteMe );
368 }
369 else
370 {
371 SynchronousActionState sync;
372 evt->SetSyncState( sync );
373 AddPendingEvent( *evt );
374 sync.WaitForResult();
375 return true;
376 }
377 }
378
379
380 void wxAppWithHelpers::CleanUp()
381 {
382 // I'm pretty sure the message pump is dead by now, which means we need to run through
383 // idle event list by hand and process the pending Deletion messages (all others can be
384 // ignored -- it's only deletions we want handled, and really we *could* ignore those too
385 // but I like to be tidy. -- air
386
387 //IdleEventDispatcher( "CleanUp" );
388 //DeletionDispatcher();
389 _parent::CleanUp();
390 }
391
392 void pxInvokeActionEvent::_DoInvokeEvent()
393 {
394 AffinityAssert_AllowFrom_MainUI();
395
396 try {
397 InvokeEvent();
398 }
399 catch( BaseException& ex )
400 {
401 SetException( ex );
402 }
403 catch( std::runtime_error& ex )
404 {
405 SetException( new Exception::RuntimeError( ex ) );
406 }
407
408 if( m_state ) m_state->PostResult();
409 }
410
411 void wxAppWithHelpers::OnSynchronousCommand( pxSynchronousCommandEvent& evt )
412 {
413 AffinityAssert_AllowFrom_MainUI();
414
415 evt.SetEventType( evt.GetRealEventType() );
416
417 try {
418 ProcessEvent( evt );
419 }
420 catch( BaseException& ex )
421 {
422 evt.SetException( ex );
423 }
424 catch( std::runtime_error& ex )
425 {
426 evt.SetException( new Exception::RuntimeError( ex, evt.GetClassInfo()->GetClassName() ) );
427 }
428
429 if( Semaphore* sema = evt.GetSemaphore() ) sema->Post();
430 }
431
432 void wxAppWithHelpers::AddIdleEvent( const wxEvent& evt )
433 {
434 ScopedLock lock( m_IdleEventMutex );
435 if( m_IdleEventQueue.size() == 0 )
436 PostEvent( pxSimpleEvent( pxEvt_StartIdleEventTimer ) );
437
438 m_IdleEventQueue.push_back( evt.Clone() );
439 }
440
441 void wxAppWithHelpers::OnStartIdleEventTimer( wxEvent& evt )
442 {
443 ScopedLock lock( m_IdleEventMutex );
444 if( m_IdleEventQueue.size() != 0 )
445 m_IdleEventTimer.Start( 100, true );
446 }
447
448 void wxAppWithHelpers::IdleEventDispatcher( const wxChar* action )
449 {
450 // Recursion is possible thanks to modal dialogs being issued from the idle event handler.
451 // (recursion shouldn't hurt anything anyway, since the node system re-creates the iterator
452 // on each pass)
453
454 //static int __guard=0;
455 //RecursionGuard guard(__guard);
456 //if( !pxAssertDev(!guard.IsReentrant(), "Re-entrant call to IdleEventdispatcher caught on camera!") ) return;
457
458 wxEventList postponed;
459 wxEventList::iterator node;
460
461 ScopedLock lock( m_IdleEventMutex );
462
463 while( node = m_IdleEventQueue.begin(), node != m_IdleEventQueue.end() )
464 {
465 ScopedPtr<wxEvent> deleteMe(*node);
466 m_IdleEventQueue.erase( node );
467
468 lock.Release();
469 if( !Threading::AllowDeletions() && (deleteMe->GetEventType() == pxEvt_DeleteThread) )
470 {
471 postponed.push_back(deleteMe.DetachPtr());
472 }
473 else
474 {
475 DbgCon.WriteLn( Color_Gray, L"(AppIdleQueue:%s) -> Dispatching event '%s'", action, deleteMe->GetClassInfo()->GetClassName() );
476 ProcessEvent( *deleteMe ); // dereference to prevent auto-deletion by ProcessEvent
477 }
478 lock.Acquire();
479 }
480
481 m_IdleEventQueue = postponed;
482 }
483
484 void wxAppWithHelpers::OnIdleEvent( wxIdleEvent& evt )
485 {
486 m_IdleEventTimer.Stop();
487 IdleEventDispatcher( L"Idle" );
488 }
489
490 void wxAppWithHelpers::OnIdleEventTimeout( wxTimerEvent& evt )
491 {
492 IdleEventDispatcher( L"Timeout" );
493 }
494
495 void wxAppWithHelpers::Ping()
496 {
497 DbgCon.WriteLn( Color_Gray, L"App Event Ping Requested from %s thread.", pxGetCurrentThreadName().c_str() );
498
499 SynchronousActionState sync;
500 pxInvokeActionEvent evt( sync );
501 AddIdleEvent( evt );
502 sync.WaitForResult();
503 }
504
505 void wxAppWithHelpers::PostCommand( void* clientData, int evtType, int intParam, long longParam, const wxString& stringParam )
506 {
507 wxCommandEvent evt( evtType );
508 evt.SetClientData( clientData );
509 evt.SetInt( intParam );
510 evt.SetExtraLong( longParam );
511 evt.SetString( stringParam );
512 AddPendingEvent( evt );
513 }
514
515 void wxAppWithHelpers::PostCommand( int evtType, int intParam, long longParam, const wxString& stringParam )
516 {
517 PostCommand( NULL, evtType, intParam, longParam, stringParam );
518 }
519
520 sptr wxAppWithHelpers::ProcessCommand( void* clientData, int evtType, int intParam, long longParam, const wxString& stringParam )
521 {
522 SynchronousActionState sync;
523 pxSynchronousCommandEvent evt( sync, evtType );
524
525 evt.SetClientData( clientData );
526 evt.SetInt( intParam );
527 evt.SetExtraLong( longParam );
528 evt.SetString( stringParam );
529 AddPendingEvent( evt );
530 sync.WaitForResult();
531
532 return sync.return_value;
533 }
534
535 sptr wxAppWithHelpers::ProcessCommand( int evtType, int intParam, long longParam, const wxString& stringParam )
536 {
537 return ProcessCommand( NULL, evtType, intParam, longParam, stringParam );
538 }
539
540 void wxAppWithHelpers::PostAction( const pxInvokeActionEvent& evt )
541 {
542 PostEvent( evt );
543 }
544
545 void wxAppWithHelpers::ProcessAction( pxInvokeActionEvent& evt )
546 {
547 if( !wxThread::IsMain() )
548 {
549 SynchronousActionState sync;
550 evt.SetSyncState( sync );
551 AddPendingEvent( evt );
552 sync.WaitForResult();
553 }
554 else
555 evt._DoInvokeEvent();
556 }
557
558
559 void wxAppWithHelpers::DeleteObject( BaseDeletableObject& obj )
560 {
561 pxAssume( !obj.IsBeingDeleted() );
562 wxCommandEvent evt( pxEvt_DeleteObject );
563 evt.SetClientData( (void*)&obj );
564 AddIdleEvent( evt );
565 }
566
567 void wxAppWithHelpers::DeleteThread( PersistentThread& obj )
568 {
569 //pxAssume( obj.IsBeingDeleted() );
570 wxCommandEvent evt( pxEvt_DeleteThread );
571 evt.SetClientData( (void*)&obj );
572 AddIdleEvent( evt );
573 }
574
575 typedef void (wxEvtHandler::*pxInvokeActionEventFunction)(pxInvokeActionEvent&);
576
577 bool wxAppWithHelpers::OnInit()
578 {
579 #define pxActionEventHandler(func) \
580 (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(pxInvokeActionEventFunction, &func )
581
582 Connect( pxEvt_SynchronousCommand, pxSynchronousEventHandler (wxAppWithHelpers::OnSynchronousCommand) );
583 Connect( pxEvt_InvokeAction, pxActionEventHandler (wxAppWithHelpers::OnInvokeAction) );
584
585 Connect( pxEvt_StartIdleEventTimer, wxEventHandler (wxAppWithHelpers::OnStartIdleEventTimer) );
586
587 Connect( pxEvt_DeleteObject, wxCommandEventHandler (wxAppWithHelpers::OnDeleteObject) );
588 Connect( pxEvt_DeleteThread, wxCommandEventHandler (wxAppWithHelpers::OnDeleteThread) );
589
590 Connect( wxEVT_IDLE, wxIdleEventHandler (wxAppWithHelpers::OnIdleEvent) );
591
592 Connect( m_IdleEventTimer.GetId(), wxEVT_TIMER, wxTimerEventHandler(wxAppWithHelpers::OnIdleEventTimeout) );
593
594 return _parent::OnInit();
595 }
596
597 void wxAppWithHelpers::OnInvokeAction( pxInvokeActionEvent& evt )
598 {
599 evt._DoInvokeEvent(); // wow this is easy!
600 }
601
602 void wxAppWithHelpers::OnDeleteObject( wxCommandEvent& evt )
603 {
604 if( evt.GetClientData() == NULL ) return;
605 delete (BaseDeletableObject*)evt.GetClientData();
606 }
607
608 // Threads have their own deletion handler that propagates exceptions thrown by the thread to the UI.
609 // (thus we have a fairly automatic threaded exception system!)
610 void wxAppWithHelpers::OnDeleteThread( wxCommandEvent& evt )
611 {
612 ScopedPtr<PersistentThread> thr( (PersistentThread*)evt.GetClientData() );
613 if( !thr ) return;
614
615 thr->RethrowException();
616 }
617
618 wxAppWithHelpers::wxAppWithHelpers()
619 : m_IdleEventTimer( this )
620 {
621 #ifdef __WXMSW__
622 // This variable assignment ensures that MSVC links in the TLS setup stubs even in
623 // full optimization builds. Without it, DLLs that use TLS won't work because the
624 // FS segment register won't have been initialized by the main exe, due to tls_insurance
625 // being optimized away >_< --air
626
627 static __threadlocal int tls_insurance = 0;
628 tls_insurance = 1;
629 #endif
630 }
631

  ViewVC Help
Powered by ViewVC 1.1.22