More thread sync fixes (these mostly only showed up on linux, because of threads starting a lot slower); fixed by including a startup signal to let the creating thread know when the worker has aquired its persistent locks.

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@2029 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-10-18 03:01:10 +00:00
parent 6b0c9cfdfe
commit 877f59a255
6 changed files with 44 additions and 41 deletions

View File

@ -237,8 +237,8 @@ void mtgsThreadObject::OpenPlugin()
void mtgsThreadObject::ExecuteTaskInThread() void mtgsThreadObject::ExecuteTaskInThread()
{ {
// Required by the underlying SysThreadBase class (is unlocked on exit)
m_RunningLock.Lock(); m_RunningLock.Lock();
m_StartupEvent.Post();
#ifdef RINGBUF_DEBUG_STACK #ifdef RINGBUF_DEBUG_STACK
PacketTagType prevCmd; PacketTagType prevCmd;
@ -247,7 +247,7 @@ void mtgsThreadObject::ExecuteTaskInThread()
pthread_cleanup_push( _clean_close_gs, this ); pthread_cleanup_push( _clean_close_gs, this );
while( true ) while( true )
{ {
m_sem_event.WaitRaw(); // ... because this does a cancel test itself.. m_sem_event.WaitRaw(); // ... because this does a cancel test itself..
StateCheckInThread( false ); // false disables cancel test here! StateCheckInThread( false ); // false disables cancel test here!
m_RingBufferIsBusy = true; m_RingBufferIsBusy = true;
@ -389,7 +389,7 @@ void mtgsThreadObject::ExecuteTaskInThread()
case GS_RINGTYPE_MODECHANGE: case GS_RINGTYPE_MODECHANGE:
_gs_ChangeTimings( tag.data[0], tag.data[1] ); _gs_ChangeTimings( tag.data[0], tag.data[1] );
break; break;
case GS_RINGTYPE_CRC: case GS_RINGTYPE_CRC:
GSsetGameCRC( tag.data[0], 0 ); GSsetGameCRC( tag.data[0], 0 );
break; break;
@ -397,7 +397,7 @@ void mtgsThreadObject::ExecuteTaskInThread()
case GS_RINGTYPE_STARTTIME: case GS_RINGTYPE_STARTTIME:
m_iSlowStart += tag.data[0]; m_iSlowStart += tag.data[0];
break; break;
#ifdef PCSX2_DEVBUILD #ifdef PCSX2_DEVBUILD
default: default:
Console.Error("GSThreadProc, bad packet (%x) at m_RingPos: %x, m_WritePos: %x", tag.command, m_RingPos, m_WritePos); Console.Error("GSThreadProc, bad packet (%x) at m_RingPos: %x, m_WritePos: %x", tag.command, m_RingPos, m_WritePos);

View File

@ -1,6 +1,6 @@
/* PCSX2 - PS2 Emulator for PCs /* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2009 PCSX2 Dev Team * Copyright (C) 2002-2009 PCSX2 Dev Team
* *
* PCSX2 is free software: you can redistribute it and/or modify it under the terms * PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found- * of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version. * ation, either version 3 of the License, or (at your option) any later version.
@ -47,6 +47,7 @@ void SysThreadBase::Start()
_parent::Start(); _parent::Start();
m_ExecMode = ExecMode_Closing; m_ExecMode = ExecMode_Closing;
m_sem_event.Post(); m_sem_event.Post();
m_StartupEvent.Wait();
} }
@ -55,6 +56,7 @@ void SysThreadBase::OnStart()
if( !pxAssertDev( m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode" ) ) return; if( !pxAssertDev( m_ExecMode == ExecMode_NoThreadYet, "SysSustainableThread:Start(): Invalid execution mode" ) ) return;
m_ResumeEvent.Reset(); m_ResumeEvent.Reset();
m_StartupEvent.Reset();
FrankenMutex( m_ExecModeMutex ); FrankenMutex( m_ExecModeMutex );
FrankenMutex( m_RunningLock ); FrankenMutex( m_RunningLock );
@ -83,7 +85,7 @@ void SysThreadBase::OnStart()
bool SysThreadBase::Suspend( bool isBlocking ) bool SysThreadBase::Suspend( bool isBlocking )
{ {
if( IsSelf() || !IsRunning() ) return false; if( IsSelf() || !IsRunning() ) return false;
// shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued // shortcut ExecMode check to avoid deadlocking on redundant calls to Suspend issued
// from Resume or OnResumeReady code. // from Resume or OnResumeReady code.
if( m_ExecMode == ExecMode_Closed ) return false; if( m_ExecMode == ExecMode_Closed ) return false;
@ -152,7 +154,7 @@ bool SysThreadBase::Pause()
// Resumes the core execution state, or does nothing is the core is already running. If // Resumes the core execution state, or does nothing is the core is already running. If
// settings were changed, resets will be performed as needed and emulation state resumed from // settings were changed, resets will be performed as needed and emulation state resumed from
// memory savestates. // memory savestates.
// //
// Note that this is considered a non-blocking action. Most times the state is safely resumed // Note that this is considered a non-blocking action. Most times the state is safely resumed
// on return, but in the case of re-entrant or nested message handling the function may return // on return, but in the case of re-entrant or nested message handling the function may return
// before the thread has resumed. If you need explicit behavior tied to the completion of the // before the thread has resumed. If you need explicit behavior tied to the completion of the
@ -171,20 +173,13 @@ void SysThreadBase::Resume()
ScopedLock locker( m_ExecModeMutex ); ScopedLock locker( m_ExecModeMutex );
// Recursion guard is needed because of the non-blocking Wait if the state
// is Suspending/Closing. Processed events could recurse into Resume, and we'll
// want to silently ignore them.
//RecursionGuard guard( m_resume_guard );
//if( guard.IsReentrant() ) return;
switch( m_ExecMode ) switch( m_ExecMode )
{ {
case ExecMode_Opened: return; case ExecMode_Opened: return;
case ExecMode_NoThreadYet: case ExecMode_NoThreadYet:
Start(); Start();
m_ExecMode = ExecMode_Closing;
// fall through... // fall through...
case ExecMode_Closing: case ExecMode_Closing:
@ -192,14 +187,12 @@ void SysThreadBase::Resume()
// we need to make sure and wait for the emuThread to enter a fully suspended // we need to make sure and wait for the emuThread to enter a fully suspended
// state before continuing... // state before continuing...
//locker.Unlock(); // no deadlocks please, thanks. :)
m_RunningLock.Wait(); m_RunningLock.Wait();
//locker.Lock();
// The entire state coming out of a Wait is indeterminate because of user input // The entire state coming out of a Wait is indeterminate because of user input
// and pending messages being handled. If something doesn't feel right, we should // and pending messages being handled. If something doesn't feel right, we should
// abort. // abort.
if( (m_ExecMode != ExecMode_Closed) && (m_ExecMode != ExecMode_Paused) ) return; if( (m_ExecMode != ExecMode_Closed) && (m_ExecMode != ExecMode_Paused) ) return;
if( g_plugins == NULL ) return; if( g_plugins == NULL ) return;
break; break;
@ -269,9 +262,9 @@ void SysThreadBase::StateCheckInThread( bool isCancelable )
OnSuspendInThread(); OnSuspendInThread();
m_ExecMode = ExecMode_Closed; m_ExecMode = ExecMode_Closed;
m_RunningLock.Unlock(); m_RunningLock.Unlock();
} }
// fallthrough... // fallthrough...
case ExecMode_Closed: case ExecMode_Closed:
while( m_ExecMode == ExecMode_Closed ) while( m_ExecMode == ExecMode_Closed )
m_ResumeEvent.WaitRaw(); m_ResumeEvent.WaitRaw();
@ -279,7 +272,7 @@ void SysThreadBase::StateCheckInThread( bool isCancelable )
m_RunningLock.Lock(); m_RunningLock.Lock();
OnResumeInThread( true ); OnResumeInThread( true );
break; break;
jNO_DEFAULT; jNO_DEFAULT;
} }
} }
@ -354,7 +347,7 @@ void SysCoreThread::ApplySettings( const Pcsx2Config& src )
m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks ); m_resetRecompilers = ( src.Cpu != EmuConfig.Cpu ) || ( src.Gamefixes != EmuConfig.Gamefixes ) || ( src.Speedhacks != EmuConfig.Speedhacks );
m_resetProfilers = (src.Profiler != EmuConfig.Profiler ); m_resetProfilers = (src.Profiler != EmuConfig.Profiler );
const_cast<Pcsx2Config&>(EmuConfig) = src; const_cast<Pcsx2Config&>(EmuConfig) = src;
if( resumeWhenDone ) Resume(); if( resumeWhenDone ) Resume();
@ -373,7 +366,7 @@ SysCoreThread& SysCoreThread::Get()
void SysCoreThread::CpuInitializeMess() void SysCoreThread::CpuInitializeMess()
{ {
if( m_hasValidState ) return; if( m_hasValidState ) return;
wxString elf_file; wxString elf_file;
if( EmuConfig.SkipBiosSplash ) if( EmuConfig.SkipBiosSplash )
{ {
@ -434,7 +427,9 @@ void SysCoreThread::CpuExecute()
void SysCoreThread::ExecuteTaskInThread() void SysCoreThread::ExecuteTaskInThread()
{ {
m_RunningLock.Lock(); m_RunningLock.Lock();
tls_coreThread = this; tls_coreThread = this;
m_StartupEvent.Post();
m_sem_event.WaitRaw(); m_sem_event.WaitRaw();
StateCheckInThread(); StateCheckInThread();

View File

@ -1,6 +1,6 @@
/* PCSX2 - PS2 Emulator for PCs /* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2009 PCSX2 Dev Team * Copyright (C) 2002-2009 PCSX2 Dev Team
* *
* PCSX2 is free software: you can redistribute it and/or modify it under the terms * PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found- * of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version. * ation, either version 3 of the License, or (at your option) any later version.
@ -44,7 +44,7 @@ protected:
// Thread has not been created yet. Typically this is the same as IsRunning() // Thread has not been created yet. Typically this is the same as IsRunning()
// returning FALSE. // returning FALSE.
ExecMode_NoThreadYet, ExecMode_NoThreadYet,
// Close signal has been sent to the thread, but the thread's response is still // Close signal has been sent to the thread, but the thread's response is still
// pending (thread is busy/running). // pending (thread is busy/running).
ExecMode_Closing, ExecMode_Closing,
@ -55,11 +55,11 @@ protected:
// Thread is active and running, with pluigns in an "open" state. // Thread is active and running, with pluigns in an "open" state.
ExecMode_Opened, ExecMode_Opened,
// Pause signal has been sent to the thread, but the thread's response is still // Pause signal has been sent to the thread, but the thread's response is still
// pending (thread is busy/running). // pending (thread is busy/running).
ExecMode_Pausing, ExecMode_Pausing,
// Thread is safely paused, with plugins in an "open" state, and waiting for a // Thread is safely paused, with plugins in an "open" state, and waiting for a
// resume/open signal. // resume/open signal.
ExecMode_Paused, ExecMode_Paused,
@ -73,7 +73,12 @@ protected:
// Used to wake up the thread from sleeping when it's in a suspended state. // Used to wake up the thread from sleeping when it's in a suspended state.
Semaphore m_ResumeEvent; Semaphore m_ResumeEvent;
// Used to signal the creating thread that the worker has entered the running state.
// This is necessary because until the thread has established itself, locking against
// m_RunningLock isn't a reliable synchronization tool.
Semaphore m_StartupEvent;
// Locked whenever the thread is not in a suspended state (either closed or paused). // Locked whenever the thread is not in a suspended state (either closed or paused).
// Issue a Wait against this mutex for performing actions that require the thread // Issue a Wait against this mutex for performing actions that require the thread
// to be suspended. // to be suspended.
@ -92,7 +97,7 @@ public:
{ {
return m_ExecMode > ExecMode_Closed; return m_ExecMode > ExecMode_Closed;
} }
bool IsClosed() const { return !IsOpen(); } bool IsClosed() const { return !IsOpen(); }
ExecutionMode GetExecutionMode() const { return m_ExecMode; } ExecutionMode GetExecutionMode() const { return m_ExecMode; }
@ -101,7 +106,7 @@ public:
virtual bool Suspend( bool isBlocking = true ); virtual bool Suspend( bool isBlocking = true );
virtual void Resume(); virtual void Resume();
virtual bool Pause(); virtual bool Pause();
virtual void StateCheckInThread( bool isCancelable = true ); virtual void StateCheckInThread( bool isCancelable = true );
virtual void OnCleanupInThread(); virtual void OnCleanupInThread();
@ -123,7 +128,7 @@ protected:
// thread, requesting this thread suspend itself temporarily). After this is called, // thread, requesting this thread suspend itself temporarily). After this is called,
// the thread enters a waiting state on the m_ResumeEvent semaphore. // the thread enters a waiting state on the m_ResumeEvent semaphore.
virtual void OnSuspendInThread()=0; virtual void OnSuspendInThread()=0;
// Extending classes should implement this, but should not call it. The parent class // Extending classes should implement this, but should not call it. The parent class
// handles invocation by the following guidelines: Called *in thread* from StateCheckInThread() // handles invocation by the following guidelines: Called *in thread* from StateCheckInThread()
// prior to pausing the thread (ie, when Pause() has been called on a separate thread, // prior to pausing the thread (ie, when Pause() has been called on a separate thread,
@ -163,7 +168,7 @@ public:
virtual void ApplySettings( const Pcsx2Config& src ); virtual void ApplySettings( const Pcsx2Config& src );
virtual void OnResumeReady(); virtual void OnResumeReady();
virtual void Reset(); virtual void Reset();
bool HasValidState() bool HasValidState()
{ {
return m_hasValidState; return m_hasValidState;

View File

@ -41,7 +41,7 @@ bool AppCoreThread::Suspend( bool isBlocking )
m_kevt.m_shiftDown = false; m_kevt.m_shiftDown = false;
m_kevt.m_controlDown = false; m_kevt.m_controlDown = false;
m_kevt.m_altDown = false; m_kevt.m_altDown = false;
return retval; return retval;
} }
@ -66,7 +66,8 @@ void AppCoreThread::Resume()
evt.SetInt( CoreStatus_Suspended ); evt.SetInt( CoreStatus_Suspended );
wxGetApp().AddPendingEvent( evt ); wxGetApp().AddPendingEvent( evt );
sApp.SysExecute(); if( (m_ExecMode != ExecMode_Closing) || (m_ExecMode != ExecMode_Pausing) )
sApp.SysExecute();
} }
} }
@ -83,7 +84,7 @@ void AppCoreThread::OnResumeReady()
void AppCoreThread::OnResumeInThread( bool isSuspended ) void AppCoreThread::OnResumeInThread( bool isSuspended )
{ {
_parent::OnResumeInThread( isSuspended ); _parent::OnResumeInThread( isSuspended );
wxCommandEvent evt( pxEVT_CoreThreadStatus ); wxCommandEvent evt( pxEVT_CoreThreadStatus );
evt.SetInt( CoreStatus_Resumed ); evt.SetInt( CoreStatus_Resumed );
wxGetApp().AddPendingEvent( evt ); wxGetApp().AddPendingEvent( evt );

View File

@ -280,7 +280,7 @@ void MainEmuFrame::OnSettingsLoadSave( void* obj, const IniInterface& evt )
{ {
if( obj == NULL ) return; if( obj == NULL ) return;
MainEmuFrame* mframe = (MainEmuFrame*)obj; MainEmuFrame* mframe = (MainEmuFrame*)obj;
// FIXME: Evil const cast hack! // FIXME: Evil const cast hack!
mframe->LoadSaveRecentIsoList( const_cast<IniInterface&>(evt) ); mframe->LoadSaveRecentIsoList( const_cast<IniInterface&>(evt) );
} }
@ -312,7 +312,7 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title):
m_SaveStatesSubmenu( *MakeStatesSubMenu( MenuId_State_Save01 ) ), m_SaveStatesSubmenu( *MakeStatesSubMenu( MenuId_State_Save01 ) ),
m_MenuItem_Console( *new wxMenuItem( &m_menuMisc, MenuId_Console, L"Show Console", wxEmptyString, wxITEM_CHECK ) ), m_MenuItem_Console( *new wxMenuItem( &m_menuMisc, MenuId_Console, L"Show Console", wxEmptyString, wxITEM_CHECK ) ),
m_Listener_CoreThreadStatus( wxGetApp().Source_CoreThreadStatus(), CmdEvt_Listener( this, OnCoreThreadStatusChanged ) ), m_Listener_CoreThreadStatus( wxGetApp().Source_CoreThreadStatus(), CmdEvt_Listener( this, OnCoreThreadStatusChanged ) ),
m_Listener_CorePluginStatus( wxGetApp().Source_CorePluginStatus(), CmdEvt_Listener( this, OnCorePluginStatusChanged ) ), m_Listener_CorePluginStatus( wxGetApp().Source_CorePluginStatus(), CmdEvt_Listener( this, OnCorePluginStatusChanged ) ),
m_Listener_SettingsApplied( wxGetApp().Source_SettingsApplied(), EventListener<int>( this, OnSettingsApplied ) ), m_Listener_SettingsApplied( wxGetApp().Source_SettingsApplied(), EventListener<int>( this, OnSettingsApplied ) ),
@ -518,6 +518,8 @@ void MainEmuFrame::ReloadRecentLists()
void MainEmuFrame::ApplyCoreStatus() void MainEmuFrame::ApplyCoreStatus()
{ {
bool valstate = SysHasValidState();
GetMenuBar()->Enable( MenuId_Sys_SuspendResume, SysHasValidState() ); GetMenuBar()->Enable( MenuId_Sys_SuspendResume, SysHasValidState() );
GetMenuBar()->Enable( MenuId_Sys_Reset, SysHasValidState() || (g_plugins!=NULL) ); GetMenuBar()->Enable( MenuId_Sys_Reset, SysHasValidState() || (g_plugins!=NULL) );

View File

@ -1,6 +1,6 @@
/* PCSX2 - PS2 Emulator for PCs /* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2009 PCSX2 Dev Team * Copyright (C) 2002-2009 PCSX2 Dev Team
* *
* PCSX2 is free software: you can redistribute it and/or modify it under the terms * PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found- * of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version. * ation, either version 3 of the License, or (at your option) any later version.
@ -70,7 +70,7 @@ bool MainEmuFrame::_DoSelectIsoBrowser()
UpdateIsoSrcFile(); UpdateIsoSrcFile();
return true; return true;
} }
return false; return false;
} }
@ -78,7 +78,7 @@ void MainEmuFrame::Menu_BootCdvd_Click( wxCommandEvent &event )
{ {
CoreThread.Suspend(); CoreThread.Suspend();
if( !wxFileExists( g_Conf->CurrentIso ) ) if( (g_Conf->CdvdSource == CDVDsrc_Iso) && !wxFileExists(g_Conf->CurrentIso) )
{ {
if( !_DoSelectIsoBrowser() ) if( !_DoSelectIsoBrowser() )
{ {