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

  ViewVC Help
Powered by ViewVC 1.1.22