Savestates work! Not much else does yet (settings, etc), but at least this monkey's off my back. I can work on tying up more loose ends now. :)

git-svn-id: http://pcsx2.googlecode.com/svn/trunk@1975 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
Jake.Stine 2009-10-07 19:20:11 +00:00
parent a2a495e842
commit eda5d1d834
49 changed files with 986 additions and 533 deletions

View File

@ -429,6 +429,10 @@
RelativePath="..\..\include\intrin_x86.h" RelativePath="..\..\include\intrin_x86.h"
> >
</File> </File>
<File
RelativePath="..\..\include\Utilities\Listeners.h"
>
</File>
<File <File
RelativePath="..\..\include\Utilities\lnx_memzero.h" RelativePath="..\..\include\Utilities\lnx_memzero.h"
> >

View File

@ -191,7 +191,7 @@ typedef struct _PS2E_SessionInfo
// Sony's assigned serial number, valid only for CD/CDVD games (ASCII-Z string). // Sony's assigned serial number, valid only for CD/CDVD games (ASCII-Z string).
// Ex: SLUS-2932 (if the running app is not a sony-registered game, the serial // Ex: SLUS-2932 (if the running app is not a sony-registered game, the serial
// will be a zero length string). // will be a zero length string).
const char Serial[16]; char Serial[16];
} PS2E_SessionInfo; } PS2E_SessionInfo;

View File

@ -45,7 +45,7 @@
#if defined(PCSX2_DEBUG) #if defined(PCSX2_DEBUG)
# define pxAssertMsg(cond, msg) ( (!!(cond)) || \ # define pxAssertMsg(cond, msg) ( (!!(cond)) || \
(pxOnAssert(__TFILE__, __LINE__, __WXFUNCTION__, _T(#cond), msg), !!(cond)) ) (pxOnAssert(__TFILE__, __LINE__, __WXFUNCTION__, _T(#cond), msg), likely(cond)) )
# define pxAssertDev(cond,msg) pxAssertMsg(cond, msg) # define pxAssertDev(cond,msg) pxAssertMsg(cond, msg)
@ -57,26 +57,28 @@
// Devel builds use __assume for standard assertions and call pxOnAssertDevel // Devel builds use __assume for standard assertions and call pxOnAssertDevel
// for AssertDev brand assertions (which typically throws a LogicError exception). // for AssertDev brand assertions (which typically throws a LogicError exception).
# define pxAssertMsg(cond, msg) (!!(cond)) # define pxAssertMsg(cond, msg) (__assume(cond), likely(cond))
# define pxAssertDev(cond, msg) ( (!!(cond)) || \ # define pxAssertDev(cond, msg) ( (!!(cond)) || \
(pxOnAssert(__TFILE__, __LINE__, __WXFUNCTION__, _T(#cond), msg), !!(cond)) ) (pxOnAssert(__TFILE__, __LINE__, __WXFUNCTION__, _T(#cond), msg), likely(cond)) )
# define pxFail(msg) __assume(false) # define pxFail(msg) (__assume(false), false)
# define pxFailDev(msg ) pxAssertDev(false, msg) # define pxFailDev(msg ) pxAssertDev(false, msg)
#else #else
// Release Builds just use __assume as an optimization, and always return 'true' // Release Builds just use __assume as an optimization, and return the conditional
// indicating the assertion check succeeded (no actual check is performed). // as a result (if .
# define pxAssertMsg(cond, msg) (__assume(cond), true) # define pxAssertMsg(cond, msg) (__assume(cond), likely(cond))
# define pxAssertDev(cond, msg) (__assume(cond), true) # define pxAssertDev(cond, msg) (__assume(cond), likely(cond))
# define pxFail(msg) (__assume(false), true) # define pxFail(msg) (__assume(false), false)
# define pxFailDev(msg) (__assume(false), true) # define pxFailDev(msg) (__assume(false), false)
#endif #endif
__cold
#define pxAssert(cond) pxAssertMsg(cond, (wxChar*)NULL) #define pxAssert(cond) pxAssertMsg(cond, (wxChar*)NULL)
extern void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const wxChar* msg); extern void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const wxChar* msg);

View File

@ -16,6 +16,7 @@
#pragma once #pragma once
#include "Dependencies.h" #include "Dependencies.h"
#include "StringHelpers.h"
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// DESTRUCTOR_CATCHALL - safe destructor helper // DESTRUCTOR_CATCHALL - safe destructor helper
@ -133,7 +134,7 @@ namespace Exception
// //
#define DEFINE_EXCEPTION_COPYTORS( classname ) \ #define DEFINE_EXCEPTION_COPYTORS( classname ) \
virtual ~classname() throw() {} \ virtual ~classname() throw() {} \
virtual void Rethrow() const { throw classname( *this ); } \ virtual void Rethrow() const { throw *this; } \
virtual BaseException* Clone() const { return new classname( *this ); } virtual BaseException* Clone() const { return new classname( *this ); }
#define DEFINE_RUNTIME_EXCEPTION( classname, defmsg ) \ #define DEFINE_RUNTIME_EXCEPTION( classname, defmsg ) \
@ -149,11 +150,13 @@ namespace Exception
explicit classname( const wxString& msg_eng ) { BaseException::InitBaseEx( msg_eng, wxEmptyString ); } explicit classname( const wxString& msg_eng ) { BaseException::InitBaseEx( msg_eng, wxEmptyString ); }
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------
// Generalized Exceptions: RuntimeError / LogicError / ObjectIsNull // RuntimeError / LogicError - Generalized Exceptions
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------
class RuntimeError : public virtual BaseException class RuntimeError : public virtual BaseException
{ {
public:
bool IsSilent;
public: public:
DEFINE_RUNTIME_EXCEPTION( RuntimeError, wxLt("An unhandled runtime error has occurred, somewhere in the depths of Pcsx2's cluttered brain-matter.") ) DEFINE_RUNTIME_EXCEPTION( RuntimeError, wxLt("An unhandled runtime error has occurred, somewhere in the depths of Pcsx2's cluttered brain-matter.") )
}; };
@ -166,7 +169,38 @@ namespace Exception
DEFINE_LOGIC_EXCEPTION( LogicError, wxLt("An unhandled logic error has occurred.") ) DEFINE_LOGIC_EXCEPTION( LogicError, wxLt("An unhandled logic error has occurred.") )
}; };
class ObjectIsNull : public virtual RuntimeError // --------------------------------------------------------------------------------------
// CancelAppEvent - Exception for canceling an event in a non-verbose fashion
// --------------------------------------------------------------------------------------
// Typically the PCSX2 interface issues popup dialogs for runtime errors. This exception
// instead issues a "silent" cancelation that is handled by the app gracefully (generates
// log, and resumes messages queue processing).
//
// I chose to have this exception derive from RuntimeError, since if one is thrown from outside
// an App message loop we'll still want it to be handled in a reasonably graceful manner.
class CancelEvent : public virtual RuntimeError
{
public:
DEFINE_EXCEPTION_COPYTORS( CancelEvent )
explicit CancelEvent( const char* logmsg )
{
m_message_diag = fromUTF8( logmsg );
// overridden message formatters only use the diagnostic version...
}
explicit CancelEvent( const wxString& logmsg=L"No reason given." )
{
m_message_diag = logmsg;
// overridden message formatters only use the diagnostic version...
}
virtual wxString FormatDisplayMessage() const;
virtual wxString FormatDiagnosticMessage() const;
};
// --------------------------------------------------------------------------------------
class ObjectIsNull : public virtual CancelEvent
{ {
public: public:
wxString ObjectName; wxString ObjectName;
@ -175,17 +209,16 @@ namespace Exception
explicit ObjectIsNull( const char* objname="unspecified" ) explicit ObjectIsNull( const char* objname="unspecified" )
{ {
m_message_diag = wxString::FromUTF8( objname ); m_message_diag = fromUTF8( objname );
// overridden message formatters only use the diagnostic version... // overridden message formatters only use the diagnostic version...
} }
virtual wxString FormatDisplayMessage() const; virtual wxString FormatDisplayMessage() const;
virtual wxString FormatDiagnosticMessage() const; virtual wxString FormatDiagnosticMessage() const;
}; };
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------
// OutOfMemory / InvalidOperation / InvalidArgument / IndexBoundsFault / ParseError // OutOfMemory / InvalidOperation / InvalidArgument / IndexBoundsFault / ParseError
// --------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------
class OutOfMemory : public virtual RuntimeError class OutOfMemory : public virtual RuntimeError
@ -282,17 +315,17 @@ namespace Exception
explicit classname( const char* objname, const char* msg=defmsg ) \ explicit classname( const char* objname, const char* msg=defmsg ) \
{ \ { \
BaseException::InitBaseEx( msg ); \ BaseException::InitBaseEx( msg ); \
StreamName = wxString::FromUTF8( objname ); \ StreamName = fromUTF8( objname ); \
} \ } \
explicit classname( const char* objname, const wxString& msg_eng, const wxString& msg_xlt ) \ explicit classname( const char* objname, const wxString& msg_eng, const wxString& msg_xlt ) \
{ \ { \
BaseException::InitBaseEx( msg_eng, msg_xlt ); \ BaseException::InitBaseEx( msg_eng, msg_xlt ); \
StreamName = wxString::FromUTF8( objname ); \ StreamName = fromUTF8( objname ); \
} \ } \
explicit classname( const char* objname, const wxString& msg_eng ) \ explicit classname( const char* objname, const wxString& msg_eng ) \
{ \ { \
BaseException::InitBaseEx( msg_eng, msg_eng ); \ BaseException::InitBaseEx( msg_eng, msg_eng ); \
StreamName = wxString::FromUTF8( objname ); \ StreamName = fromUTF8( objname ); \
} \ } \
explicit classname( const wxString& objname, const wxString& msg_eng ) \ explicit classname( const wxString& objname, const wxString& msg_eng ) \
{ \ { \

View File

@ -0,0 +1,204 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2009 PCSX2 Dev Team
*
* 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-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <wx/event.h>
template< typename EvtType >
struct EventListener
{
typedef void FuncType( void* object, const EvtType& evt );
void* object;
FuncType* OnEvent;
EventListener( FuncType* fnptr )
{
object = NULL;
OnEvent = fnptr;
}
EventListener( void* objhandle, FuncType* fnptr )
{
object = objhandle;
OnEvent = fnptr;
}
bool operator ==( const EventListener& right ) const
{
return (object == right.object) && (OnEvent == right.OnEvent);
}
bool operator !=( const EventListener& right ) const
{
return this->operator ==(right);
}
};
template< typename EvtType >
class EventSource
{
public:
typedef typename EventListener<EvtType> ListenerType;
typedef typename std::list< ListenerType > ListenerList;
typedef typename ListenerList::const_iterator Handle;
protected:
ListenerList m_listeners;
public:
virtual ~EventSource() throw() {}
virtual void Remove( const ListenerType& listener )
{
m_listeners.remove( listener );
}
virtual void Remove( const Handle& listenerHandle )
{
m_listeners.erase( listenerHandle );
}
virtual Handle AddFast( const ListenerType& listener )
{
m_listeners.push_front( listener );
return m_listeners.begin();
}
// Checks for duplicates before adding the event.
virtual inline void Add( const ListenerType& listener );
virtual inline void RemoveObject( const void* object );
Handle Add( void* objhandle, typename ListenerType::FuncType* fnptr )
{
return Add( Handle( objhandle, fnptr ) );
}
void Remove( void* objhandle, typename ListenerType::FuncType* fnptr )
{
Remove( Handle( objhandle, fnptr ) );
}
void Dispatch( const wxCommandEvent& evt ) const
{
Handle iter = m_listeners.begin();
while( iter != m_listeners.end() )
{
try
{
iter->OnEvent( iter->object, evt );
}
catch( Exception::RuntimeError& ex )
{
Console::Error( L"Ignoring runtime error thrown from event listener: " + ex.FormatDiagnosticMessage() );
}
catch( BaseException& ex )
{
}
}
}
};
// --------------------------------------------------------------------------------------
// EventListenerBinding
// --------------------------------------------------------------------------------------
// Encapsulated event listener binding, provides the "benefits" of object unwinding.
//
template< typename EvtType = wxCommandEvent >
class EventListenerBinding
{
public:
typedef typename EventSource<EvtType>::ListenerType ListenerHandle;
typedef typename EventSource<EvtType>::Handle ConstIterator;
protected:
EventSource<EvtType>& m_source;
const ListenerHandle m_listener;
ConstIterator m_iter;
bool m_attached;
public:
EventListenerBinding( EventSource<EvtType>& source, const ListenerHandle& listener, bool autoAttach=true ) :
m_source( source )
, m_listener( listener )
, m_attached( false )
{
// If you want to assert on null pointers, you'll need to do the check yourself. There's
// too many cases where silently ignoring null pointers is the desired behavior.
//if( !pxAssertDev( listener.OnEvent != NULL, "NULL listener callback function." ) ) return;
if( autoAttach ) Attach();
}
virtual ~EventListenerBinding() throw()
{
Detach();
}
void Detach()
{
if( !m_attached ) return;
m_source.Remove( m_iter );
m_attached = false;
}
void Attach()
{
if( m_attached || (m_listener.OnEvent == NULL) ) return;
m_iter = m_source.AddFast( m_listener );
m_attached = true;
}
};
typedef EventSource<wxCommandEvent> CmdEvt_Source;
typedef EventListener<wxCommandEvent> CmdEvt_Listener;
typedef EventListenerBinding<wxCommandEvent> CmdEvt_ListenerBinding;
// Checks for duplicates before adding the event.
template< typename EvtType >
void EventSource<EvtType>::Add( const ListenerType& listener )
{
if( !pxAssertDev( listener.OnEvent != NULL, "NULL listener callback function." ) ) return;
Handle iter = m_listeners.begin();
while( iter != m_listeners.end() )
{
if( *iter == listener ) return;
++iter;
}
AddFast( listener );
}
// removes all listeners which reference the given object. Use for assuring object deletion.
template< typename EvtType >
void EventSource<EvtType>::RemoveObject( const void* object )
{
class PredicatesAreTheThingsOfNightmares
{
protected:
const void* const m_object_match;
public:
PredicatesAreTheThingsOfNightmares( const void* objmatch ) : m_object_match( objmatch ) { }
bool operator()( const ListenerType& src )
{
return src.object == m_object_match;
}
};
m_listeners.remove_if( PredicatesAreTheThingsOfNightmares( object ) );
}

View File

@ -15,6 +15,10 @@
#pragma once #pragma once
#ifdef __WXMSW__
# include <wx/msw/wrapwin.h>
#else
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
// Windows Redtape! No windows.h should be included without it! // Windows Redtape! No windows.h should be included without it!
// //
@ -40,3 +44,4 @@
#undef max #undef max
#endif #endif
#endif

View File

@ -15,25 +15,14 @@
#pragma once #pragma once
#include <semaphore.h>
#include <errno.h> // EBUSY #include <errno.h> // EBUSY
#include <pthread.h> #include <pthread.h>
#include <semaphore.h>
#include "Pcsx2Defs.h" #include "Pcsx2Defs.h"
#include "ScopedPtr.h" #include "ScopedPtr.h"
namespace Exception #undef Yield // release th burden of windows.h global namespace spam.
{
//////////////////////////////////////////////////////////////////////////////////////////
// Thread termination exception, used to quickly terminate threads from anywhere in the
// thread's call stack. This exception is handled by the PCSX2 PersistentThread class. Threads
// not derived from that class will not handle this exception.
//
class ThreadTermination
{
};
}
class wxTimeSpan; class wxTimeSpan;
namespace Threading namespace Threading
@ -57,6 +46,38 @@ namespace Threading
}; };
#endif #endif
// --------------------------------------------------------------------------------------
// NonblockingMutex
// --------------------------------------------------------------------------------------
// This is a very simple non-blocking mutex, which behaves similarly to pthread_mutex's
// trylock(), but without any of the extra overhead needed to set up a structure capable
// of blocking waits. It basically optimizes to a single InterlockedExchange.
//
// Simple use: if TryLock() returns false, the Bool is already interlocked by another thread.
// If TryLock() returns true, you've locked the object and are *responsible* for unlocking
// it later.
//
class NonblockingMutex
{
protected:
volatile long val;
public:
NonblockingMutex() : val( false ) {}
virtual ~NonblockingMutex() throw() {};
bool TryLock() throw()
{
return !_InterlockedExchange( &val, true );
}
bool IsLocked()
{ return !!val; }
void Release()
{ val = false; }
};
struct Semaphore struct Semaphore
{ {
sem_t sema; sem_t sema;
@ -166,7 +187,7 @@ namespace Threading
pthread_t m_thread; pthread_t m_thread;
Semaphore m_sem_event; // general wait event that's needed by most threads. Semaphore m_sem_event; // general wait event that's needed by most threads.
Semaphore m_sem_finished; // used for canceling and closing threads in a deadlock-safe manner Semaphore m_sem_finished; // used for canceling and closing threads in a deadlock-safe manner
MutexLock m_lock_start; // used to lock the Start() code from starting simutaneous threads accidentally. MutexLock m_lock_start; // used to lock the Start() code from starting simultaneous threads accidentally.
volatile long m_detached; // a boolean value which indicates if the m_thread handle is valid volatile long m_detached; // a boolean value which indicates if the m_thread handle is valid
volatile long m_running; // set true by Start(), and set false by Cancel(), Block(), etc. volatile long m_running; // set true by Start(), and set false by Cancel(), Block(), etc.
@ -190,27 +211,49 @@ namespace Threading
bool IsSelf() const; bool IsSelf() const;
wxString GetName() const; wxString GetName() const;
void _ThreadCleanup();
protected: protected:
// Extending classes should always implement your own OnStart(), which is called by // Extending classes should always implement your own OnStart(), which is called by
// Start() once necessary locks have been obtained. Do not override Start() directly // Start() once necessary locks have been obtained. Do not override Start() directly
// unless you're really sure that's what you need to do. ;) // unless you're really sure that's what you need to do. ;)
virtual void OnStart()=0; virtual void OnStart()=0;
virtual void OnThreadCleanup()=0; virtual void OnThreadCleanup()=0;
void DoSetThreadName( const wxString& name );
void DoSetThreadName( __unused const char* name );
void _internal_execute();
// Used to dispatch the thread callback function.
// (handles some thread cleanup on Win32, and is basically a typecast
// on linux).
static void* _internal_callback( void* func );
// Implemented by derived class to handle threading actions! // Implemented by derived class to handle threading actions!
virtual void ExecuteTask()=0; virtual void ExecuteTask()=0;
// Inserts a thread cancellation point. If the thread has received a cancel request, this
// function will throw an SEH exception designed to exit the thread (so make sure to use C++
// object encapsulation for anything that could leak resources, to ensure object unwinding
// and cleanup, or use the DoThreadCleanup() override to perform resource cleanup).
void TestCancel();
// Yields this thread to other threads and checks for cancellation. A sleeping thread should
// always test for cancellation, however if you really don't want to, you can use Threading::Sleep()
// or better yet, disable cancellation of the thread completely with DisableCancellation().
//
// Parameters:
// ms - 'minimum' yield time in milliseconds (rough -- typically yields are longer by 1-5ms
// depending on operating system/platform). If ms is 0 or unspecified, then a single
// timeslice is yielded to other contending threads. If no threads are contending for
// time when ms==0, then no yield is done, but cancellation is still tested.
void Yield( int ms = 0 )
{
pxAssert( IsSelf() );
Threading::Sleep( ms );
TestCancel();
}
// ----------------------------------------------------------------------------
// Section of methods for internal use only.
void _DoSetThreadName( const wxString& name );
void _DoSetThreadName( __unused const char* name );
void _internal_execute();
void _try_virtual_invoke( void (PersistentThread::*method)() );
void _ThreadCleanup();
static void* _internal_callback( void* func );
static void _pt_callback_cleanup( void* handle );
}; };
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////

View File

@ -15,6 +15,10 @@
#pragma once #pragma once
#ifdef _MSC_VER
# pragma warning(disable:4063) // case '1' is not a valid value for switch()
#endif
// These functions are meant for memset operations of constant length only. // These functions are meant for memset operations of constant length only.
// For dynamic length clears, use the C-compiler provided memset instead. // For dynamic length clears, use the C-compiler provided memset instead.

View File

@ -227,14 +227,14 @@ void IConsoleWriter::_Write( const char* fmt, va_list args ) const
{ {
std::string m_format_buffer; std::string m_format_buffer;
vssprintf( m_format_buffer, fmt, args ); vssprintf( m_format_buffer, fmt, args );
Write( wxString::FromUTF8( m_format_buffer.c_str() ) ); Write( fromUTF8( m_format_buffer.c_str() ) );
} }
void IConsoleWriter::_WriteLn( const char* fmt, va_list args ) const void IConsoleWriter::_WriteLn( const char* fmt, va_list args ) const
{ {
std::string m_format_buffer; std::string m_format_buffer;
vssprintf( m_format_buffer, fmt, args ); vssprintf( m_format_buffer, fmt, args );
WriteLn( wxString::FromUTF8( m_format_buffer.c_str() ) ); WriteLn( fromUTF8( m_format_buffer.c_str() ) );
} }
void IConsoleWriter::_WriteLn( ConsoleColors color, const char* fmt, va_list args ) const void IConsoleWriter::_WriteLn( ConsoleColors color, const char* fmt, va_list args ) const

View File

@ -116,11 +116,23 @@ wxString Exception::BaseException::FormatDiagnosticMessage() const
return m_message_diag + L"\n\n" + m_stacktrace; return m_message_diag + L"\n\n" + m_stacktrace;
} }
// ------------------------------------------------------------------------
wxString Exception::CancelEvent::FormatDiagnosticMessage() const
{
// FIXME: This should probably just log a single line from the stacktrace.. ?
return L"Action canceled: " + m_message_diag;
}
wxString Exception::CancelEvent::FormatDisplayMessage() const
{
return L"Action canceled: " + m_message_diag;
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
wxString Exception::ObjectIsNull::FormatDiagnosticMessage() const wxString Exception::ObjectIsNull::FormatDiagnosticMessage() const
{ {
return wxsFormat( return wxsFormat(
L"An attempted reference to the %s has failed; the frame does not exist or it's handle is null.", L"reference to '%s' failed : handle is null.",
m_message_diag.c_str() m_message_diag.c_str()
) + m_stacktrace; ) + m_stacktrace;
} }
@ -128,7 +140,7 @@ wxString Exception::ObjectIsNull::FormatDiagnosticMessage() const
wxString Exception::ObjectIsNull::FormatDisplayMessage() const wxString Exception::ObjectIsNull::FormatDisplayMessage() const
{ {
return wxsFormat( return wxsFormat(
L"An attempted reference to the %s has failed; the frame does not exist or it's handle is null.", L"reference to '%s' failed : handle is null.",
m_message_diag.c_str() m_message_diag.c_str()
); );
} }

View File

@ -15,24 +15,30 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "Threading.h"
#include "wxBaseTools.h"
#include <wx/datetime.h> #ifdef _WIN32
#include <wx/thread.h> # include <wx/msw/wrapwin.h> // for thread renaming features
#endif
#include <wx/app.h> #include <wx/app.h>
#ifdef __LINUX__ #ifdef __LINUX__
# include <signal.h> // for pthread_kill, which is in pthread.h on w32-pthreads # include <signal.h> // for pthread_kill, which is in pthread.h on w32-pthreads
#endif #endif
#include "Threading.h"
#include "wxBaseTools.h"
#include <wx/datetime.h>
#include <wx/thread.h>
using namespace Threading; using namespace Threading;
namespace Threading namespace Threading
{ {
static const wxTimeSpan ts_msec_250( 0, 0, 0, 250 ); static const wxTimeSpan ts_msec_250( 0, 0, 0, 250 );
static void _pt_callback_cleanup( void* handle ) void PersistentThread::_pt_callback_cleanup( void* handle )
{ {
((PersistentThread*)handle)->_ThreadCleanup(); ((PersistentThread*)handle)->_ThreadCleanup();
} }
@ -59,11 +65,11 @@ namespace Threading
{ {
try try
{ {
wxString logfix = L"Thread Destructor for " + m_name; Console.WriteLn( L"Thread Log: Executing destructor for " + m_name );
if( m_running ) if( m_running )
{ {
Console.WriteLn( logfix + L": Waiting for running thread to end."); Console.WriteLn( L"\tWaiting for running thread to end...");
#if wxUSE_GUI #if wxUSE_GUI
m_sem_finished.WaitGui(); m_sem_finished.WaitGui();
#else #else
@ -73,9 +79,7 @@ namespace Threading
// it gets destroyed, otherwise th mutex handle would become invalid. // it gets destroyed, otherwise th mutex handle would become invalid.
ScopedLock locker( m_lock_start ); ScopedLock locker( m_lock_start );
} }
else Threading::Sleep( 1 );
Console.WriteLn( logfix + L": thread not running.");
Sleep( 1 );
Detach(); Detach();
} }
DESTRUCTOR_CATCHALL DESTRUCTOR_CATCHALL
@ -189,47 +193,22 @@ namespace Threading
m_except->Rethrow(); m_except->Rethrow();
} }
// invoked internally when canceling or exiting the thread. Extending classes should implement void PersistentThread::TestCancel()
// OnThreadCleanup() to extend clenup functionality.
void PersistentThread::_ThreadCleanup()
{ {
pxAssertMsg( IsSelf(), "Thread affinity error." ); // only allowed from our own thread, thanks. pxAssert( IsSelf() );
pthread_testcancel();
// Typically thread cleanup needs to lock against thread startup, since both
// will perform some measure of variable inits or resets, depending on how the
// derrived class is implemented.
ScopedLock startlock( m_lock_start );
OnThreadCleanup();
m_running = false;
m_sem_finished.Post();
} }
wxString PersistentThread::GetName() const // Executes the virtual member method
void PersistentThread::_try_virtual_invoke( void (PersistentThread::*method)() )
{ {
return m_name;
}
void PersistentThread::_internal_execute()
{
m_running = true;
DoSetThreadName( m_name );
try { try {
ExecuteTask(); (this->*method)();
}
catch( std::logic_error& ex )
{
throw Exception::LogicError( wxsFormat( L"(thread: %s) STL Logic Error: %s\n\t%s",
GetName().c_str(), fromUTF8( ex.what() ).c_str() )
);
}
catch( Exception::LogicError& ex )
{
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
ex.Rethrow();
} }
// ----------------------------------------------------------------------------
// Neat repackaging for STL Runtime errors...
//
catch( std::runtime_error& ex ) catch( std::runtime_error& ex )
{ {
m_except = new Exception::RuntimeError( m_except = new Exception::RuntimeError(
@ -244,16 +223,79 @@ namespace Threading
) )
); );
} }
// ----------------------------------------------------------------------------
catch( Exception::RuntimeError& ex ) catch( Exception::RuntimeError& ex )
{ {
m_except = ex.Clone(); m_except = ex.Clone();
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg(); m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
} }
// ----------------------------------------------------------------------------
// Should we let logic errors propagate, or swallow them and let the thread manager
// handle them? Hmm..
/*catch( std::logic_error& ex )
{
throw Exception::LogicError( wxsFormat( L"(thread: %s) STL Logic Error: %s\n\t%s",
GetName().c_str(), fromUTF8( ex.what() ).c_str() )
);
}
catch( Exception::LogicError& ex )
{
m_except = ex.Clone();
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
}*/
// ----------------------------------------------------------------------------
// BaseException / std::exception -- same deal. Allow propagation or no?
//
/*catch( std::exception& ex )
{
throw Exception::BaseException( wxsFormat( L"(thread: %s) STL exception: %s\n\t%s",
GetName().c_str(), fromUTF8( ex.what() ).c_str() )
);
}
catch( Exception::BaseException& ex )
{
m_except = ex.Clone();
m_except->DiagMsg() = wxsFormat( L"(thread:%s) ", GetName().c_str() ) + m_except->DiagMsg();
}*/
}
// invoked internally when canceling or exiting the thread. Extending classes should implement
// OnThreadCleanup() to extend cleanup functionality.
void PersistentThread::_ThreadCleanup()
{
pxAssertMsg( IsSelf(), "Thread affinity error." ); // only allowed from our own thread, thanks.
// Typically thread cleanup needs to lock against thread startup, since both
// will perform some measure of variable inits or resets, depending on how the
// derived class is implemented.
ScopedLock startlock( m_lock_start );
_try_virtual_invoke( &PersistentThread::OnThreadCleanup );
m_running = false;
m_sem_finished.Post();
}
wxString PersistentThread::GetName() const
{
return m_name;
}
void PersistentThread::_internal_execute()
{
m_running = true;
_DoSetThreadName( m_name );
_try_virtual_invoke( &PersistentThread::ExecuteTask );
} }
void PersistentThread::OnStart() {} void PersistentThread::OnStart() {}
void PersistentThread::OnThreadCleanup() {} void PersistentThread::OnThreadCleanup() {}
// passed into pthread_create, and is used to dispatch the thread's object oriented
// callback function
void* PersistentThread::_internal_callback( void* itsme ) void* PersistentThread::_internal_callback( void* itsme )
{ {
jASSUME( itsme != NULL ); jASSUME( itsme != NULL );
@ -265,12 +307,12 @@ namespace Threading
return NULL; return NULL;
} }
void PersistentThread::DoSetThreadName( const wxString& name ) void PersistentThread::_DoSetThreadName( const wxString& name )
{ {
DoSetThreadName( toUTF8(name) ); _DoSetThreadName( toUTF8(name) );
} }
void PersistentThread::DoSetThreadName( __unused const char* name ) void PersistentThread::_DoSetThreadName( __unused const char* name )
{ {
pxAssertMsg( IsSelf(), "Thread affinity error." ); // only allowed from our own thread, thanks. pxAssertMsg( IsSelf(), "Thread affinity error." ); // only allowed from our own thread, thanks.

View File

@ -18,7 +18,6 @@
#include "x86emitter/tools.h" #include "x86emitter/tools.h"
#include "Threading.h" #include "Threading.h"
#ifdef _WIN32 #ifdef _WIN32
#include "implement.h" // win32 pthreads implementations. #include "implement.h" // win32 pthreads implementations.
#endif #endif

View File

@ -15,11 +15,13 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "internal.h"
#include "tools.h"
#include "Utilities/RedtapeWindows.h" #include "Utilities/RedtapeWindows.h"
#include "Utilities/Threading.h" #include "Utilities/Threading.h"
#include "internal.h"
#include "tools.h"
using namespace x86Emitter; using namespace x86Emitter;
__aligned16 x86CPU_INFO x86caps; __aligned16 x86CPU_INFO x86caps;

View File

@ -5,7 +5,7 @@
Name="PcsxBaseProperties" Name="PcsxBaseProperties"
OutputDirectory="$(SolutionDir)\bin\$(PcsxSubsection)" OutputDirectory="$(SolutionDir)\bin\$(PcsxSubsection)"
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)" IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
DeleteExtensionsOnClean="*.bsc;*.idb;*.sbr;*.res;*.pch;*.pdb;*.obj;*.ilk;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;$(TargetPath)" DeleteExtensionsOnClean="*.bsc;*.idb;*.sbr;*.res;*.pch;*.pdb;*.obj;*.tlb;*.tli;*.tlh;*.tmp;*.rsp;*.pgc;*.pgd;*.meta;$(TargetPath)"
> >
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
@ -18,6 +18,7 @@
WarningLevel="3" WarningLevel="3"
DebugInformationFormat="3" DebugInformationFormat="3"
CompileAs="0" CompileAs="0"
DisableSpecificWarnings="4063;4100"
/> />
<Tool <Tool
Name="VCLinkerTool" Name="VCLinkerTool"

View File

@ -16,6 +16,7 @@
WarningLevel="3" WarningLevel="3"
DebugInformationFormat="3" DebugInformationFormat="3"
CompileAs="0" CompileAs="0"
DisableSpecificWarnings="4063;4100"
/> />
<Tool <Tool
Name="VCLinkerTool" Name="VCLinkerTool"

View File

@ -19,7 +19,7 @@
#define ENABLE_TIMESTAMPS #define ENABLE_TIMESTAMPS
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> # include <wx/msw/wrapwin.h>
#endif #endif
#include <ctype.h> #include <ctype.h>

View File

@ -17,7 +17,8 @@
#include "IsoFileTools.h" #include "IsoFileTools.h"
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> # include <wx/msw/wrapwin.h>
void *_openfile(const char *filename, int flags) void *_openfile(const char *filename, int flags)
{ {

View File

@ -102,7 +102,6 @@ protected:
int m_CopyCommandTally; int m_CopyCommandTally;
int m_CopyDataTally; int m_CopyDataTally;
volatile bool m_RingBufferIsBusy; volatile bool m_RingBufferIsBusy;
volatile bool m_LoadState;
// Counts the number of vsync frames queued in the MTGS ringbuffer. This is used to // Counts the number of vsync frames queued in the MTGS ringbuffer. This is used to
// throttle the number of frames allowed to be rendered ahead of time for games that // throttle the number of frames allowed to be rendered ahead of time for games that
@ -153,7 +152,8 @@ public:
protected: protected:
void OpenPlugin(); void OpenPlugin();
void OnSuspendInThread(); void OnSuspendInThread();
void OnResumeInThread(); void OnPauseInThread() {}
void OnResumeInThread( bool IsSuspended );
void OnResumeReady(); void OnResumeReady();

View File

@ -206,7 +206,7 @@ struct GIFregisters
u32 padding[3]; u32 padding[3];
tGIF_MODE mode; tGIF_MODE mode;
u32 padding1[3]; u32 padding1[3];
tGIF_STAT stat; tGIF_STAT stat;
u32 padding2[7]; u32 padding2[7];
tGIF_TAG0 tag0; tGIF_TAG0 tag0;
@ -218,7 +218,7 @@ struct GIFregisters
u32 tag3; u32 tag3;
u32 padding6[3]; u32 padding6[3];
tGIF_CNT cnt; tGIF_CNT cnt;
u32 padding7[3]; u32 padding7[3];
tGIF_P3CNT p3cnt; tGIF_P3CNT p3cnt;
u32 padding8[3]; u32 padding8[3];

View File

@ -54,7 +54,6 @@ public:
extern StartupParams g_Startup; extern StartupParams g_Startup;
extern void States_Load( const wxString& file );
extern void States_Save( const wxString& file ); extern void States_Save( const wxString& file );
extern bool States_isSlotUsed(int num); extern bool States_isSlotUsed(int num);

View File

@ -99,7 +99,6 @@ mtgsThreadObject::mtgsThreadObject() :
, m_CopyCommandTally( 0 ) , m_CopyCommandTally( 0 )
, m_CopyDataTally( 0 ) , m_CopyDataTally( 0 )
, m_RingBufferIsBusy( false ) , m_RingBufferIsBusy( false )
, m_LoadState( false )
, m_QueuedFrames( 0 ) , m_QueuedFrames( 0 )
, m_lock_FrameQueueCounter() , m_lock_FrameQueueCounter()
, m_packet_size( 0 ) , m_packet_size( 0 )
@ -127,7 +126,6 @@ void mtgsThreadObject::OnStart()
m_WritePos = 0; m_WritePos = 0;
m_RingBufferIsBusy = false; m_RingBufferIsBusy = false;
m_LoadState = false;
m_QueuedFrames = 0; m_QueuedFrames = 0;
m_packet_size = 0; m_packet_size = 0;
@ -435,9 +433,9 @@ void mtgsThreadObject::OnSuspendInThread()
_clean_close_gs( NULL ); _clean_close_gs( NULL );
} }
void mtgsThreadObject::OnResumeInThread() void mtgsThreadObject::OnResumeInThread( bool isSuspended )
{ {
if( !m_LoadState ) if( isSuspended )
OpenPlugin(); OpenPlugin();
} }
@ -812,7 +810,6 @@ void mtgsThreadObject::Freeze( int mode, MTGS_FreezeData& data )
{ {
AtomicExchange( m_RingPos, m_WritePos ); AtomicExchange( m_RingPos, m_WritePos );
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data ); SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
m_LoadState = true;
SetEvent(); SetEvent();
Resume(); Resume();
} }
@ -820,7 +817,6 @@ void mtgsThreadObject::Freeze( int mode, MTGS_FreezeData& data )
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data ); SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
mtgsWaitGS(); mtgsWaitGS();
m_LoadState = false;
} }
// Waits for the GS to empty out the entire ring buffer contents. // Waits for the GS to empty out the entire ring buffer contents.

View File

@ -90,7 +90,7 @@ struct LegacyApi_CommonMethod
// returns the method name as a wxString, converted from UTF8. // returns the method name as a wxString, converted from UTF8.
wxString GetMethodName( PluginsEnum_t pid ) const wxString GetMethodName( PluginsEnum_t pid ) const
{ {
return tbl_PluginInfo[pid].GetShortname() + wxString::FromUTF8( MethodName ); return tbl_PluginInfo[pid].GetShortname() + fromUTF8( MethodName );
} }
}; };
@ -107,7 +107,7 @@ struct LegacyApi_ReqMethod
// returns the method name as a wxString, converted from UTF8. // returns the method name as a wxString, converted from UTF8.
wxString GetMethodName( ) const wxString GetMethodName( ) const
{ {
return wxString::FromUTF8( MethodName ); return fromUTF8( MethodName );
} }
}; };
@ -118,7 +118,7 @@ struct LegacyApi_OptMethod
VoidMethod** Dest; // Target function where the binding is saved. VoidMethod** Dest; // Target function where the binding is saved.
// returns the method name as a wxString, converted from UTF8. // returns the method name as a wxString, converted from UTF8.
wxString GetMethodName() const { return wxString::FromUTF8( MethodName ); } wxString GetMethodName() const { return fromUTF8( MethodName ); }
}; };

View File

@ -32,10 +32,21 @@ struct PluginInfo
wxString GetShortname() const wxString GetShortname() const
{ {
return wxString::FromUTF8( shortname ); return fromUTF8( shortname );
} }
}; };
#ifdef _MSC_VER
// Disabling C4673: throwing 'Exception::Blah' the following types will not be considered at the catch site
// The warning is bugged, and happens even though we're properly inheriting classes with
// 'virtual' qualifiers. But since the warning is potentially useful elsewhere, I disable
// it only for the scope of these exceptions.
# pragma warning(push)
# pragma warning(disable:4673)
#endif
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Plugin-related Exceptions // Plugin-related Exceptions
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -139,9 +150,12 @@ namespace Exception
virtual wxString FormatDiagnosticMessage() const; virtual wxString FormatDiagnosticMessage() const;
virtual wxString FormatDisplayMessage() const; virtual wxString FormatDisplayMessage() const;
}; };
}; };
#ifdef _MSC_VER
# pragma warning(pop)
#endif
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// LegacyPluginAPI_Common // LegacyPluginAPI_Common
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -47,7 +47,7 @@ namespace R5900Exception
void Init( const char*msg ) void Init( const char*msg )
{ {
m_message = wxString::FromUTF8( msg ); m_message = fromUTF8( msg );
cpuState = cpuRegs; cpuState = cpuRegs;
} }
}; };

View File

@ -13,57 +13,98 @@
* If not, see <http://www.gnu.org/licenses/>. * If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include "App.h" #include "App.h"
#include "HostGui.h" #include "HostGui.h"
#include "zlib/zlib.h" #include "zlib/zlib.h"
static SafeArray<u8> state_buffer; static SafeArray<u8> state_buffer;
// Simple lock boolean for the state buffer in use by a thread. This simple solution works because // Simple lock boolean for the state buffer being in use by a thread.
// we are assured that state save/load actions will only be initiated from the main thread. static NonblockingMutex state_buffer_lock;
static bool state_buffer_lock = false;
// This boolean is to keep the system from resuming emulation until the current state has completely // This boolean is to keep the system from resuming emulation until the current state has completely
// uploaded or downloaded itself. It is only modified from the main thread, and should only be read // uploaded or downloaded itself. It is only modified from the main thread, and should only be read
// form the main thread. // form the main thread.
bool sys_resume_lock = false; bool sys_resume_lock = false;
// -------------------------------------------------------------------------------------- static FnType_OnThreadComplete* Callback_FreezeFinished = NULL;
// StateThread_Freeze
// -------------------------------------------------------------------------------------- enum
class StateThread_Freeze : public PersistentThread {
StateThreadAction_None = 0,
StateThreadAction_Create,
StateThreadAction_Restore,
StateThreadAction_ZipToDisk,
StateThreadAction_UnzipFromDisk,
};
class _BaseStateThread : public PersistentThread
{ {
typedef PersistentThread _parent; typedef PersistentThread _parent;
public: public:
StateThread_Freeze( const wxString& file ) virtual ~_BaseStateThread() throw()
{ {
m_name = L"SaveState::Freeze"; state_buffer_lock.Release(); // just in case;
AllowFromMainThreadOnly();
if( state_buffer_lock )
throw Exception::RuntimeError( "Cannot save state; a previous save or load action is already in progress." );
Start();
sys_resume_lock = true;
} }
protected: protected:
void OnStart() {} _BaseStateThread( const char* name, FnType_OnThreadComplete* onFinished )
{
Callback_FreezeFinished = onFinished;
m_name = L"StateThread::" + fromUTF8(name);
}
void OnStart()
{
if( !state_buffer_lock.TryLock() )
throw Exception::CancelEvent( m_name + L"request ignored: state copy buffer is already locked!" );
}
void SendFinishEvent( int type )
{
wxCommandEvent evt( pxEVT_FreezeThreadFinished );
evt.SetClientData( this );
evt.SetInt( type );
wxGetApp().AddPendingEvent( evt );
}
};
// --------------------------------------------------------------------------------------
// StateThread_Freeze
// --------------------------------------------------------------------------------------
class StateThread_Freeze : public _BaseStateThread
{
typedef _BaseStateThread _parent;
public:
StateThread_Freeze( FnType_OnThreadComplete* onFinished ) : _BaseStateThread( "Freeze", onFinished )
{
if( !SysHasValidState() )
throw Exception::RuntimeError( L"Cannot complete state freeze request; the virtual machine state is reset.", _("You'll need to start a new virtual machine before you can save its state.") );
}
protected:
void OnStart()
{
_parent::OnStart();
sys_resume_lock = true;
sCoreThread.Pause();
}
void ExecuteTask() void ExecuteTask()
{ {
memSavingState( state_buffer ).FreezeAll(); memSavingState( state_buffer ).FreezeAll();
} }
void OnThreadCleanup() void OnThreadCleanup()
{ {
wxCommandEvent evt( pxEVT_FreezeFinished ); SendFinishEvent( StateThreadAction_Create );
evt.SetClientData( this );
wxGetApp().AddPendingEvent( evt );
_parent::OnThreadCleanup(); _parent::OnThreadCleanup();
} }
}; };
@ -71,37 +112,36 @@ protected:
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// StateThread_Thaw // StateThread_Thaw
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class StateThread_Thaw : public PersistentThread class StateThread_Thaw : public _BaseStateThread
{ {
typedef PersistentThread _parent; typedef _BaseStateThread _parent;
public: public:
StateThread_Thaw( const wxString& file ) StateThread_Thaw( FnType_OnThreadComplete* onFinished ) : _BaseStateThread( "Thaw", onFinished ) { }
{
m_name = L"SaveState::Thaw";
AllowFromMainThreadOnly();
if( state_buffer_lock )
throw Exception::RuntimeError( "Cannot sload state; a previous save or load action is already in progress." );
Start();
sys_resume_lock = true;
}
protected: protected:
void OnStart() {} void OnStart()
{
_parent::OnStart();
if( state_buffer.IsDisposed() )
{
state_buffer_lock.Release();
throw Exception::RuntimeError( "ThawState request made, but no valid state exists!" );
}
sys_resume_lock = true;
sCoreThread.Pause();
}
void ExecuteTask() void ExecuteTask()
{ {
memSavingState( state_buffer ).FreezeAll(); memLoadingState( state_buffer ).FreezeAll();
} }
void OnThreadCleanup() void OnThreadCleanup()
{ {
wxCommandEvent evt( pxEVT_FreezeFinished ); SendFinishEvent( StateThreadAction_Restore );
evt.SetClientData( this );
wxGetApp().AddPendingEvent( evt );
_parent::OnThreadCleanup(); _parent::OnThreadCleanup();
} }
}; };
@ -109,57 +149,46 @@ protected:
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// StateThread_ZipToDisk // StateThread_ZipToDisk
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class StateThread_ZipToDisk : public PersistentThread class StateThread_ZipToDisk : public _BaseStateThread
{ {
typedef PersistentThread _parent; typedef _BaseStateThread _parent;
protected: protected:
gzFile m_gzfp; const wxString m_filename;
gzFile m_gzfp;
public: public:
StateThread_ZipToDisk( const wxString& file ) : m_gzfp( NULL ) StateThread_ZipToDisk( FnType_OnThreadComplete* onFinished, const wxString& file ) :
_BaseStateThread( "ZipToDisk", onFinished )
, m_filename( file )
, m_gzfp( NULL )
{ {
m_name = L"SaveState::ZipToDisk";
AllowFromMainThreadOnly();
if( state_buffer_lock )
throw Exception::RuntimeError( "Cannot save state; a previous save or load action is already in progress." );
m_gzfp = gzopen( file.ToUTF8().data(), "wb" );
if( m_gzfp == NULL )
throw Exception::CreateStream( file, "Cannot create savestate file for writing." );
try{ Start(); }
catch(...)
{
gzclose( m_gzfp ); m_gzfp = NULL;
throw;
}
sys_resume_lock = true;
} }
~StateThread_ZipToDisk() throw() ~StateThread_ZipToDisk() throw()
{ {
sys_resume_lock = false; // just in case;
if( m_gzfp != NULL ) gzclose( m_gzfp ); if( m_gzfp != NULL ) gzclose( m_gzfp );
} }
protected: protected:
void OnStart() {} void OnStart()
{
_parent::OnStart();
m_gzfp = gzopen( toUTF8(m_filename), "wb" );
if( m_gzfp == NULL )
throw Exception::CreateStream( m_filename, "Cannot create savestate file for writing." );
}
void ExecuteTask() void ExecuteTask()
{ {
Sleep( 2 ); Yield( 2 );
if( gzwrite( (gzFile)m_gzfp, state_buffer.GetPtr(), state_buffer.GetSizeInBytes() ) < state_buffer.GetSizeInBytes() ) if( gzwrite( (gzFile)m_gzfp, state_buffer.GetPtr(), state_buffer.GetSizeInBytes() ) < state_buffer.GetSizeInBytes() )
throw Exception::BadStream(); throw Exception::BadStream();
} }
void OnThreadCleanup() void OnThreadCleanup()
{ {
wxCommandEvent evt( pxEVT_FreezeFinished ); SendFinishEvent( StateThreadAction_ZipToDisk );
evt.SetClientData( this ); // tells message to clean us up.
wxGetApp().AddPendingEvent( evt );
_parent::OnThreadCleanup(); _parent::OnThreadCleanup();
} }
}; };
@ -168,43 +197,41 @@ protected:
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// StateThread_UnzipFromDisk // StateThread_UnzipFromDisk
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
class StateThread_UnzipFromDisk : public PersistentThread class StateThread_UnzipFromDisk : public _BaseStateThread
{ {
typedef PersistentThread _parent; typedef _BaseStateThread _parent;
protected: protected:
gzFile m_gzfp; const wxString m_filename;
gzFile m_gzfp;
// set true only once the whole file has finished loading. IF the thread is canceled or
// an error occurs, this will remain false.
bool m_finished;
public: public:
StateThread_UnzipFromDisk( const wxString& file ) : m_gzfp( NULL ) StateThread_UnzipFromDisk( FnType_OnThreadComplete* onFinished, const wxString& file ) :
_BaseStateThread( "UnzipFromDisk", onFinished )
, m_filename( file )
, m_gzfp( NULL )
, m_finished( false )
{ {
m_name = L"SaveState::UnzipFromDisk";
AllowFromMainThreadOnly();
if( state_buffer_lock )
throw Exception::RuntimeError( "Cannot save state; a previous save or load action is already in progress." );
m_gzfp = gzopen( file.ToUTF8().data(), "wb" );
if( m_gzfp == NULL )
throw Exception::CreateStream( file, "Cannot create savestate file for writing." );
try{ Start(); }
catch(...)
{
gzclose( m_gzfp ); m_gzfp = NULL;
throw;
}
sys_resume_lock = true;
} }
~StateThread_UnzipFromDisk() throw() ~StateThread_UnzipFromDisk() throw()
{ {
sys_resume_lock = false; // just in case;
if( m_gzfp != NULL ) gzclose( m_gzfp ); if( m_gzfp != NULL ) gzclose( m_gzfp );
} }
protected: protected:
void OnStart() {} void OnStart()
{
_parent::OnStart();
m_gzfp = gzopen( toUTF8(m_filename), "rb" );
if( m_gzfp == NULL )
throw Exception::CreateStream( m_filename, "Cannot open savestate file for reading." );
}
void ExecuteTask() void ExecuteTask()
{ {
@ -217,70 +244,95 @@ protected:
state_buffer.ExactAlloc( curidx+BlockSize ); state_buffer.ExactAlloc( curidx+BlockSize );
gzread( m_gzfp, state_buffer.GetPtr(curidx), BlockSize ); gzread( m_gzfp, state_buffer.GetPtr(curidx), BlockSize );
curidx += BlockSize; curidx += BlockSize;
TestCancel();
} while( !gzeof(m_gzfp) ); } while( !gzeof(m_gzfp) );
m_finished = true;
} }
void OnThreadCleanup() void OnThreadCleanup()
{ {
wxCommandEvent evt( pxEVT_ThawFinished ); SendFinishEvent( StateThreadAction_UnzipFromDisk );
evt.SetClientData( this ); // tells message to clean us up.
wxGetApp().AddPendingEvent( evt );
_parent::OnThreadCleanup(); _parent::OnThreadCleanup();
} }
}; };
void Pcsx2App::OnFreezeFinished( wxCommandEvent& evt ) void Pcsx2App::OnFreezeThreadFinished( wxCommandEvent& evt )
{ {
state_buffer.Dispose(); // clear the OnFreezeFinsihed to NULL now, in case of error.
state_buffer_lock = false; // (but only actually run it if no errors occur)
FnType_OnThreadComplete* fn_tmp = Callback_FreezeFinished;
Callback_FreezeFinished = NULL;
SysClearExecutionCache(); {
sCoreThread.Resume(); ScopedPtr<PersistentThread> thr( (PersistentThread*)evt.GetClientData() );
if( !pxAssertDev( thr != NULL, "NULL thread handle on freeze finished?" ) ) return;
state_buffer_lock.Release();
sys_resume_lock = false;
thr->RethrowException();
}
delete (PersistentThread*)evt.GetClientData(); if( fn_tmp != NULL ) fn_tmp( evt );
//m_evtsrc_FreezeThreadFinished.Dispatch( evt );
} }
void Pcsx2App::OnThawFinished( wxCommandEvent& evt ) void OnFinished_Resume( const wxCommandEvent& evt )
{ {
PersistentThread* thr = (PersistentThread*)evt.GetClientData(); if( evt.GetInt() == StateThreadAction_Restore )
if( thr == NULL )
{ {
pxAssert( false ); // Successfully restored state, so remove the copy. Don't remove it sooner
return; // because the thread may have failed with some exception/error.
state_buffer.Dispose();
SysClearExecutionCache();
} }
/*catch( Exception::BadSavedState& ex)
{
// At this point we can return control back to the user, no questions asked.
// StateLoadErrors are only thorwn if the load failed prior to any virtual
// machine memory contents being changed. (usually missing file errors)
Console.Notice( ex.FormatDiagnosticMessage() );
sCoreThread.Resume();
}*/
state_buffer.Dispose();
state_buffer_lock = false;
SysClearExecutionCache();
sCoreThread.Resume(); sCoreThread.Resume();
delete (PersistentThread*)evt.GetClientData();
} }
static wxString zip_dest_filename;
void OnFinished_ZipToDisk( const wxCommandEvent& evt )
{
if( !pxAssertDev( evt.GetInt() == StateThreadAction_Create, "Unexpected StateThreadAction value, aborting save." ) ) return;
if( zip_dest_filename.IsEmpty() )
{
Console.Notice( "Cannot save state to disk: empty filename specified." );
return;
}
// Phase 2: Record to disk!!
(new StateThread_ZipToDisk( OnFinished_Resume, zip_dest_filename ))->Start();
}
void OnFinished_Restore( const wxCommandEvent& evt )
{
if( !pxAssertDev( evt.GetInt() == StateThreadAction_UnzipFromDisk, "Unexpected StateThreadAction value, aborting restore." ) ) return;
// Phase 2: Restore over existing VM state!!
(new StateThread_Thaw( OnFinished_Resume ))->Start();
}
// =====================================================================================================
// StateCopy Public Interface
// =====================================================================================================
void StateCopy_SaveToFile( const wxString& file ) void StateCopy_SaveToFile( const wxString& file )
{ {
if( state_buffer_lock ) return; if( state_buffer_lock.IsLocked() ) return;
new StateThread_ZipToDisk( file ); zip_dest_filename = file;
(new StateThread_Freeze( OnFinished_ZipToDisk ))->Start();
Console.Status( wxsFormat( L"Saving savestate to file: %s", zip_dest_filename.c_str() ) );
} }
void StateCopy_LoadFromFile( const wxString& file ) void StateCopy_LoadFromFile( const wxString& file )
{ {
if( state_buffer_lock ) return; if( state_buffer_lock.IsLocked() ) return;
sCoreThread.Pause();
sCoreThread.ShortSuspend(); (new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start();
new StateThread_UnzipFromDisk( file );
} }
// Saves recovery state info to the given saveslot, or saves the active emulation state // Saves recovery state info to the given saveslot, or saves the active emulation state
@ -289,8 +341,28 @@ void StateCopy_LoadFromFile( const wxString& file )
// the one in the memory save. :) // the one in the memory save. :)
void StateCopy_SaveToSlot( uint num ) void StateCopy_SaveToSlot( uint num )
{ {
if( state_buffer_lock ) return; zip_dest_filename = SaveStateBase::GetFilename( num );
StateCopy_SaveToFile( SaveStateBase::GetFilename( num ) ); (new StateThread_Freeze( OnFinished_ZipToDisk ))->Start();
Console.Status( "Saving savestate to slot %d...", num );
Console.Status( wxsFormat(L"\tfilename: %s", zip_dest_filename.c_str()) );
}
void StateCopy_LoadFromSlot( uint slot )
{
if( state_buffer_lock.IsLocked() ) return;
wxString file( SaveStateBase::GetFilename( slot ) );
if( !wxFileExists( file ) )
{
Console.Notice( "Savestate slot %d is empty.", slot );
return;
}
Console.Status( "Loading savestate from slot %d...", slot );
Console.Status( wxsFormat(L"\tfilename: %s", file.c_str()) );
sCoreThread.Pause();
(new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start();
} }
bool StateCopy_IsValid() bool StateCopy_IsValid()
@ -310,46 +382,19 @@ bool StateCopy_HasPartialState()
void StateCopy_FreezeToMem() void StateCopy_FreezeToMem()
{ {
if( state_buffer_lock ) return; if( state_buffer_lock.IsLocked() ) return;
(new StateThread_Freeze( OnFinished_Restore ))->Start();
} }
void StateCopy_ThawFromMem() void StateCopy_ThawFromMem()
{ {
if( state_buffer_lock ) return; if( state_buffer_lock.IsLocked() ) return;
new StateThread_Thaw( OnFinished_Restore );
} }
void StateCopy_Clear() void StateCopy_Clear()
{ {
if( state_buffer_lock ) return; if( state_buffer_lock.IsLocked() ) return;
state_buffer.Dispose(); state_buffer.Dispose();
} }
// Creates a full recovery of the entire emulation state (CPU and all plugins).
// If a current recovery state is already present, then nothing is done (the
// existing recovery state takes precedence since if it were out-of-date it'd be
// deleted!).
void MakeFull()
{
//if( g_RecoveryState ) return;
//if( !SysHasValidState() ) return;
/*
try
{
g_RecoveryState = new SafeArray<u8>( L"Memory Savestate Recovery" );
memSavingState( *g_RecoveryState ).FreezeAll();
}
catch( Exception::RuntimeError& ex )
{
Msgbox::Alert( wxsFormat( // fixme: needs proper translation
L"PCSX2 encountered an error while trying to backup/suspend the PS2 VirtualMachine state. "
L"You may resume emulation without losing any data, however the machine state will not be "
L"able to recover if you make changes to your PCSX2 configuration.\n\n"
L"Details: %s", ex.FormatDisplayMessage().c_str() )
);
g_RecoveryState = NULL;
}*/
}

View File

@ -136,6 +136,9 @@ static const int MainMemorySizeInBytes =
void SaveStateBase::FreezeMainMemory() void SaveStateBase::FreezeMainMemory()
{ {
if( IsLoading() )
PreLoadPrep();
// First Block - Memory Dumps // First Block - Memory Dumps
// --------------------------- // ---------------------------
FreezeMem(PS2MEM_BASE, Ps2MemSize::Base); // 32 MB main memory FreezeMem(PS2MEM_BASE, Ps2MemSize::Base); // 32 MB main memory
@ -149,8 +152,8 @@ void SaveStateBase::FreezeMainMemory()
void SaveStateBase::FreezeRegisters() void SaveStateBase::FreezeRegisters()
{ {
//if( IsLoading() ) if( IsLoading() )
// PreLoadPrep(); PreLoadPrep();
// Second Block - Various CPU Registers and States // Second Block - Various CPU Registers and States
// ----------------------------------------------- // -----------------------------------------------

View File

@ -202,5 +202,6 @@ extern void StateCopy_ThawFromMem();
extern void StateCopy_SaveToFile( const wxString& file ); extern void StateCopy_SaveToFile( const wxString& file );
extern void StateCopy_LoadFromFile( const wxString& file ); extern void StateCopy_LoadFromFile( const wxString& file );
extern void StateCopy_SaveToSlot( uint num ); extern void StateCopy_SaveToSlot( uint num );
extern void StateCopy_LoadFromSlot( uint slot );
extern void StateCopy_Clear(); extern void StateCopy_Clear();

View File

@ -139,7 +139,7 @@ SysCoreAllocations::SysCoreAllocations()
throw Exception::OutOfMemory( throw Exception::OutOfMemory(
wxsFormat( // Diagnostic (english) wxsFormat( // Diagnostic (english)
L"std::bad_alloc caught while trying to allocate memory for the PS2 Virtual Machine.\n" L"std::bad_alloc caught while trying to allocate memory for the PS2 Virtual Machine.\n"
L"Error Details: " + wxString::FromUTF8( ex.what() ) L"Error Details: " + fromUTF8( ex.what() )
), ),
GetMemoryErrorVM() // translated GetMemoryErrorVM() // translated

View File

@ -55,7 +55,6 @@ void SysSuspendableThread::OnStart()
_parent::OnStart(); _parent::OnStart();
} }
// Pauses the emulation state at the next PS2 vsync, and returns control to the calling // Pauses the emulation state at the next PS2 vsync, and returns control to the calling
// thread; or does nothing if the core is already suspended. Calling this thread from the // thread; or does nothing if the core is already suspended. Calling this thread from the
// Core thread will result in deadlock. // Core thread will result in deadlock.
@ -66,6 +65,11 @@ void SysSuspendableThread::OnStart()
// is mostly useful for starting certain non-Emu related gui activities (improves gui // is mostly useful for starting certain non-Emu related gui activities (improves gui
// responsiveness). // responsiveness).
// //
// Exceptions:
// CancelEvent - thrown if the thread is already in a Paused state. Because actions that
// pause emulation typically rely on plugins remaining loaded/active, Suspension must
// cansel itself forcefully or risk crashing whatever other action is in progress.
//
void SysSuspendableThread::Suspend( bool isBlocking ) void SysSuspendableThread::Suspend( bool isBlocking )
{ {
if( IsSelf() || !IsRunning() ) return; if( IsSelf() || !IsRunning() ) return;
@ -76,11 +80,34 @@ void SysSuspendableThread::Suspend( bool isBlocking )
if( m_ExecMode == ExecMode_Suspended ) if( m_ExecMode == ExecMode_Suspended )
return; return;
if( m_ExecMode == ExecMode_Pausing || m_ExecMode == ExecMode_Paused )
throw Exception::CancelEvent( "Another thread is pausing the VM state." );
if( m_ExecMode == ExecMode_Running ) if( m_ExecMode == ExecMode_Running )
m_ExecMode = ExecMode_Suspending; m_ExecMode = ExecMode_Suspending;
pxAssertDev( m_ExecMode == ExecMode_Suspending, "ExecMode should be nothing other than Suspended..." ); pxAssertDev( m_ExecMode == ExecMode_Suspending, "ExecMode should be nothing other than Suspending..." );
} }
m_SuspendEvent.Reset();
m_sem_event.Post();
if( isBlocking ) m_SuspendEvent.WaitGui();
}
void SysSuspendableThread::Pause()
{
if( IsSelf() || !IsRunning() ) return;
{
ScopedLock locker( m_lock_ExecMode );
if( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Paused) ) return;
if( m_ExecMode == ExecMode_Running )
m_ExecMode = ExecMode_Pausing;
pxAssertDev( m_ExecMode == ExecMode_Pausing, "ExecMode should be nothing other than Pausing..." );
}
m_SuspendEvent.Reset();
m_sem_event.Post(); m_sem_event.Post();
m_SuspendEvent.WaitGui(); m_SuspendEvent.WaitGui();
} }
@ -111,6 +138,7 @@ void SysSuspendableThread::Resume()
// fall through... // fall through...
case ExecMode_Suspending: case ExecMode_Suspending:
case ExecMode_Pausing:
// 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...
@ -120,8 +148,8 @@ void SysSuspendableThread::Resume()
} }
} }
pxAssertDev( m_ExecMode == ExecMode_Suspended, pxAssertDev( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Paused),
"SysSuspendableThread is not in a suspended/idle state? wtf!" ); "SysSuspendableThread is not in a suspended/paused state? wtf!" );
m_ExecMode = ExecMode_Running; m_ExecMode = ExecMode_Running;
m_ResumeProtection = true; m_ResumeProtection = true;
@ -148,7 +176,7 @@ void SysSuspendableThread::StateCheck( bool isCancelable )
// Shortcut for the common case, to avoid unnecessary Mutex locks: // Shortcut for the common case, to avoid unnecessary Mutex locks:
if( m_ExecMode == ExecMode_Running ) if( m_ExecMode == ExecMode_Running )
{ {
if( isCancelable ) pthread_testcancel(); if( isCancelable ) TestCancel();
return; return;
} }
@ -170,23 +198,41 @@ void SysSuspendableThread::StateCheck( bool isCancelable )
// Yup, need this a second time. Variable state could have changed while we // Yup, need this a second time. Variable state could have changed while we
// were trying to acquire the lock above. // were trying to acquire the lock above.
if( isCancelable ) if( isCancelable )
pthread_testcancel(); TestCancel();
break; break;
// -------------------------------------
case ExecMode_Pausing:
{
OnPauseInThread();
m_ExecMode = ExecMode_Paused;
m_SuspendEvent.Post();
}
// fallthrough...
case ExecMode_Paused:
m_lock_ExecMode.Unlock();
while( m_ExecMode == ExecMode_Paused )
m_ResumeEvent.WaitGui();
OnResumeInThread( false );
break;
// -------------------------------------
case ExecMode_Suspending: case ExecMode_Suspending:
{ {
OnSuspendInThread(); OnSuspendInThread();
m_ExecMode = ExecMode_Suspended; m_ExecMode = ExecMode_Suspended;
m_SuspendEvent.Post(); m_SuspendEvent.Post();
} }
// fall through... // fallthrough...
case ExecMode_Suspended: case ExecMode_Suspended:
m_lock_ExecMode.Unlock(); m_lock_ExecMode.Unlock();
while( m_ExecMode == ExecMode_Suspended ) while( m_ExecMode == ExecMode_Suspended )
m_ResumeEvent.WaitGui(); m_ResumeEvent.WaitGui();
OnResumeInThread(); OnResumeInThread( true );
break; break;
jNO_DEFAULT; jNO_DEFAULT;
@ -218,16 +264,6 @@ void SysCoreThread::Start()
_parent::Start(); _parent::Start();
} }
// Suspends the system without closing plugins or updating GUI status.
// Should be used for savestates or other actions which happen very quickly.
void SysCoreThread::ShortSuspend()
{
m_shortSuspend = true;
Suspend();
m_shortSuspend = false;
}
// 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.
@ -348,11 +384,6 @@ void SysCoreThread::CpuExecute()
PCSX2_MEM_PROTECT_END(); PCSX2_MEM_PROTECT_END();
} }
static void _cet_callback_cleanup( void* handle )
{
((SysCoreThread*)handle)->OnThreadCleanup();
}
void SysCoreThread::ExecuteTask() void SysCoreThread::ExecuteTask()
{ {
tls_coreThread = this; tls_coreThread = this;
@ -369,9 +400,10 @@ void SysCoreThread::OnSuspendInThread()
m_plugins.Close(); m_plugins.Close();
} }
void SysCoreThread::OnResumeInThread() void SysCoreThread::OnResumeInThread( bool isSuspended )
{ {
m_plugins.Open(); if( isSuspended )
m_plugins.Open();
} }

View File

@ -41,7 +41,9 @@ protected:
ExecMode_NoThreadYet, ExecMode_NoThreadYet,
ExecMode_Running, ExecMode_Running,
ExecMode_Suspending, ExecMode_Suspending,
ExecMode_Suspended ExecMode_Suspended,
ExecMode_Pausing,
ExecMode_Paused,
}; };
volatile ExecutionMode m_ExecMode; volatile ExecutionMode m_ExecMode;
@ -59,6 +61,7 @@ public:
virtual void Suspend( bool isBlocking = true ); virtual void Suspend( bool isBlocking = true );
virtual void Resume(); virtual void Resume();
virtual void Pause();
virtual void StateCheck( bool isCancelable = true ); virtual void StateCheck( bool isCancelable = true );
virtual void OnThreadCleanup(); virtual void OnThreadCleanup();
@ -78,11 +81,21 @@ 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
// handles invocation by the following guidelines: Called *in thread* from StateCheck()
// prior to pausing the thread (ie, when Pause() has been called on a separate thread,
// requesting this thread pause itself temporarily). After this is called, the thread
// enters a waiting state on the m_ResumeEvent semaphore.
virtual void OnPauseInThread()=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 from StateCheck() after the // handles invocation by the following guidelines: Called from StateCheck() after the
// thread has been suspended and then subsequently resumed. // thread has been suspended and then subsequently resumed.
virtual void OnResumeInThread()=0; // Parameter:
// isSuspended - set to TRUE if the thread is returning from a suspended state, or
// FALSE if it's returning from a paused state.
virtual void OnResumeInThread( bool isSuspended )=0;
}; };
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -107,7 +120,6 @@ public:
virtual void ApplySettings( const Pcsx2Config& src ); virtual void ApplySettings( const Pcsx2Config& src );
virtual void OnThreadCleanup(); virtual void OnThreadCleanup();
virtual void ShortSuspend();
virtual void OnResumeReady(); virtual void OnResumeReady();
protected: protected:
@ -116,6 +128,7 @@ protected:
virtual void Start(); virtual void Start();
virtual void OnSuspendInThread(); virtual void OnSuspendInThread();
virtual void OnResumeInThread(); virtual void OnPauseInThread() {}
virtual void OnResumeInThread( bool IsSuspended );
virtual void ExecuteTask(); virtual void ExecuteTask();
}; };

View File

@ -27,12 +27,6 @@
VUmicroCpu CpuVU0; // contains a working copy of the VU0 cpu functions/API VUmicroCpu CpuVU0; // contains a working copy of the VU0 cpu functions/API
VUmicroCpu CpuVU1; // contains a working copy of the VU1 cpu functions/API VUmicroCpu CpuVU1; // contains a working copy of the VU1 cpu functions/API
static void DummyExecuteVU1Block(void)
{
VU0.VI[ REG_VPU_STAT ].UL &= ~0x100;
VU1.vifRegs->stat.VEW = 0; // also reset the bit (grandia 3 works)
}
void vuMicroCpuReset() void vuMicroCpuReset()
{ {
CpuVU0 = CHECK_VU0REC ? recVU0 : intVU0; CpuVU0 = CHECK_VU0REC ? recVU0 : intVU0;

View File

@ -21,6 +21,8 @@
#include <wx/docview.h> #include <wx/docview.h>
#include <wx/apptrait.h> #include <wx/apptrait.h>
#include "Utilities/Listeners.h"
class IniInterface; class IniInterface;
class MainEmuFrame; class MainEmuFrame;
class GSFrame; class GSFrame;
@ -35,6 +37,9 @@ class AppCoreThread;
#include "System.h" #include "System.h"
#include "System/SysThreads.h" #include "System/SysThreads.h"
typedef void FnType_OnThreadComplete(const wxCommandEvent& evt);
#define AllowFromMainThreadOnly() \ #define AllowFromMainThreadOnly() \
pxAssertMsg( wxThread::IsMain(), "Thread affinity violation: Call allowed from main thread only." ) pxAssertMsg( wxThread::IsMain(), "Thread affinity violation: Call allowed from main thread only." )
@ -42,10 +47,10 @@ BEGIN_DECLARE_EVENT_TYPES()
DECLARE_EVENT_TYPE( pxEVT_SemaphorePing, -1 ) DECLARE_EVENT_TYPE( pxEVT_SemaphorePing, -1 )
DECLARE_EVENT_TYPE( pxEVT_OpenModalDialog, -1 ) DECLARE_EVENT_TYPE( pxEVT_OpenModalDialog, -1 )
DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 ) DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 )
DECLARE_EVENT_TYPE( pxEVT_SysExecute, -1 )
DECLARE_EVENT_TYPE( pxEVT_LoadPluginsComplete, -1 ) DECLARE_EVENT_TYPE( pxEVT_LoadPluginsComplete, -1 )
DECLARE_EVENT_TYPE( pxEVT_AppCoreThread_Terminated, -1 ) DECLARE_EVENT_TYPE( pxEVT_AppCoreThreadFinished, -1 )
DECLARE_EVENT_TYPE( pxEVT_FreezeFinished, -1 ) DECLARE_EVENT_TYPE( pxEVT_FreezeThreadFinished, -1 )
DECLARE_EVENT_TYPE( pxEVT_ThawFinished, -1 )
END_DECLARE_EVENT_TYPES() END_DECLARE_EVENT_TYPES()
// This is used when the GS plugin is handling its own window. Messages from the PAD // This is used when the GS plugin is handling its own window. Messages from the PAD
@ -335,8 +340,6 @@ public:
Pcsx2App(); Pcsx2App();
virtual ~Pcsx2App(); virtual ~Pcsx2App();
void ReloadPlugins();
void PostPadKey( wxKeyEvent& evt ); void PostPadKey( wxKeyEvent& evt );
void PostMenuAction( MenuIdentifiers menu_id ) const; void PostMenuAction( MenuIdentifiers menu_id ) const;
int ThreadedModalDialog( DialogIdentifiers dialogId ); int ThreadedModalDialog( DialogIdentifiers dialogId );
@ -392,6 +395,18 @@ public:
void DisableDiskLogging() const; void DisableDiskLogging() const;
void OnProgramLogClosed(); void OnProgramLogClosed();
// ----------------------------------------------------------------------------
// Event Sources!
// ----------------------------------------------------------------------------
protected:
CmdEvt_Source m_evtsrc_CorePluginStatus;
CmdEvt_Source m_evtsrc_CoreThreadStatus;
public:
CmdEvt_Source& Source_CoreThreadStatus() { return m_evtsrc_CoreThreadStatus; }
CmdEvt_Source& Source_CorePluginStatus() { return m_evtsrc_CorePluginStatus; }
protected: protected:
void InitDefaultGlobalAccelerators(); void InitDefaultGlobalAccelerators();
void BuildCommandHash(); void BuildCommandHash();
@ -402,18 +417,18 @@ protected:
void HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const; void HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const;
void OnSysExecute( wxCommandEvent& evt );
void OnReloadPlugins( wxCommandEvent& evt ); void OnReloadPlugins( wxCommandEvent& evt );
void OnLoadPluginsComplete( wxCommandEvent& evt ); void OnLoadPluginsComplete( wxCommandEvent& evt );
void OnSemaphorePing( wxCommandEvent& evt ); void OnSemaphorePing( wxCommandEvent& evt );
void OnOpenModalDialog( wxCommandEvent& evt ); void OnOpenModalDialog( wxCommandEvent& evt );
void OnCoreThreadTerminated( wxCommandEvent& evt ); void OnCoreThreadTerminated( wxCommandEvent& evt );
void OnFreezeFinished( wxCommandEvent& evt ); void OnFreezeThreadFinished( wxCommandEvent& evt );
void OnThawFinished( wxCommandEvent& evt );
void OnMessageBox( pxMessageBoxEvent& evt ); void OnMessageBox( pxMessageBoxEvent& evt );
void OnEmuKeyDown( wxKeyEvent& evt ); void OnEmuKeyDown( wxKeyEvent& evt );
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Override wx default exception handling behavior // Override wx default exception handling behavior
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -496,7 +511,7 @@ DECLARE_APP(Pcsx2App)
extern bool sys_resume_lock; extern bool sys_resume_lock;
extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest ); extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest );
extern void LoadPluginsPassive(); extern void LoadPluginsPassive( FnType_OnThreadComplete* onComplete );
extern void LoadPluginsImmediate(); extern void LoadPluginsImmediate();
extern void UnloadPlugins(); extern void UnloadPlugins();

View File

@ -72,7 +72,7 @@ void AppCoreThread::OnResumeReady()
// the new (lack of) thread status, so this posts a message to the App to do so. // the new (lack of) thread status, so this posts a message to the App to do so.
void AppCoreThread::OnThreadCleanup() void AppCoreThread::OnThreadCleanup()
{ {
wxCommandEvent evt( pxEVT_AppCoreThread_Terminated ); wxCommandEvent evt( pxEVT_AppCoreThreadFinished );
wxGetApp().AddPendingEvent( evt ); wxGetApp().AddPendingEvent( evt );
_parent::OnThreadCleanup(); _parent::OnThreadCleanup();
} }

View File

@ -220,12 +220,12 @@ bool Pcsx2App::OnInit()
Connect( pxEVT_SemaphorePing, wxCommandEventHandler( Pcsx2App::OnSemaphorePing ) ); Connect( pxEVT_SemaphorePing, wxCommandEventHandler( Pcsx2App::OnSemaphorePing ) );
Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) ); Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) );
Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) ); Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) );
Connect( pxEVT_SysExecute, wxCommandEventHandler( Pcsx2App::OnSysExecute ) );
Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) ); Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) );
Connect( pxEVT_FreezeFinished, wxCommandEventHandler( Pcsx2App::OnCoreThreadTerminated ) ); Connect( pxEVT_AppCoreThreadFinished, wxCommandEventHandler( Pcsx2App::OnCoreThreadTerminated ) );
Connect( pxEVT_FreezeThreadFinished, wxCommandEventHandler( Pcsx2App::OnFreezeThreadFinished ) );
Connect( pxEVT_FreezeFinished, wxCommandEventHandler( Pcsx2App::OnFreezeFinished ) );
Connect( pxEVT_ThawFinished, wxCommandEventHandler( Pcsx2App::OnThawFinished ) );
Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) ); Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) );
@ -310,7 +310,7 @@ bool Pcsx2App::OnInit()
return false; return false;
} }
LoadPluginsPassive(); LoadPluginsPassive( NULL );
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
catch( Exception::StartupAborted& ex ) catch( Exception::StartupAborted& ex )

View File

@ -30,10 +30,10 @@ IMPLEMENT_APP(Pcsx2App)
DEFINE_EVENT_TYPE( pxEVT_SemaphorePing ); DEFINE_EVENT_TYPE( pxEVT_SemaphorePing );
DEFINE_EVENT_TYPE( pxEVT_OpenModalDialog ); DEFINE_EVENT_TYPE( pxEVT_OpenModalDialog );
DEFINE_EVENT_TYPE( pxEVT_ReloadPlugins ); DEFINE_EVENT_TYPE( pxEVT_ReloadPlugins );
DEFINE_EVENT_TYPE( pxEVT_SysExecute );
DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete ); DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete );
DEFINE_EVENT_TYPE( pxEVT_AppCoreThread_Terminated ); DEFINE_EVENT_TYPE( pxEVT_AppCoreThreadFinished );
DEFINE_EVENT_TYPE( pxEVT_FreezeFinished ); DEFINE_EVENT_TYPE( pxEVT_FreezeThreadFinished );
DEFINE_EVENT_TYPE( pxEVT_ThawFinished );
bool UseAdminMode = false; bool UseAdminMode = false;
wxDirName SettingsFolder; wxDirName SettingsFolder;
@ -211,6 +211,18 @@ void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent&
(handler->*func)(event); (handler->*func)(event);
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
catch( Exception::CancelEvent& ex )
{
Console.Notice( ex.FormatDiagnosticMessage() );
}
// ----------------------------------------------------------------------------
catch( Exception::BadSavedState& ex)
{
// Saved state load failed.
Console.Notice( ex.FormatDiagnosticMessage() );
sCoreThread.Resume();
}
// ----------------------------------------------------------------------------
catch( Exception::PluginError& ex ) catch( Exception::PluginError& ex )
{ {
Console.Error( ex.FormatDiagnosticMessage() ); Console.Error( ex.FormatDiagnosticMessage() );
@ -376,32 +388,53 @@ void Pcsx2App::OnMainFrameClosed()
m_MainFrame = NULL; m_MainFrame = NULL;
} }
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// Sys/Core API and Shortcuts (for wxGetApp()) // Sys/Core API and Shortcuts (for wxGetApp())
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
static int _sysexec_cdvdsrc_type = -1;
static void OnSysExecuteAfterPlugins( const wxCommandEvent& loadevt )
{
if( !wxGetApp().m_CorePlugins ) return;
wxCommandEvent execevt( pxEVT_SysExecute );
execevt.SetInt( _sysexec_cdvdsrc_type );
wxGetApp().AddPendingEvent( execevt );
}
// Executes the emulator using a saved/existing virtual machine state and currently // Executes the emulator using a saved/existing virtual machine state and currently
// configured CDVD source device. // configured CDVD source device.
void Pcsx2App::SysExecute() void Pcsx2App::SysExecute()
{ {
if( sys_resume_lock ) if( !m_CorePlugins )
{ {
Console.WriteLn( "SysExecute: State is locked, ignoring Execute request!" ); LoadPluginsPassive( OnSysExecuteAfterPlugins );
return; return;
} }
SysReset(); wxCommandEvent evt( pxEVT_SysExecute );
LoadPluginsImmediate(); evt.SetInt( -1 );
m_CoreThread = new AppCoreThread( *m_CorePlugins ); AddPendingEvent( evt );
m_CoreThread->Resume();
} }
// Executes the specified cdvd source and optional elf file. This command performs a // Executes the specified cdvd source and optional elf file. This command performs a
// full closure of any existing VM state and starts a fresh VM with the requested // full closure of any existing VM state and starts a fresh VM with the requested
// sources. // sources.
void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc ) void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc )
{
if( !m_CorePlugins )
{
LoadPluginsPassive( OnSysExecuteAfterPlugins );
return;
}
wxCommandEvent evt( pxEVT_SysExecute );
evt.SetInt( (int)cdvdsrc );
AddPendingEvent( evt );
}
void Pcsx2App::OnSysExecute( wxCommandEvent& evt )
{ {
if( sys_resume_lock ) if( sys_resume_lock )
{ {
@ -409,10 +442,13 @@ void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc )
return; return;
} }
SysReset(); // if something unloaded plugins since this messages was queued then it's best to ignore
LoadPluginsImmediate(); // it, because apparently too much stuff is going on and the emulation states are wonky.
if( !m_CorePlugins ) return;
if( evt.GetInt() != -1 ) SysReset();
CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso ); CDVDsys_SetFile( CDVDsrc_Iso, g_Conf->CurrentIso );
CDVDsys_ChangeSource( cdvdsrc ); if( evt.GetInt() != -1 ) CDVDsys_ChangeSource( (CDVD_SourceType)evt.GetInt() );
m_CoreThread = new AppCoreThread( *m_CorePlugins ); m_CoreThread = new AppCoreThread( *m_CorePlugins );
m_CoreThread->Resume(); m_CoreThread->Resume();

View File

@ -109,7 +109,7 @@ void ConsoleTestThread::ExecuteTask()
// worst case scenario (without being entirely unrealistic). // worst case scenario (without being entirely unrealistic).
Console.WriteLn( wxsFormat( L"This is a threaded logging test. Something bad could happen... %d", ++numtrack ) ); Console.WriteLn( wxsFormat( L"This is a threaded logging test. Something bad could happen... %d", ++numtrack ) );
Console.Status( wxsFormat( L"Testing high stress loads %s", L"(multi-color)" ) ); Console.Status( wxsFormat( L"Testing high stress loads %s", L"(multi-color)" ) );
Sleep( 0 ); Yield( 0 );
} }
} }

View File

@ -51,7 +51,7 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
m_bitmap_dualshock( this, wxID_ANY, wxBitmap( EmbeddedImage<res_Dualshock>().Get() ), m_bitmap_dualshock( this, wxID_ANY, wxBitmap( EmbeddedImage<res_Dualshock>().Get() ),
wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN ) wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN )
{ {
static const wxString LabelAuthors = wxString::FromUTF8( static const wxString LabelAuthors = fromUTF8(
"Developers" "Developers"
"\n\n" "\n\n"
"v0.9.6+: Arcum42, Refraction," "v0.9.6+: Arcum42, Refraction,"
@ -69,7 +69,7 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
"Webmasters: CKemu, Falcon4ever" "Webmasters: CKemu, Falcon4ever"
); );
static const wxString LabelGreets = wxString::FromUTF8( static const wxString LabelGreets = fromUTF8(
"Contributors" "Contributors"
"\n\n" "\n\n"
"Hiryu and Sjeep (libcdvd / iso filesystem), nneeve (fpu and vu)" "Hiryu and Sjeep (libcdvd / iso filesystem), nneeve (fpu and vu)"

View File

@ -44,7 +44,7 @@ static const int IdealWidth = 500;
template< typename T > template< typename T >
void Dialogs::ConfigurationDialog::AddPage( const char* label, int iconid ) void Dialogs::ConfigurationDialog::AddPage( const char* label, int iconid )
{ {
const wxString labelstr( wxString::FromUTF8( label ) ); const wxString labelstr( fromUTF8( label ) );
const int curidx = m_labels.Add( labelstr ); const int curidx = m_labels.Add( labelstr );
g_ApplyState.SetCurrentPage( curidx ); g_ApplyState.SetCurrentPage( curidx );
m_listbook.AddPage( new T( m_listbook, IdealWidth ), wxGetTranslation( labelstr ), m_listbook.AddPage( new T( m_listbook, IdealWidth ), wxGetTranslation( labelstr ),

View File

@ -263,7 +263,7 @@ void AcceleratorDictionary::Map( const KeyAcceleratorCode& acode, const char *se
Console.Notice( wxsFormat( Console.Notice( wxsFormat(
L"Kbd Accelerator '%s' is mapped multiple times.\n" L"Kbd Accelerator '%s' is mapped multiple times.\n"
L"\t'Command %s' is being replaced by '%s'", L"\t'Command %s' is being replaced by '%s'",
acode.ToString().c_str(), wxString::FromUTF8( result->Id ).c_str(), searchfor ) acode.ToString().c_str(), fromUTF8( result->Id ).c_str(), searchfor )
); );
} }
@ -272,7 +272,7 @@ void AcceleratorDictionary::Map( const KeyAcceleratorCode& acode, const char *se
if( result == NULL ) if( result == NULL )
{ {
Console.Notice( wxsFormat( L"Kbd Accelerator '%s' is mapped to unknown command '%s'", Console.Notice( wxsFormat( L"Kbd Accelerator '%s' is mapped to unknown command '%s'",
acode.ToString().c_str(), wxString::FromUTF8( searchfor ).c_str() ) acode.ToString().c_str(), fromUTF8( searchfor ).c_str() )
); );
} }
else else

View File

@ -310,7 +310,7 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title):
{ {
// Odd versions: beta / development editions, which feature revision number and compile date. // Odd versions: beta / development editions, which feature revision number and compile date.
wintitle.Printf( _("PCSX2 %d.%d.%d.%d%s (svn) %s"), PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo, wintitle.Printf( _("PCSX2 %d.%d.%d.%d%s (svn) %s"), PCSX2_VersionHi, PCSX2_VersionMid, PCSX2_VersionLo,
SVN_REV, SVN_MODS ? L"m" : wxEmptyString, wxString::FromUTF8(__DATE__).c_str() ); SVN_REV, SVN_MODS ? L"m" : wxEmptyString, fromUTF8(__DATE__).c_str() );
} }
else else
{ {

View File

@ -559,13 +559,13 @@ void Panels::PluginSelectorPanel::EnumThread::ExecuteTask()
DevCon.Status( "Plugin Enumeration Thread started..." ); DevCon.Status( "Plugin Enumeration Thread started..." );
wxGetApp().Ping(); // gives the gui thread some time to refresh wxGetApp().Ping(); // gives the gui thread some time to refresh
Sleep( 3 ); Yield( 3 );
for( int curidx=0; curidx < m_master.FileCount(); ++curidx ) for( int curidx=0; curidx < m_master.FileCount(); ++curidx )
{ {
DoNextPlugin( curidx ); DoNextPlugin( curidx );
if( (curidx & 3) == 3 ) wxGetApp().Ping(); // gives the gui thread some time to refresh if( (curidx & 3) == 3 ) wxGetApp().Ping(); // gives the gui thread some time to refresh
pthread_testcancel(); TestCancel();
//Sleep(150); // uncomment this to slow down the selector, for debugging threading. //Sleep(150); // uncomment this to slow down the selector, for debugging threading.
} }

View File

@ -27,6 +27,8 @@
using namespace Threading; using namespace Threading;
static FnType_OnThreadComplete* Callback_PluginsLoadComplete = NULL;
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
// LoadPluginsTask // LoadPluginsTask
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -37,76 +39,52 @@ using namespace Threading;
// //
class LoadPluginsTask : public PersistentThread class LoadPluginsTask : public PersistentThread
{ {
typedef PersistentThread _parent;
public: public:
Exception::PluginError* Ex_PluginError;
Exception::RuntimeError* Ex_RuntimeError;
PluginManager* Result; PluginManager* Result;
protected: protected:
wxString m_folders[PluginId_Count]; wxString m_folders[PluginId_Count];
public: public:
LoadPluginsTask( const wxString (&folders)[PluginId_Count] ) : LoadPluginsTask( const wxString (&folders)[PluginId_Count] ) : Result( NULL )
Ex_PluginError( NULL )
, Ex_RuntimeError( NULL )
, Result( NULL )
{ {
for(int i=0; i<PluginId_Count; ++i ) for(int i=0; i<PluginId_Count; ++i )
m_folders[i] = folders[i]; m_folders[i] = folders[i];
Start();
} }
virtual ~LoadPluginsTask() throw(); virtual ~LoadPluginsTask() throw();
protected: protected:
void OnStart() {} void OnStart() {}
void OnThreadCleanup() {} void OnThreadCleanup();
void ExecuteTask(); void ExecuteTask();
}; };
static ScopedPtr<LoadPluginsTask> _loadTask;
LoadPluginsTask::~LoadPluginsTask() throw() LoadPluginsTask::~LoadPluginsTask() throw()
{ {
if( _loadTask )
_loadTask.DetachPtr(); // avoids recursive deletion
PersistentThread::Cancel(); PersistentThread::Cancel();
_loadTask = NULL;
} }
void LoadPluginsTask::ExecuteTask() void LoadPluginsTask::ExecuteTask()
{ {
wxGetApp().Ping(); wxGetApp().Ping();
Sleep(3); Yield(3);
// This is for testing of the error handler... uncomment for fun?
//throw Exception::PluginError( PluginId_PAD, "This one is for testing the error handler!" );
Result = PluginManager_Create( m_folders );
}
void LoadPluginsTask::OnThreadCleanup()
{
wxCommandEvent evt( pxEVT_LoadPluginsComplete ); wxCommandEvent evt( pxEVT_LoadPluginsComplete );
evt.SetClientData( this ); evt.SetClientData( this );
try
{
// This is for testing of the error handler... uncomment for fun?
//throw Exception::PluginError( PluginId_PAD, "This one is for testing the error handler!" );
Result = PluginManager_Create( m_folders );
}
catch( Exception::PluginError& ex )
{
Ex_PluginError = new Exception::PluginError( ex );
}
catch( Exception::RuntimeError& innerEx )
{
// Runtime errors are typically recoverable, so handle them here
// and prep them for re-throw on the main thread.
Ex_RuntimeError = new Exception::RuntimeError(
L"A runtime error occurred on the LoadPlugins thread.\n" + innerEx.FormatDiagnosticMessage(),
innerEx.FormatDisplayMessage()
);
}
// anything else leave unhandled so that the debugger catches it!
wxGetApp().AddPendingEvent( evt ); wxGetApp().AddPendingEvent( evt );
_parent::OnThreadCleanup();
} }
///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////
@ -132,30 +110,23 @@ int EnumeratePluginsInFolder( const wxDirName& searchpath, wxArrayString* dest )
wxDir::GetAllFiles( searchpath.ToString(), realdest, wxsFormat( pattern, wxDynamicLibrary::GetDllExt()), wxDIR_FILES ) : 0; wxDir::GetAllFiles( searchpath.ToString(), realdest, wxsFormat( pattern, wxDynamicLibrary::GetDllExt()), wxDIR_FILES ) : 0;
} }
void ConvertPluginFilenames( wxString (&passins)[PluginId_Count] )
{
const PluginInfo* pi = tbl_PluginInfo; do
{
passins[pi->id] = OverrideOptions.Filenames[pi->id].GetFullPath();
if( passins[pi->id].IsEmpty() || !wxFileExists( passins[pi->id] ) )
passins[pi->id] = g_Conf->FullpathTo( pi->id );
} while( ++pi, pi->shortname != NULL );
}
// boolean lock modified from the main thread only...
static bool plugin_load_lock = false;
void Pcsx2App::OnReloadPlugins( wxCommandEvent& evt ) void Pcsx2App::OnReloadPlugins( wxCommandEvent& evt )
{ {
ReloadPlugins(); if( plugin_load_lock ) return;
}
void Pcsx2App::OnLoadPluginsComplete( wxCommandEvent& evt )
{
// scoped ptr ensures the thread object is cleaned up even on exception:
ScopedPtr<LoadPluginsTask> killTask( (LoadPluginsTask*)evt.GetClientData() );
m_CorePlugins = killTask->Result;
if( !m_CorePlugins )
{
if( killTask->Ex_PluginError != NULL )
throw *killTask->Ex_PluginError;
if( killTask->Ex_RuntimeError != NULL )
throw *killTask->Ex_RuntimeError; // Re-Throws generic threaded errors
}
}
void Pcsx2App::ReloadPlugins()
{
if( _loadTask ) return;
m_CoreThread = NULL; m_CoreThread = NULL;
m_CorePlugins = NULL; m_CorePlugins = NULL;
@ -169,42 +140,79 @@ void Pcsx2App::ReloadPlugins()
passins[pi->id] = g_Conf->FullpathTo( pi->id ); passins[pi->id] = g_Conf->FullpathTo( pi->id );
} while( ++pi, pi->shortname != NULL ); } while( ++pi, pi->shortname != NULL );
_loadTask.Delete() = new LoadPluginsTask( passins ); (new LoadPluginsTask( passins ))->Start();
// ... and when it finishes it posts up a OnLoadPluginsComplete(). Bye. :) // ... and when it finishes it posts up a OnLoadPluginsComplete(). Bye. :)
plugin_load_lock = true;
}
// Note: If the ClientData paremeter of wxCommandEvt is NULL, this message simply dispatches
// the plugged in listeners.
void Pcsx2App::OnLoadPluginsComplete( wxCommandEvent& evt )
{
plugin_load_lock = false;
FnType_OnThreadComplete* fn_tmp = Callback_PluginsLoadComplete;
if( evt.GetClientData() != NULL )
{
if( !pxAssertDev( !m_CorePlugins, "LoadPlugins thread just finished, but CorePlugins state != NULL (odd!)." ) )
m_CorePlugins = NULL;
// scoped ptr ensures the thread object is cleaned up even on exception:
ScopedPtr<LoadPluginsTask> killTask( (LoadPluginsTask*)evt.GetClientData() );
killTask->RethrowException();
m_CorePlugins = killTask->Result;
}
if( fn_tmp != NULL ) fn_tmp( evt );
//m_evtsrc_PluginLoadFinished.Dispatch( evt );
} }
// Posts a message to the App to reload plugins. Plugins are loaded via a background thread // Posts a message to the App to reload plugins. Plugins are loaded via a background thread
// which is started on a pending event, so don't expect them to be ready "right now." // which is started on a pending event, so don't expect them to be ready "right now."
// If plugins are already loaded then no action is performed. // If plugins are already loaded, onComplete is invoked, and the function returns with no
void LoadPluginsPassive() // other actions performed.
void LoadPluginsPassive( FnType_OnThreadComplete* onComplete )
{ {
if( g_plugins ) return; // Plugins already loaded?
if( wxGetApp().m_CorePlugins )
{
if( onComplete ) onComplete( wxCommandEvent( pxEVT_LoadPluginsComplete ) );
return;
}
if( onComplete )
Callback_PluginsLoadComplete = onComplete;
wxCommandEvent evt( pxEVT_ReloadPlugins ); wxCommandEvent evt( pxEVT_ReloadPlugins );
wxGetApp().AddPendingEvent( evt ); wxGetApp().AddPendingEvent( evt );
} }
// Blocks until plugins have been successfully loaded, or throws an exception if // Performs a blocking load of plugins. If the emulation thread is active, it is shut down
// the user cancels the loading procedure after error. If plugins are already loaded // automatically to prevent race conditions (it depends on plugins).
// then no action is performed. //
// Exceptions regarding plugin failures will propagate out of this function, so be prepared
// to handle them.
//
// Note that this is not recommended for most situations, but coding improper passive loads
// is probably worse, so if in doubt use this and air will fix it up for you later. :)
//
void LoadPluginsImmediate() void LoadPluginsImmediate()
{ {
AllowFromMainThreadOnly(); if( g_plugins != NULL ) return;
if( g_plugins ) return;
static int _reentrant = 0; wxGetApp().m_CoreThread = NULL;
RecursionGuard guard( _reentrant );
wxString passins[PluginId_Count];
ConvertPluginFilenames( passins );
wxGetApp().m_CorePlugins = PluginManager_Create( passins );
pxAssertMsg( !guard.IsReentrant(), "Recrsive calls to this function are prohibited." );
wxGetApp().ReloadPlugins();
while( _loadTask )
{
Sleep( 10 );
wxGetApp().ProcessPendingEvents();
}
} }
void UnloadPlugins() void UnloadPlugins()
{ {
wxGetApp().m_CoreThread = NULL;
wxGetApp().m_CorePlugins = NULL; wxGetApp().m_CorePlugins = NULL;
} }

View File

@ -23,59 +23,10 @@
StartupParams g_Startup; StartupParams g_Startup;
// returns true if the new state was loaded, or false if nothing happened.
void States_Load( const wxString& file )
{
sCoreThread.ShortSuspend();
try
{
StateCopy_LoadFromFile( file );
//SysLoadState( file );
//SysStatus( wxsFormat( _("Loaded State %s"), wxFileName( file ).GetFullName().c_str() ) );
}
catch( Exception::BaseException& )
{
// VM state is probably ruined. We'll need to recover from the in-memory backup.
//StateRecovery::Recover();
}
sApp.SysExecute();
}
// Save state save-to-file (or slot) helpers. // Save state save-to-file (or slot) helpers.
void States_Save( const wxString& file ) void States_Save( const wxString& file )
{ {
if( !SysHasValidState() ) StateCopy_SaveToFile( file );
{
Msgbox::Alert( _("You need to start emulation first before you can save it's state.") );
return;
}
try
{
Console.Status( wxsFormat( L"Saving savestate to file: %s", file.c_str() ) );
StateCopy_SaveToFile( file );
SysStatus( wxsFormat( _("State saved to file: %s"), wxFileName( file ).GetFullName().c_str() ) );
}
catch( Exception::BaseException& ex )
{
// TODO: Implement a "pause the action and issue a popup" thing here.
// *OR* some kind of GS overlay... [for now we use the console]
// Translation Tip: "Your emulation state has not been saved!"
/*Msgbox::Alert(
wxsFormat( _("Error saving state to file: %s"), file.c_str() ) +
L"\n\n" + _("Error details:") + ex.DisplayMessage()
);*/
Console.Error( wxsFormat(
L"An error occurred while trying to save to file %s\n", file.c_str() ) +
L"Your emulation state has not been saved!\n"
L"\nError: " + ex.FormatDiagnosticMessage()
);
}
} }
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------
@ -95,22 +46,12 @@ bool States_isSlotUsed(int num)
void States_FreezeCurrentSlot() void States_FreezeCurrentSlot()
{ {
Console.Status( "Saving savestate to slot %d...", StatesC ); StateCopy_SaveToSlot( StatesC );
States_Save( SaveStateBase::GetFilename( StatesC ) );
} }
void States_DefrostCurrentSlot() void States_DefrostCurrentSlot()
{ {
wxString file( SaveStateBase::GetFilename( StatesC ) ); StateCopy_LoadFromSlot( StatesC );
if( !wxFileExists( file ) )
{
Console.Notice( "Savestate slot %d is empty.", StatesC );
return;
}
Console.Status( "Loading savestate from slot %d...", StatesC );
States_Load( file );
//SysStatus( wxsFormat( _("Loaded State (slot %d)"), StatesC ) ); //SysStatus( wxsFormat( _("Loaded State (slot %d)"), StatesC ) );
} }

View File

@ -1804,6 +1804,10 @@
RelativePath="..\..\gui\IniInterface.cpp" RelativePath="..\..\gui\IniInterface.cpp"
> >
</File> </File>
<File
RelativePath="..\..\..\common\include\Utilities\Listeners.h"
>
</File>
<File <File
RelativePath="..\..\gui\MainFrame.cpp" RelativePath="..\..\gui\MainFrame.cpp"
> >

View File

@ -142,6 +142,9 @@ static __forceinline bool ReadPipe(HANDLE h_Pipe, ConsoleColors color )
return true; return true;
} }
// --------------------------------------------------------------------------------------
// WinPipeThread
// --------------------------------------------------------------------------------------
class WinPipeThread : public PersistentThread class WinPipeThread : public PersistentThread
{ {
typedef PersistentThread _parent; typedef PersistentThread _parent;
@ -173,8 +176,7 @@ protected:
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL ); SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL );
while( true ) while( true )
{ {
Sleep( 100 ); Yield( 100 );
pthread_testcancel();
ReadPipe( m_outpipe, m_color ); ReadPipe( m_outpipe, m_color );
} }
} }
@ -189,6 +191,9 @@ protected:
void OnThreadCleanup() { } void OnThreadCleanup() { }
}; };
// --------------------------------------------------------------------------------------
// WinPipeRedirection
// --------------------------------------------------------------------------------------
class WinPipeRedirection : public PipeRedirectionBase class WinPipeRedirection : public PipeRedirectionBase
{ {
DeclareNoncopyableObject( WinPipeRedirection ); DeclareNoncopyableObject( WinPipeRedirection );
@ -221,7 +226,8 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) :
DWORD stdhandle = ( stdstream == stderr ) ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE; DWORD stdhandle = ( stdstream == stderr ) ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE;
CreatePipe( m_pipe, m_file ); CreatePipe( m_pipe, m_file );
SetStdHandle( stdhandle, m_file ); if( 0 == SetStdHandle( stdhandle, m_file ) )
throw Exception::Win32Error( "SetStdHandle failed." );
// In some cases GetStdHandle can fail, even when the one we just assigned above is valid. // In some cases GetStdHandle can fail, even when the one we just assigned above is valid.
HANDLE newhandle = GetStdHandle(stdhandle); HANDLE newhandle = GetStdHandle(stdhandle);
@ -231,11 +237,11 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) :
if( newhandle == NULL ) if( newhandle == NULL )
throw Exception::RuntimeError( "GetStdHandle returned NULL." ); // not a Win32error (no error code) throw Exception::RuntimeError( "GetStdHandle returned NULL." ); // not a Win32error (no error code)
m_crtFile = _open_osfhandle( (intptr_t)newhandle, _O_TEXT ); m_crtFile = _open_osfhandle( (intptr_t)newhandle, _O_TEXT );
if( m_crtFile == -1 ) if( m_crtFile == -1 )
throw Exception::RuntimeError( "_open_osfhandle returned -1." ); throw Exception::RuntimeError( "_open_osfhandle returned -1." );
m_fp = _fdopen( m_crtFile, "w" ); m_fp = _fdopen( m_crtFile, "w" );
if( m_fp == NULL ) if( m_fp == NULL )
throw Exception::RuntimeError( "_fdopen returned NULL." ); throw Exception::RuntimeError( "_fdopen returned NULL." );
@ -269,10 +275,22 @@ void WinPipeRedirection::Cleanup() throw()
{ {
fclose( m_fp ); fclose( m_fp );
m_fp = NULL; m_fp = NULL;
m_crtFile = -1; // crtFile is closed implicitly when closing m_fp
m_file = INVALID_HANDLE_VALUE; // m_file is closed implicitly when closing crtFile
}
if( m_crtFile != -1 )
{
_close( m_crtFile );
m_crtFile = -1; // m_file is closed implicitly when closing crtFile
} }
// crtFile is closed implicitly when closing m_fp if( m_file != INVALID_HANDLE_VALUE )
// m_file is closed implicitly when closing crtFile {
CloseHandle( m_pipe );
m_file = INVALID_HANDLE_VALUE;
}
if( m_pipe != INVALID_HANDLE_VALUE ) if( m_pipe != INVALID_HANDLE_VALUE )
{ {

View File

@ -30,7 +30,6 @@ int mVUdebugNow = 0;
#ifdef DEBUG_COMPARE #ifdef DEBUG_COMPARE
#include <windows.h>
static int runAmount = 0; static int runAmount = 0;
void VUtestPause() { void VUtestPause() {
@ -70,8 +69,9 @@ void VUtestPause() {
SysPrintf("VU Mem CRC = 0x%08x\n", j); SysPrintf("VU Mem CRC = 0x%08x\n", j);
SysPrintf("EndPC = 0x%04x\n", VU1.VI[REG_TPC].UL); SysPrintf("EndPC = 0x%04x\n", VU1.VI[REG_TPC].UL);
// ... wtf?? --air
for (int i = 0; i < 10000000; i++) { for (int i = 0; i < 10000000; i++) {
Sleep(1000); Threading::Sleep(1000);
} }
} }
#else #else
@ -84,9 +84,6 @@ extern u32 vudump;
#ifdef DEBUG_COMPARE2 #ifdef DEBUG_COMPARE2
#ifndef DEBUG_COMPARE
#include <windows.h>
#endif
__aligned16 u8 backVUregs[sizeof(VURegs)]; __aligned16 u8 backVUregs[sizeof(VURegs)];
__aligned16 u8 cmpVUregs [sizeof(VURegs)]; __aligned16 u8 cmpVUregs [sizeof(VURegs)];

View File

@ -238,20 +238,6 @@ static __forceinline s32 __fastcall GetNextDataBuffered( V_Core& thiscore, uint
g_counter_cache_misses++; g_counter_cache_misses++;
} }
s16* sbuffer = cacheLine.Sampledata;
//if( vc.LoopFlags & XAFLAG_LOOP )
// vc.Prev1 = vc.Prev2 = 0;
// saturated decoder
//XA_decode_block( sbuffer, memptr, vc.Prev1, vc.Prev2 );
// [Air]: Testing use of a new unsaturated decoder. (benchmark needed)
// Chances are the saturation isn't needed, but for a very few exception games.
// This is definitely faster than the above version, but is it by enough to
// merit possible lower compatibility? Especially now that games that make
// heavy use of the SPU2 via music or sfx will mostly use the cache anyway.
XA_decode_block_unsaturated( vc.SBuffer, memptr, vc.Prev1, vc.Prev2 ); XA_decode_block_unsaturated( vc.SBuffer, memptr, vc.Prev1, vc.Prev2 );
} }

View File

@ -63,8 +63,6 @@ StereoOut32 V_Core::ReadInput_HiFi()
AdmaInProgress = 0; AdmaInProgress = 0;
if(InputDataLeft >= 0x200) if(InputDataLeft >= 0x200)
{ {
u8 k = (InputDataLeft >= InputDataProgress);
#ifdef PCM24_S1_INTERLEAVE #ifdef PCM24_S1_INTERLEAVE
AutoDMAReadBuffer(1); AutoDMAReadBuffer(1);
#else #else

View File

@ -837,7 +837,7 @@ static void __fastcall RegWrite_Core( u16 value )
{ {
bool irqe = thiscore.IRQEnable; bool irqe = thiscore.IRQEnable;
int bit0 = thiscore.AttrBit0; int bit0 = thiscore.AttrBit0;
int bit4 = thiscore.AttrBit4; //int bit4 = thiscore.AttrBit4;
if( ((value>>15)&1) && (!thiscore.CoreEnabled) && (thiscore.InitDelay==0) ) // on init/reset if( ((value>>15)&1) && (!thiscore.CoreEnabled) && (thiscore.InitDelay==0) ) // on init/reset
{ {