mirror of https://github.com/PCSX2/pcsx2.git
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:
parent
a2a495e842
commit
eda5d1d834
|
@ -429,6 +429,10 @@
|
|||
RelativePath="..\..\include\intrin_x86.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\include\Utilities\Listeners.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\include\Utilities\lnx_memzero.h"
|
||||
>
|
||||
|
|
|
@ -191,7 +191,7 @@ typedef struct _PS2E_SessionInfo
|
|||
// 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
|
||||
// will be a zero length string).
|
||||
const char Serial[16];
|
||||
char Serial[16];
|
||||
|
||||
} PS2E_SessionInfo;
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
#if defined(PCSX2_DEBUG)
|
||||
|
||||
# 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)
|
||||
|
||||
|
@ -57,26 +57,28 @@
|
|||
// Devel builds use __assume for standard assertions and call pxOnAssertDevel
|
||||
// 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)) || \
|
||||
(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)
|
||||
|
||||
#else
|
||||
|
||||
// Release Builds just use __assume as an optimization, and always return 'true'
|
||||
// indicating the assertion check succeeded (no actual check is performed).
|
||||
// Release Builds just use __assume as an optimization, and return the conditional
|
||||
// as a result (if .
|
||||
|
||||
# define pxAssertMsg(cond, msg) (__assume(cond), true)
|
||||
# define pxAssertDev(cond, msg) (__assume(cond), true)
|
||||
# define pxFail(msg) (__assume(false), true)
|
||||
# define pxFailDev(msg) (__assume(false), true)
|
||||
# define pxAssertMsg(cond, msg) (__assume(cond), likely(cond))
|
||||
# define pxAssertDev(cond, msg) (__assume(cond), likely(cond))
|
||||
# define pxFail(msg) (__assume(false), false)
|
||||
# define pxFailDev(msg) (__assume(false), false)
|
||||
|
||||
#endif
|
||||
|
||||
__cold
|
||||
|
||||
#define pxAssert(cond) pxAssertMsg(cond, (wxChar*)NULL)
|
||||
|
||||
extern void pxOnAssert( const wxChar* file, int line, const char* func, const wxChar* cond, const wxChar* msg);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "Dependencies.h"
|
||||
#include "StringHelpers.h"
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// DESTRUCTOR_CATCHALL - safe destructor helper
|
||||
|
@ -133,7 +134,7 @@ namespace Exception
|
|||
//
|
||||
#define DEFINE_EXCEPTION_COPYTORS( classname ) \
|
||||
virtual ~classname() throw() {} \
|
||||
virtual void Rethrow() const { throw classname( *this ); } \
|
||||
virtual void Rethrow() const { throw *this; } \
|
||||
virtual BaseException* Clone() const { return new classname( *this ); }
|
||||
|
||||
#define DEFINE_RUNTIME_EXCEPTION( classname, defmsg ) \
|
||||
|
@ -149,11 +150,13 @@ namespace Exception
|
|||
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
|
||||
{
|
||||
public:
|
||||
bool IsSilent;
|
||||
public:
|
||||
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.") )
|
||||
};
|
||||
|
||||
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:
|
||||
wxString ObjectName;
|
||||
|
@ -175,17 +209,16 @@ namespace Exception
|
|||
|
||||
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...
|
||||
}
|
||||
|
||||
|
||||
virtual wxString FormatDisplayMessage() const;
|
||||
virtual wxString FormatDiagnosticMessage() const;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// OutOfMemory / InvalidOperation / InvalidArgument / IndexBoundsFault / ParseError
|
||||
// OutOfMemory / InvalidOperation / InvalidArgument / IndexBoundsFault / ParseError
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
||||
class OutOfMemory : public virtual RuntimeError
|
||||
|
@ -282,17 +315,17 @@ namespace Exception
|
|||
explicit classname( const char* objname, const char* msg=defmsg ) \
|
||||
{ \
|
||||
BaseException::InitBaseEx( msg ); \
|
||||
StreamName = wxString::FromUTF8( objname ); \
|
||||
StreamName = fromUTF8( objname ); \
|
||||
} \
|
||||
explicit classname( const char* objname, const wxString& msg_eng, const wxString& msg_xlt ) \
|
||||
{ \
|
||||
BaseException::InitBaseEx( msg_eng, msg_xlt ); \
|
||||
StreamName = wxString::FromUTF8( objname ); \
|
||||
StreamName = fromUTF8( objname ); \
|
||||
} \
|
||||
explicit classname( const char* objname, const wxString& msg_eng ) \
|
||||
{ \
|
||||
BaseException::InitBaseEx( msg_eng, msg_eng ); \
|
||||
StreamName = wxString::FromUTF8( objname ); \
|
||||
StreamName = fromUTF8( objname ); \
|
||||
} \
|
||||
explicit classname( const wxString& objname, const wxString& msg_eng ) \
|
||||
{ \
|
||||
|
|
|
@ -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 ) );
|
||||
}
|
||||
|
|
@ -15,6 +15,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#ifdef __WXMSW__
|
||||
# include <wx/msw/wrapwin.h>
|
||||
#else
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Windows Redtape! No windows.h should be included without it!
|
||||
//
|
||||
|
@ -40,3 +44,4 @@
|
|||
#undef max
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -15,25 +15,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <semaphore.h>
|
||||
#include <errno.h> // EBUSY
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
|
||||
#include "Pcsx2Defs.h"
|
||||
#include "ScopedPtr.h"
|
||||
|
||||
namespace Exception
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
#undef Yield // release th burden of windows.h global namespace spam.
|
||||
class wxTimeSpan;
|
||||
|
||||
namespace Threading
|
||||
|
@ -57,6 +46,38 @@ namespace Threading
|
|||
};
|
||||
#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
|
||||
{
|
||||
sem_t sema;
|
||||
|
@ -166,7 +187,7 @@ namespace Threading
|
|||
pthread_t m_thread;
|
||||
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
|
||||
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_running; // set true by Start(), and set false by Cancel(), Block(), etc.
|
||||
|
@ -190,27 +211,49 @@ namespace Threading
|
|||
bool IsSelf() const;
|
||||
wxString GetName() const;
|
||||
|
||||
void _ThreadCleanup();
|
||||
|
||||
protected:
|
||||
|
||||
// Extending classes should always implement your own OnStart(), which is called by
|
||||
// Start() once necessary locks have been obtained. Do not override Start() directly
|
||||
// unless you're really sure that's what you need to do. ;)
|
||||
virtual void OnStart()=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!
|
||||
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 );
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
|
||||
#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.
|
||||
// For dynamic length clears, use the C-compiler provided memset instead.
|
||||
|
||||
|
|
|
@ -227,14 +227,14 @@ void IConsoleWriter::_Write( const char* fmt, va_list args ) const
|
|||
{
|
||||
std::string m_format_buffer;
|
||||
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
|
||||
{
|
||||
std::string m_format_buffer;
|
||||
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
|
||||
|
|
|
@ -116,11 +116,23 @@ wxString Exception::BaseException::FormatDiagnosticMessage() const
|
|||
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
|
||||
{
|
||||
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_stacktrace;
|
||||
}
|
||||
|
@ -128,7 +140,7 @@ wxString Exception::ObjectIsNull::FormatDiagnosticMessage() const
|
|||
wxString Exception::ObjectIsNull::FormatDisplayMessage() const
|
||||
{
|
||||
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()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,24 +15,30 @@
|
|||
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "Threading.h"
|
||||
#include "wxBaseTools.h"
|
||||
|
||||
#include <wx/datetime.h>
|
||||
#include <wx/thread.h>
|
||||
#ifdef _WIN32
|
||||
# include <wx/msw/wrapwin.h> // for thread renaming features
|
||||
#endif
|
||||
#include <wx/app.h>
|
||||
|
||||
#ifdef __LINUX__
|
||||
# include <signal.h> // for pthread_kill, which is in pthread.h on w32-pthreads
|
||||
#endif
|
||||
|
||||
#include "Threading.h"
|
||||
#include "wxBaseTools.h"
|
||||
|
||||
#include <wx/datetime.h>
|
||||
#include <wx/thread.h>
|
||||
|
||||
|
||||
using namespace Threading;
|
||||
|
||||
namespace Threading
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
@ -59,11 +65,11 @@ namespace Threading
|
|||
{
|
||||
try
|
||||
{
|
||||
wxString logfix = L"Thread Destructor for " + m_name;
|
||||
Console.WriteLn( L"Thread Log: Executing destructor for " + m_name );
|
||||
|
||||
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
|
||||
m_sem_finished.WaitGui();
|
||||
#else
|
||||
|
@ -73,9 +79,7 @@ namespace Threading
|
|||
// it gets destroyed, otherwise th mutex handle would become invalid.
|
||||
ScopedLock locker( m_lock_start );
|
||||
}
|
||||
else
|
||||
Console.WriteLn( logfix + L": thread not running.");
|
||||
Sleep( 1 );
|
||||
Threading::Sleep( 1 );
|
||||
Detach();
|
||||
}
|
||||
DESTRUCTOR_CATCHALL
|
||||
|
@ -189,47 +193,22 @@ namespace Threading
|
|||
m_except->Rethrow();
|
||||
}
|
||||
|
||||
// invoked internally when canceling or exiting the thread. Extending classes should implement
|
||||
// OnThreadCleanup() to extend clenup functionality.
|
||||
void PersistentThread::_ThreadCleanup()
|
||||
void PersistentThread::TestCancel()
|
||||
{
|
||||
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
|
||||
// derrived class is implemented.
|
||||
ScopedLock startlock( m_lock_start );
|
||||
|
||||
OnThreadCleanup();
|
||||
|
||||
m_running = false;
|
||||
m_sem_finished.Post();
|
||||
pxAssert( IsSelf() );
|
||||
pthread_testcancel();
|
||||
}
|
||||
|
||||
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 {
|
||||
ExecuteTask();
|
||||
}
|
||||
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();
|
||||
(this->*method)();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Neat repackaging for STL Runtime errors...
|
||||
//
|
||||
catch( std::runtime_error& ex )
|
||||
{
|
||||
m_except = new Exception::RuntimeError(
|
||||
|
@ -244,16 +223,79 @@ namespace Threading
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
catch( Exception::RuntimeError& ex )
|
||||
{
|
||||
m_except = ex.Clone();
|
||||
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::OnThreadCleanup() {}
|
||||
|
||||
// passed into pthread_create, and is used to dispatch the thread's object oriented
|
||||
// callback function
|
||||
void* PersistentThread::_internal_callback( void* itsme )
|
||||
{
|
||||
jASSUME( itsme != NULL );
|
||||
|
@ -265,12 +307,12 @@ namespace Threading
|
|||
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.
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include "x86emitter/tools.h"
|
||||
#include "Threading.h"
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "implement.h" // win32 pthreads implementations.
|
||||
#endif
|
||||
|
|
|
@ -15,11 +15,13 @@
|
|||
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
#include "internal.h"
|
||||
#include "tools.h"
|
||||
#include "Utilities/RedtapeWindows.h"
|
||||
#include "Utilities/Threading.h"
|
||||
|
||||
#include "internal.h"
|
||||
#include "tools.h"
|
||||
|
||||
|
||||
using namespace x86Emitter;
|
||||
|
||||
__aligned16 x86CPU_INFO x86caps;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
Name="PcsxBaseProperties"
|
||||
OutputDirectory="$(SolutionDir)\bin\$(PcsxSubsection)"
|
||||
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
|
||||
Name="VCCLCompilerTool"
|
||||
|
@ -18,6 +18,7 @@
|
|||
WarningLevel="3"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="0"
|
||||
DisableSpecificWarnings="4063;4100"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
WarningLevel="3"
|
||||
DebugInformationFormat="3"
|
||||
CompileAs="0"
|
||||
DisableSpecificWarnings="4063;4100"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#define ENABLE_TIMESTAMPS
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
# include <wx/msw/wrapwin.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
#include "IsoFileTools.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
# include <wx/msw/wrapwin.h>
|
||||
|
||||
|
||||
void *_openfile(const char *filename, int flags)
|
||||
{
|
||||
|
|
|
@ -102,7 +102,6 @@ protected:
|
|||
int m_CopyCommandTally;
|
||||
int m_CopyDataTally;
|
||||
volatile bool m_RingBufferIsBusy;
|
||||
volatile bool m_LoadState;
|
||||
|
||||
// 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
|
||||
|
@ -153,7 +152,8 @@ public:
|
|||
protected:
|
||||
void OpenPlugin();
|
||||
void OnSuspendInThread();
|
||||
void OnResumeInThread();
|
||||
void OnPauseInThread() {}
|
||||
void OnResumeInThread( bool IsSuspended );
|
||||
|
||||
void OnResumeReady();
|
||||
|
||||
|
|
|
@ -206,7 +206,7 @@ struct GIFregisters
|
|||
u32 padding[3];
|
||||
tGIF_MODE mode;
|
||||
u32 padding1[3];
|
||||
tGIF_STAT stat;
|
||||
tGIF_STAT stat;
|
||||
u32 padding2[7];
|
||||
|
||||
tGIF_TAG0 tag0;
|
||||
|
@ -218,7 +218,7 @@ struct GIFregisters
|
|||
u32 tag3;
|
||||
u32 padding6[3];
|
||||
|
||||
tGIF_CNT cnt;
|
||||
tGIF_CNT cnt;
|
||||
u32 padding7[3];
|
||||
tGIF_P3CNT p3cnt;
|
||||
u32 padding8[3];
|
||||
|
|
|
@ -54,7 +54,6 @@ public:
|
|||
|
||||
extern StartupParams g_Startup;
|
||||
|
||||
extern void States_Load( const wxString& file );
|
||||
extern void States_Save( const wxString& file );
|
||||
extern bool States_isSlotUsed(int num);
|
||||
|
||||
|
|
|
@ -99,7 +99,6 @@ mtgsThreadObject::mtgsThreadObject() :
|
|||
, m_CopyCommandTally( 0 )
|
||||
, m_CopyDataTally( 0 )
|
||||
, m_RingBufferIsBusy( false )
|
||||
, m_LoadState( false )
|
||||
, m_QueuedFrames( 0 )
|
||||
, m_lock_FrameQueueCounter()
|
||||
, m_packet_size( 0 )
|
||||
|
@ -127,7 +126,6 @@ void mtgsThreadObject::OnStart()
|
|||
m_WritePos = 0;
|
||||
|
||||
m_RingBufferIsBusy = false;
|
||||
m_LoadState = false;
|
||||
|
||||
m_QueuedFrames = 0;
|
||||
m_packet_size = 0;
|
||||
|
@ -435,9 +433,9 @@ void mtgsThreadObject::OnSuspendInThread()
|
|||
_clean_close_gs( NULL );
|
||||
}
|
||||
|
||||
void mtgsThreadObject::OnResumeInThread()
|
||||
void mtgsThreadObject::OnResumeInThread( bool isSuspended )
|
||||
{
|
||||
if( !m_LoadState )
|
||||
if( isSuspended )
|
||||
OpenPlugin();
|
||||
}
|
||||
|
||||
|
@ -812,7 +810,6 @@ void mtgsThreadObject::Freeze( int mode, MTGS_FreezeData& data )
|
|||
{
|
||||
AtomicExchange( m_RingPos, m_WritePos );
|
||||
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
|
||||
m_LoadState = true;
|
||||
SetEvent();
|
||||
Resume();
|
||||
}
|
||||
|
@ -820,7 +817,6 @@ void mtgsThreadObject::Freeze( int mode, MTGS_FreezeData& data )
|
|||
SendPointerPacket( GS_RINGTYPE_FREEZE, mode, &data );
|
||||
|
||||
mtgsWaitGS();
|
||||
m_LoadState = false;
|
||||
}
|
||||
|
||||
// Waits for the GS to empty out the entire ring buffer contents.
|
||||
|
|
|
@ -90,7 +90,7 @@ struct LegacyApi_CommonMethod
|
|||
// returns the method name as a wxString, converted from UTF8.
|
||||
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.
|
||||
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.
|
||||
|
||||
// returns the method name as a wxString, converted from UTF8.
|
||||
wxString GetMethodName() const { return wxString::FromUTF8( MethodName ); }
|
||||
wxString GetMethodName() const { return fromUTF8( MethodName ); }
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -32,10 +32,21 @@ struct PluginInfo
|
|||
|
||||
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
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -139,9 +150,12 @@ namespace Exception
|
|||
virtual wxString FormatDiagnosticMessage() const;
|
||||
virtual wxString FormatDisplayMessage() const;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// LegacyPluginAPI_Common
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace R5900Exception
|
|||
|
||||
void Init( const char*msg )
|
||||
{
|
||||
m_message = wxString::FromUTF8( msg );
|
||||
m_message = fromUTF8( msg );
|
||||
cpuState = cpuRegs;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -13,57 +13,98 @@
|
|||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "App.h"
|
||||
#include "HostGui.h"
|
||||
|
||||
#include "zlib/zlib.h"
|
||||
|
||||
|
||||
static SafeArray<u8> state_buffer;
|
||||
|
||||
// Simple lock boolean for the state buffer in use by a thread. This simple solution works because
|
||||
// we are assured that state save/load actions will only be initiated from the main thread.
|
||||
static bool state_buffer_lock = false;
|
||||
// Simple lock boolean for the state buffer being in use by a thread.
|
||||
static NonblockingMutex state_buffer_lock;
|
||||
|
||||
// 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
|
||||
// form the main thread.
|
||||
bool sys_resume_lock = false;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// StateThread_Freeze
|
||||
// --------------------------------------------------------------------------------------
|
||||
class StateThread_Freeze : public PersistentThread
|
||||
static FnType_OnThreadComplete* Callback_FreezeFinished = NULL;
|
||||
|
||||
enum
|
||||
{
|
||||
StateThreadAction_None = 0,
|
||||
StateThreadAction_Create,
|
||||
StateThreadAction_Restore,
|
||||
StateThreadAction_ZipToDisk,
|
||||
StateThreadAction_UnzipFromDisk,
|
||||
};
|
||||
|
||||
class _BaseStateThread : public PersistentThread
|
||||
{
|
||||
typedef PersistentThread _parent;
|
||||
|
||||
public:
|
||||
StateThread_Freeze( const wxString& file )
|
||||
virtual ~_BaseStateThread() throw()
|
||||
{
|
||||
m_name = L"SaveState::Freeze";
|
||||
|
||||
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;
|
||||
state_buffer_lock.Release(); // just in case;
|
||||
}
|
||||
|
||||
|
||||
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()
|
||||
{
|
||||
memSavingState( state_buffer ).FreezeAll();
|
||||
}
|
||||
|
||||
|
||||
void OnThreadCleanup()
|
||||
{
|
||||
wxCommandEvent evt( pxEVT_FreezeFinished );
|
||||
evt.SetClientData( this );
|
||||
wxGetApp().AddPendingEvent( evt );
|
||||
|
||||
SendFinishEvent( StateThreadAction_Create );
|
||||
_parent::OnThreadCleanup();
|
||||
}
|
||||
};
|
||||
|
@ -71,37 +112,36 @@ protected:
|
|||
// --------------------------------------------------------------------------------------
|
||||
// StateThread_Thaw
|
||||
// --------------------------------------------------------------------------------------
|
||||
class StateThread_Thaw : public PersistentThread
|
||||
class StateThread_Thaw : public _BaseStateThread
|
||||
{
|
||||
typedef PersistentThread _parent;
|
||||
typedef _BaseStateThread _parent;
|
||||
|
||||
public:
|
||||
StateThread_Thaw( const wxString& file )
|
||||
{
|
||||
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;
|
||||
}
|
||||
StateThread_Thaw( FnType_OnThreadComplete* onFinished ) : _BaseStateThread( "Thaw", onFinished ) { }
|
||||
|
||||
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()
|
||||
{
|
||||
memSavingState( state_buffer ).FreezeAll();
|
||||
memLoadingState( state_buffer ).FreezeAll();
|
||||
}
|
||||
|
||||
void OnThreadCleanup()
|
||||
{
|
||||
wxCommandEvent evt( pxEVT_FreezeFinished );
|
||||
evt.SetClientData( this );
|
||||
wxGetApp().AddPendingEvent( evt );
|
||||
|
||||
SendFinishEvent( StateThreadAction_Restore );
|
||||
_parent::OnThreadCleanup();
|
||||
}
|
||||
};
|
||||
|
@ -109,57 +149,46 @@ protected:
|
|||
// --------------------------------------------------------------------------------------
|
||||
// StateThread_ZipToDisk
|
||||
// --------------------------------------------------------------------------------------
|
||||
class StateThread_ZipToDisk : public PersistentThread
|
||||
class StateThread_ZipToDisk : public _BaseStateThread
|
||||
{
|
||||
typedef PersistentThread _parent;
|
||||
typedef _BaseStateThread _parent;
|
||||
|
||||
protected:
|
||||
gzFile m_gzfp;
|
||||
const wxString m_filename;
|
||||
gzFile m_gzfp;
|
||||
|
||||
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()
|
||||
{
|
||||
sys_resume_lock = false; // just in case;
|
||||
if( m_gzfp != NULL ) gzclose( m_gzfp );
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
Sleep( 2 );
|
||||
Yield( 2 );
|
||||
if( gzwrite( (gzFile)m_gzfp, state_buffer.GetPtr(), state_buffer.GetSizeInBytes() ) < state_buffer.GetSizeInBytes() )
|
||||
throw Exception::BadStream();
|
||||
}
|
||||
|
||||
void OnThreadCleanup()
|
||||
{
|
||||
wxCommandEvent evt( pxEVT_FreezeFinished );
|
||||
evt.SetClientData( this ); // tells message to clean us up.
|
||||
wxGetApp().AddPendingEvent( evt );
|
||||
|
||||
SendFinishEvent( StateThreadAction_ZipToDisk );
|
||||
_parent::OnThreadCleanup();
|
||||
}
|
||||
};
|
||||
|
@ -168,43 +197,41 @@ protected:
|
|||
// --------------------------------------------------------------------------------------
|
||||
// StateThread_UnzipFromDisk
|
||||
// --------------------------------------------------------------------------------------
|
||||
class StateThread_UnzipFromDisk : public PersistentThread
|
||||
class StateThread_UnzipFromDisk : public _BaseStateThread
|
||||
{
|
||||
typedef PersistentThread _parent;
|
||||
typedef _BaseStateThread _parent;
|
||||
|
||||
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:
|
||||
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()
|
||||
{
|
||||
sys_resume_lock = false; // just in case;
|
||||
if( m_gzfp != NULL ) gzclose( m_gzfp );
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
|
@ -217,70 +244,95 @@ protected:
|
|||
state_buffer.ExactAlloc( curidx+BlockSize );
|
||||
gzread( m_gzfp, state_buffer.GetPtr(curidx), BlockSize );
|
||||
curidx += BlockSize;
|
||||
TestCancel();
|
||||
} while( !gzeof(m_gzfp) );
|
||||
|
||||
m_finished = true;
|
||||
}
|
||||
|
||||
void OnThreadCleanup()
|
||||
{
|
||||
wxCommandEvent evt( pxEVT_ThawFinished );
|
||||
evt.SetClientData( this ); // tells message to clean us up.
|
||||
wxGetApp().AddPendingEvent( evt );
|
||||
|
||||
SendFinishEvent( StateThreadAction_UnzipFromDisk );
|
||||
_parent::OnThreadCleanup();
|
||||
}
|
||||
};
|
||||
|
||||
void Pcsx2App::OnFreezeFinished( wxCommandEvent& evt )
|
||||
void Pcsx2App::OnFreezeThreadFinished( wxCommandEvent& evt )
|
||||
{
|
||||
state_buffer.Dispose();
|
||||
state_buffer_lock = false;
|
||||
// clear the OnFreezeFinsihed to NULL now, in case of error.
|
||||
// (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( thr == NULL )
|
||||
if( evt.GetInt() == StateThreadAction_Restore )
|
||||
{
|
||||
pxAssert( false );
|
||||
return;
|
||||
// Successfully restored state, so remove the copy. Don't remove it sooner
|
||||
// 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();
|
||||
|
||||
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 )
|
||||
{
|
||||
if( state_buffer_lock ) return;
|
||||
new StateThread_ZipToDisk( file );
|
||||
if( state_buffer_lock.IsLocked() ) return;
|
||||
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 )
|
||||
{
|
||||
if( state_buffer_lock ) return;
|
||||
|
||||
sCoreThread.ShortSuspend();
|
||||
new StateThread_UnzipFromDisk( file );
|
||||
if( state_buffer_lock.IsLocked() ) return;
|
||||
sCoreThread.Pause();
|
||||
(new StateThread_UnzipFromDisk( OnFinished_Restore, file ))->Start();
|
||||
}
|
||||
|
||||
// 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. :)
|
||||
void StateCopy_SaveToSlot( uint num )
|
||||
{
|
||||
if( state_buffer_lock ) return;
|
||||
StateCopy_SaveToFile( SaveStateBase::GetFilename( num ) );
|
||||
zip_dest_filename = 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()
|
||||
|
@ -310,46 +382,19 @@ bool StateCopy_HasPartialState()
|
|||
|
||||
void StateCopy_FreezeToMem()
|
||||
{
|
||||
if( state_buffer_lock ) return;
|
||||
if( state_buffer_lock.IsLocked() ) return;
|
||||
(new StateThread_Freeze( OnFinished_Restore ))->Start();
|
||||
}
|
||||
|
||||
void StateCopy_ThawFromMem()
|
||||
{
|
||||
if( state_buffer_lock ) return;
|
||||
if( state_buffer_lock.IsLocked() ) return;
|
||||
new StateThread_Thaw( OnFinished_Restore );
|
||||
}
|
||||
|
||||
void StateCopy_Clear()
|
||||
{
|
||||
if( state_buffer_lock ) return;
|
||||
if( state_buffer_lock.IsLocked() ) return;
|
||||
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;
|
||||
}*/
|
||||
}
|
||||
|
||||
|
|
|
@ -136,6 +136,9 @@ static const int MainMemorySizeInBytes =
|
|||
|
||||
void SaveStateBase::FreezeMainMemory()
|
||||
{
|
||||
if( IsLoading() )
|
||||
PreLoadPrep();
|
||||
|
||||
// First Block - Memory Dumps
|
||||
// ---------------------------
|
||||
FreezeMem(PS2MEM_BASE, Ps2MemSize::Base); // 32 MB main memory
|
||||
|
@ -149,8 +152,8 @@ void SaveStateBase::FreezeMainMemory()
|
|||
|
||||
void SaveStateBase::FreezeRegisters()
|
||||
{
|
||||
//if( IsLoading() )
|
||||
// PreLoadPrep();
|
||||
if( IsLoading() )
|
||||
PreLoadPrep();
|
||||
|
||||
// Second Block - Various CPU Registers and States
|
||||
// -----------------------------------------------
|
||||
|
|
|
@ -202,5 +202,6 @@ extern void StateCopy_ThawFromMem();
|
|||
extern void StateCopy_SaveToFile( const wxString& file );
|
||||
extern void StateCopy_LoadFromFile( const wxString& file );
|
||||
extern void StateCopy_SaveToSlot( uint num );
|
||||
extern void StateCopy_LoadFromSlot( uint slot );
|
||||
extern void StateCopy_Clear();
|
||||
|
||||
|
|
|
@ -139,7 +139,7 @@ SysCoreAllocations::SysCoreAllocations()
|
|||
throw Exception::OutOfMemory(
|
||||
wxsFormat( // Diagnostic (english)
|
||||
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
|
||||
|
|
|
@ -55,7 +55,6 @@ void SysSuspendableThread::OnStart()
|
|||
_parent::OnStart();
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// 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 )
|
||||
{
|
||||
if( IsSelf() || !IsRunning() ) return;
|
||||
|
@ -76,11 +80,34 @@ void SysSuspendableThread::Suspend( bool isBlocking )
|
|||
if( m_ExecMode == ExecMode_Suspended )
|
||||
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 )
|
||||
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_SuspendEvent.WaitGui();
|
||||
}
|
||||
|
@ -111,6 +138,7 @@ void SysSuspendableThread::Resume()
|
|||
// fall through...
|
||||
|
||||
case ExecMode_Suspending:
|
||||
case ExecMode_Pausing:
|
||||
// we need to make sure and wait for the emuThread to enter a fully suspended
|
||||
// state before continuing...
|
||||
|
||||
|
@ -120,8 +148,8 @@ void SysSuspendableThread::Resume()
|
|||
}
|
||||
}
|
||||
|
||||
pxAssertDev( m_ExecMode == ExecMode_Suspended,
|
||||
"SysSuspendableThread is not in a suspended/idle state? wtf!" );
|
||||
pxAssertDev( (m_ExecMode == ExecMode_Suspended) || (m_ExecMode == ExecMode_Paused),
|
||||
"SysSuspendableThread is not in a suspended/paused state? wtf!" );
|
||||
|
||||
m_ExecMode = ExecMode_Running;
|
||||
m_ResumeProtection = true;
|
||||
|
@ -148,7 +176,7 @@ void SysSuspendableThread::StateCheck( bool isCancelable )
|
|||
// Shortcut for the common case, to avoid unnecessary Mutex locks:
|
||||
if( m_ExecMode == ExecMode_Running )
|
||||
{
|
||||
if( isCancelable ) pthread_testcancel();
|
||||
if( isCancelable ) TestCancel();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -170,23 +198,41 @@ void SysSuspendableThread::StateCheck( bool isCancelable )
|
|||
// Yup, need this a second time. Variable state could have changed while we
|
||||
// were trying to acquire the lock above.
|
||||
if( isCancelable )
|
||||
pthread_testcancel();
|
||||
TestCancel();
|
||||
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:
|
||||
{
|
||||
OnSuspendInThread();
|
||||
m_ExecMode = ExecMode_Suspended;
|
||||
m_SuspendEvent.Post();
|
||||
}
|
||||
// fall through...
|
||||
|
||||
// fallthrough...
|
||||
|
||||
case ExecMode_Suspended:
|
||||
m_lock_ExecMode.Unlock();
|
||||
while( m_ExecMode == ExecMode_Suspended )
|
||||
m_ResumeEvent.WaitGui();
|
||||
|
||||
OnResumeInThread();
|
||||
OnResumeInThread( true );
|
||||
break;
|
||||
|
||||
jNO_DEFAULT;
|
||||
|
@ -218,16 +264,6 @@ void SysCoreThread::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
|
||||
// settings were changed, resets will be performed as needed and emulation state resumed from
|
||||
// memory savestates.
|
||||
|
@ -348,11 +384,6 @@ void SysCoreThread::CpuExecute()
|
|||
PCSX2_MEM_PROTECT_END();
|
||||
}
|
||||
|
||||
static void _cet_callback_cleanup( void* handle )
|
||||
{
|
||||
((SysCoreThread*)handle)->OnThreadCleanup();
|
||||
}
|
||||
|
||||
void SysCoreThread::ExecuteTask()
|
||||
{
|
||||
tls_coreThread = this;
|
||||
|
@ -369,9 +400,10 @@ void SysCoreThread::OnSuspendInThread()
|
|||
m_plugins.Close();
|
||||
}
|
||||
|
||||
void SysCoreThread::OnResumeInThread()
|
||||
void SysCoreThread::OnResumeInThread( bool isSuspended )
|
||||
{
|
||||
m_plugins.Open();
|
||||
if( isSuspended )
|
||||
m_plugins.Open();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -41,7 +41,9 @@ protected:
|
|||
ExecMode_NoThreadYet,
|
||||
ExecMode_Running,
|
||||
ExecMode_Suspending,
|
||||
ExecMode_Suspended
|
||||
ExecMode_Suspended,
|
||||
ExecMode_Pausing,
|
||||
ExecMode_Paused,
|
||||
};
|
||||
|
||||
volatile ExecutionMode m_ExecMode;
|
||||
|
@ -59,6 +61,7 @@ public:
|
|||
|
||||
virtual void Suspend( bool isBlocking = true );
|
||||
virtual void Resume();
|
||||
virtual void Pause();
|
||||
|
||||
virtual void StateCheck( bool isCancelable = true );
|
||||
virtual void OnThreadCleanup();
|
||||
|
@ -78,11 +81,21 @@ protected:
|
|||
// thread, requesting this thread suspend itself temporarily). After this is called,
|
||||
// the thread enters a waiting state on the m_ResumeEvent semaphore.
|
||||
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
|
||||
// handles invocation by the following guidelines: Called from StateCheck() after the
|
||||
// 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 OnThreadCleanup();
|
||||
virtual void ShortSuspend();
|
||||
virtual void OnResumeReady();
|
||||
|
||||
protected:
|
||||
|
@ -116,6 +128,7 @@ protected:
|
|||
|
||||
virtual void Start();
|
||||
virtual void OnSuspendInThread();
|
||||
virtual void OnResumeInThread();
|
||||
virtual void OnPauseInThread() {}
|
||||
virtual void OnResumeInThread( bool IsSuspended );
|
||||
virtual void ExecuteTask();
|
||||
};
|
||||
|
|
|
@ -27,12 +27,6 @@
|
|||
VUmicroCpu CpuVU0; // contains a working copy of the VU0 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()
|
||||
{
|
||||
CpuVU0 = CHECK_VU0REC ? recVU0 : intVU0;
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include <wx/docview.h>
|
||||
#include <wx/apptrait.h>
|
||||
|
||||
#include "Utilities/Listeners.h"
|
||||
|
||||
class IniInterface;
|
||||
class MainEmuFrame;
|
||||
class GSFrame;
|
||||
|
@ -35,6 +37,9 @@ class AppCoreThread;
|
|||
#include "System.h"
|
||||
#include "System/SysThreads.h"
|
||||
|
||||
|
||||
typedef void FnType_OnThreadComplete(const wxCommandEvent& evt);
|
||||
|
||||
#define AllowFromMainThreadOnly() \
|
||||
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_OpenModalDialog, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEVT_ReloadPlugins, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEVT_SysExecute, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEVT_LoadPluginsComplete, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEVT_AppCoreThread_Terminated, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEVT_FreezeFinished, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEVT_ThawFinished, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEVT_AppCoreThreadFinished, -1 )
|
||||
DECLARE_EVENT_TYPE( pxEVT_FreezeThreadFinished, -1 )
|
||||
END_DECLARE_EVENT_TYPES()
|
||||
|
||||
// This is used when the GS plugin is handling its own window. Messages from the PAD
|
||||
|
@ -335,8 +340,6 @@ public:
|
|||
Pcsx2App();
|
||||
virtual ~Pcsx2App();
|
||||
|
||||
void ReloadPlugins();
|
||||
|
||||
void PostPadKey( wxKeyEvent& evt );
|
||||
void PostMenuAction( MenuIdentifiers menu_id ) const;
|
||||
int ThreadedModalDialog( DialogIdentifiers dialogId );
|
||||
|
@ -392,6 +395,18 @@ public:
|
|||
void DisableDiskLogging() const;
|
||||
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:
|
||||
void InitDefaultGlobalAccelerators();
|
||||
void BuildCommandHash();
|
||||
|
@ -402,18 +417,18 @@ protected:
|
|||
|
||||
void HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent& event) const;
|
||||
|
||||
void OnSysExecute( wxCommandEvent& evt );
|
||||
void OnReloadPlugins( wxCommandEvent& evt );
|
||||
void OnLoadPluginsComplete( wxCommandEvent& evt );
|
||||
void OnSemaphorePing( wxCommandEvent& evt );
|
||||
void OnOpenModalDialog( wxCommandEvent& evt );
|
||||
void OnCoreThreadTerminated( wxCommandEvent& evt );
|
||||
|
||||
void OnFreezeFinished( wxCommandEvent& evt );
|
||||
void OnThawFinished( wxCommandEvent& evt );
|
||||
void OnFreezeThreadFinished( wxCommandEvent& evt );
|
||||
|
||||
void OnMessageBox( pxMessageBoxEvent& evt );
|
||||
void OnEmuKeyDown( wxKeyEvent& evt );
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Override wx default exception handling behavior
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -496,7 +511,7 @@ DECLARE_APP(Pcsx2App)
|
|||
extern bool sys_resume_lock;
|
||||
|
||||
extern int EnumeratePluginsInFolder( const wxDirName& searchPath, wxArrayString* dest );
|
||||
extern void LoadPluginsPassive();
|
||||
extern void LoadPluginsPassive( FnType_OnThreadComplete* onComplete );
|
||||
extern void LoadPluginsImmediate();
|
||||
extern void UnloadPlugins();
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ void AppCoreThread::OnResumeReady()
|
|||
// the new (lack of) thread status, so this posts a message to the App to do so.
|
||||
void AppCoreThread::OnThreadCleanup()
|
||||
{
|
||||
wxCommandEvent evt( pxEVT_AppCoreThread_Terminated );
|
||||
wxCommandEvent evt( pxEVT_AppCoreThreadFinished );
|
||||
wxGetApp().AddPendingEvent( evt );
|
||||
_parent::OnThreadCleanup();
|
||||
}
|
||||
|
|
|
@ -220,12 +220,12 @@ bool Pcsx2App::OnInit()
|
|||
Connect( pxEVT_SemaphorePing, wxCommandEventHandler( Pcsx2App::OnSemaphorePing ) );
|
||||
Connect( pxEVT_OpenModalDialog, wxCommandEventHandler( Pcsx2App::OnOpenModalDialog ) );
|
||||
Connect( pxEVT_ReloadPlugins, wxCommandEventHandler( Pcsx2App::OnReloadPlugins ) );
|
||||
Connect( pxEVT_SysExecute, wxCommandEventHandler( Pcsx2App::OnSysExecute ) );
|
||||
|
||||
Connect( pxEVT_LoadPluginsComplete, wxCommandEventHandler( Pcsx2App::OnLoadPluginsComplete ) );
|
||||
|
||||
Connect( pxEVT_FreezeFinished, wxCommandEventHandler( Pcsx2App::OnCoreThreadTerminated ) );
|
||||
|
||||
Connect( pxEVT_FreezeFinished, wxCommandEventHandler( Pcsx2App::OnFreezeFinished ) );
|
||||
Connect( pxEVT_ThawFinished, wxCommandEventHandler( Pcsx2App::OnThawFinished ) );
|
||||
Connect( pxEVT_AppCoreThreadFinished, wxCommandEventHandler( Pcsx2App::OnCoreThreadTerminated ) );
|
||||
Connect( pxEVT_FreezeThreadFinished, wxCommandEventHandler( Pcsx2App::OnFreezeThreadFinished ) );
|
||||
|
||||
Connect( pxID_PadHandler_Keydown, wxEVT_KEY_DOWN, wxKeyEventHandler( Pcsx2App::OnEmuKeyDown ) );
|
||||
|
||||
|
@ -310,7 +310,7 @@ bool Pcsx2App::OnInit()
|
|||
return false;
|
||||
}
|
||||
|
||||
LoadPluginsPassive();
|
||||
LoadPluginsPassive( NULL );
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
catch( Exception::StartupAborted& ex )
|
||||
|
|
|
@ -30,10 +30,10 @@ IMPLEMENT_APP(Pcsx2App)
|
|||
DEFINE_EVENT_TYPE( pxEVT_SemaphorePing );
|
||||
DEFINE_EVENT_TYPE( pxEVT_OpenModalDialog );
|
||||
DEFINE_EVENT_TYPE( pxEVT_ReloadPlugins );
|
||||
DEFINE_EVENT_TYPE( pxEVT_SysExecute );
|
||||
DEFINE_EVENT_TYPE( pxEVT_LoadPluginsComplete );
|
||||
DEFINE_EVENT_TYPE( pxEVT_AppCoreThread_Terminated );
|
||||
DEFINE_EVENT_TYPE( pxEVT_FreezeFinished );
|
||||
DEFINE_EVENT_TYPE( pxEVT_ThawFinished );
|
||||
DEFINE_EVENT_TYPE( pxEVT_AppCoreThreadFinished );
|
||||
DEFINE_EVENT_TYPE( pxEVT_FreezeThreadFinished );
|
||||
|
||||
bool UseAdminMode = false;
|
||||
wxDirName SettingsFolder;
|
||||
|
@ -211,6 +211,18 @@ void Pcsx2App::HandleEvent(wxEvtHandler *handler, wxEventFunction func, wxEvent&
|
|||
(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 )
|
||||
{
|
||||
Console.Error( ex.FormatDiagnosticMessage() );
|
||||
|
@ -376,32 +388,53 @@ void Pcsx2App::OnMainFrameClosed()
|
|||
m_MainFrame = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// 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
|
||||
// configured CDVD source device.
|
||||
void Pcsx2App::SysExecute()
|
||||
{
|
||||
if( sys_resume_lock )
|
||||
if( !m_CorePlugins )
|
||||
{
|
||||
Console.WriteLn( "SysExecute: State is locked, ignoring Execute request!" );
|
||||
LoadPluginsPassive( OnSysExecuteAfterPlugins );
|
||||
return;
|
||||
}
|
||||
|
||||
SysReset();
|
||||
LoadPluginsImmediate();
|
||||
m_CoreThread = new AppCoreThread( *m_CorePlugins );
|
||||
m_CoreThread->Resume();
|
||||
wxCommandEvent evt( pxEVT_SysExecute );
|
||||
evt.SetInt( -1 );
|
||||
AddPendingEvent( evt );
|
||||
}
|
||||
|
||||
// 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
|
||||
// sources.
|
||||
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 )
|
||||
{
|
||||
|
@ -409,10 +442,13 @@ void Pcsx2App::SysExecute( CDVD_SourceType cdvdsrc )
|
|||
return;
|
||||
}
|
||||
|
||||
SysReset();
|
||||
LoadPluginsImmediate();
|
||||
// if something unloaded plugins since this messages was queued then it's best to ignore
|
||||
// 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_ChangeSource( cdvdsrc );
|
||||
if( evt.GetInt() != -1 ) CDVDsys_ChangeSource( (CDVD_SourceType)evt.GetInt() );
|
||||
|
||||
m_CoreThread = new AppCoreThread( *m_CorePlugins );
|
||||
m_CoreThread->Resume();
|
||||
|
|
|
@ -109,7 +109,7 @@ void ConsoleTestThread::ExecuteTask()
|
|||
// worst case scenario (without being entirely unrealistic).
|
||||
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)" ) );
|
||||
Sleep( 0 );
|
||||
Yield( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
|
|||
m_bitmap_dualshock( this, wxID_ANY, wxBitmap( EmbeddedImage<res_Dualshock>().Get() ),
|
||||
wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN )
|
||||
{
|
||||
static const wxString LabelAuthors = wxString::FromUTF8(
|
||||
static const wxString LabelAuthors = fromUTF8(
|
||||
"Developers"
|
||||
"\n\n"
|
||||
"v0.9.6+: Arcum42, Refraction,"
|
||||
|
@ -69,7 +69,7 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
|
|||
"Webmasters: CKemu, Falcon4ever"
|
||||
);
|
||||
|
||||
static const wxString LabelGreets = wxString::FromUTF8(
|
||||
static const wxString LabelGreets = fromUTF8(
|
||||
"Contributors"
|
||||
"\n\n"
|
||||
"Hiryu and Sjeep (libcdvd / iso filesystem), nneeve (fpu and vu)"
|
||||
|
|
|
@ -44,7 +44,7 @@ static const int IdealWidth = 500;
|
|||
template< typename T >
|
||||
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 );
|
||||
g_ApplyState.SetCurrentPage( curidx );
|
||||
m_listbook.AddPage( new T( m_listbook, IdealWidth ), wxGetTranslation( labelstr ),
|
||||
|
|
|
@ -263,7 +263,7 @@ void AcceleratorDictionary::Map( const KeyAcceleratorCode& acode, const char *se
|
|||
Console.Notice( wxsFormat(
|
||||
L"Kbd Accelerator '%s' is mapped multiple times.\n"
|
||||
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 )
|
||||
{
|
||||
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
|
||||
|
|
|
@ -310,7 +310,7 @@ MainEmuFrame::MainEmuFrame(wxWindow* parent, const wxString& title):
|
|||
{
|
||||
// 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,
|
||||
SVN_REV, SVN_MODS ? L"m" : wxEmptyString, wxString::FromUTF8(__DATE__).c_str() );
|
||||
SVN_REV, SVN_MODS ? L"m" : wxEmptyString, fromUTF8(__DATE__).c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -559,13 +559,13 @@ void Panels::PluginSelectorPanel::EnumThread::ExecuteTask()
|
|||
DevCon.Status( "Plugin Enumeration Thread started..." );
|
||||
|
||||
wxGetApp().Ping(); // gives the gui thread some time to refresh
|
||||
Sleep( 3 );
|
||||
Yield( 3 );
|
||||
|
||||
for( int curidx=0; curidx < m_master.FileCount(); ++curidx )
|
||||
{
|
||||
DoNextPlugin( curidx );
|
||||
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.
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
|
||||
using namespace Threading;
|
||||
|
||||
static FnType_OnThreadComplete* Callback_PluginsLoadComplete = NULL;
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// LoadPluginsTask
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -37,76 +39,52 @@ using namespace Threading;
|
|||
//
|
||||
class LoadPluginsTask : public PersistentThread
|
||||
{
|
||||
typedef PersistentThread _parent;
|
||||
|
||||
public:
|
||||
Exception::PluginError* Ex_PluginError;
|
||||
Exception::RuntimeError* Ex_RuntimeError;
|
||||
PluginManager* Result;
|
||||
|
||||
protected:
|
||||
wxString m_folders[PluginId_Count];
|
||||
|
||||
public:
|
||||
LoadPluginsTask( const wxString (&folders)[PluginId_Count] ) :
|
||||
Ex_PluginError( NULL )
|
||||
, Ex_RuntimeError( NULL )
|
||||
, Result( NULL )
|
||||
LoadPluginsTask( const wxString (&folders)[PluginId_Count] ) : Result( NULL )
|
||||
{
|
||||
for(int i=0; i<PluginId_Count; ++i )
|
||||
m_folders[i] = folders[i];
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
virtual ~LoadPluginsTask() throw();
|
||||
|
||||
protected:
|
||||
void OnStart() {}
|
||||
void OnThreadCleanup() {}
|
||||
void OnThreadCleanup();
|
||||
void ExecuteTask();
|
||||
};
|
||||
|
||||
static ScopedPtr<LoadPluginsTask> _loadTask;
|
||||
|
||||
LoadPluginsTask::~LoadPluginsTask() throw()
|
||||
{
|
||||
if( _loadTask )
|
||||
_loadTask.DetachPtr(); // avoids recursive deletion
|
||||
|
||||
PersistentThread::Cancel();
|
||||
_loadTask = NULL;
|
||||
}
|
||||
|
||||
void LoadPluginsTask::ExecuteTask()
|
||||
{
|
||||
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 );
|
||||
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 );
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
ReloadPlugins();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if( plugin_load_lock ) return;
|
||||
m_CoreThread = NULL;
|
||||
m_CorePlugins = NULL;
|
||||
|
||||
|
@ -169,42 +140,79 @@ void Pcsx2App::ReloadPlugins()
|
|||
passins[pi->id] = g_Conf->FullpathTo( pi->id );
|
||||
} while( ++pi, pi->shortname != NULL );
|
||||
|
||||
_loadTask.Delete() = new LoadPluginsTask( passins );
|
||||
(new LoadPluginsTask( passins ))->Start();
|
||||
// ... 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
|
||||
// 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.
|
||||
void LoadPluginsPassive()
|
||||
// If plugins are already loaded, onComplete is invoked, and the function returns with no
|
||||
// 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 );
|
||||
wxGetApp().AddPendingEvent( evt );
|
||||
}
|
||||
|
||||
// Blocks until plugins have been successfully loaded, or throws an exception if
|
||||
// the user cancels the loading procedure after error. If plugins are already loaded
|
||||
// then no action is performed.
|
||||
// Performs a blocking load of plugins. If the emulation thread is active, it is shut down
|
||||
// automatically to prevent race conditions (it depends on plugins).
|
||||
//
|
||||
// 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()
|
||||
{
|
||||
AllowFromMainThreadOnly();
|
||||
if( g_plugins ) return;
|
||||
if( g_plugins != NULL ) return;
|
||||
|
||||
static int _reentrant = 0;
|
||||
RecursionGuard guard( _reentrant );
|
||||
wxGetApp().m_CoreThread = NULL;
|
||||
|
||||
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()
|
||||
{
|
||||
wxGetApp().m_CoreThread = NULL;
|
||||
wxGetApp().m_CorePlugins = NULL;
|
||||
}
|
||||
|
|
|
@ -23,59 +23,10 @@
|
|||
|
||||
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.
|
||||
void States_Save( const wxString& file )
|
||||
{
|
||||
if( !SysHasValidState() )
|
||||
{
|
||||
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()
|
||||
);
|
||||
}
|
||||
StateCopy_SaveToFile( file );
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
@ -95,22 +46,12 @@ bool States_isSlotUsed(int num)
|
|||
|
||||
void States_FreezeCurrentSlot()
|
||||
{
|
||||
Console.Status( "Saving savestate to slot %d...", StatesC );
|
||||
States_Save( SaveStateBase::GetFilename( StatesC ) );
|
||||
StateCopy_SaveToSlot( StatesC );
|
||||
}
|
||||
|
||||
void States_DefrostCurrentSlot()
|
||||
{
|
||||
wxString file( SaveStateBase::GetFilename( StatesC ) );
|
||||
|
||||
if( !wxFileExists( file ) )
|
||||
{
|
||||
Console.Notice( "Savestate slot %d is empty.", StatesC );
|
||||
return;
|
||||
}
|
||||
|
||||
Console.Status( "Loading savestate from slot %d...", StatesC );
|
||||
States_Load( file );
|
||||
StateCopy_LoadFromSlot( StatesC );
|
||||
//SysStatus( wxsFormat( _("Loaded State (slot %d)"), StatesC ) );
|
||||
}
|
||||
|
||||
|
|
|
@ -1804,6 +1804,10 @@
|
|||
RelativePath="..\..\gui\IniInterface.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\common\include\Utilities\Listeners.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\gui\MainFrame.cpp"
|
||||
>
|
||||
|
|
|
@ -142,6 +142,9 @@ static __forceinline bool ReadPipe(HANDLE h_Pipe, ConsoleColors color )
|
|||
return true;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// WinPipeThread
|
||||
// --------------------------------------------------------------------------------------
|
||||
class WinPipeThread : public PersistentThread
|
||||
{
|
||||
typedef PersistentThread _parent;
|
||||
|
@ -173,8 +176,7 @@ protected:
|
|||
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL );
|
||||
while( true )
|
||||
{
|
||||
Sleep( 100 );
|
||||
pthread_testcancel();
|
||||
Yield( 100 );
|
||||
ReadPipe( m_outpipe, m_color );
|
||||
}
|
||||
}
|
||||
|
@ -189,6 +191,9 @@ protected:
|
|||
void OnThreadCleanup() { }
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// WinPipeRedirection
|
||||
// --------------------------------------------------------------------------------------
|
||||
class WinPipeRedirection : public PipeRedirectionBase
|
||||
{
|
||||
DeclareNoncopyableObject( WinPipeRedirection );
|
||||
|
@ -221,7 +226,8 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) :
|
|||
DWORD stdhandle = ( stdstream == stderr ) ? STD_ERROR_HANDLE : STD_OUTPUT_HANDLE;
|
||||
|
||||
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.
|
||||
HANDLE newhandle = GetStdHandle(stdhandle);
|
||||
|
@ -231,11 +237,11 @@ WinPipeRedirection::WinPipeRedirection( FILE* stdstream ) :
|
|||
if( newhandle == NULL )
|
||||
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 )
|
||||
throw Exception::RuntimeError( "_open_osfhandle returned -1." );
|
||||
|
||||
m_fp = _fdopen( m_crtFile, "w" );
|
||||
m_fp = _fdopen( m_crtFile, "w" );
|
||||
if( m_fp == NULL )
|
||||
throw Exception::RuntimeError( "_fdopen returned NULL." );
|
||||
|
||||
|
@ -269,10 +275,22 @@ void WinPipeRedirection::Cleanup() throw()
|
|||
{
|
||||
fclose( m_fp );
|
||||
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
|
||||
// m_file is closed implicitly when closing crtFile
|
||||
if( m_file != INVALID_HANDLE_VALUE )
|
||||
{
|
||||
CloseHandle( m_pipe );
|
||||
m_file = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if( m_pipe != INVALID_HANDLE_VALUE )
|
||||
{
|
||||
|
|
|
@ -30,7 +30,6 @@ int mVUdebugNow = 0;
|
|||
|
||||
#ifdef DEBUG_COMPARE
|
||||
|
||||
#include <windows.h>
|
||||
static int runAmount = 0;
|
||||
|
||||
void VUtestPause() {
|
||||
|
@ -70,8 +69,9 @@ void VUtestPause() {
|
|||
SysPrintf("VU Mem CRC = 0x%08x\n", j);
|
||||
SysPrintf("EndPC = 0x%04x\n", VU1.VI[REG_TPC].UL);
|
||||
|
||||
// ... wtf?? --air
|
||||
for (int i = 0; i < 10000000; i++) {
|
||||
Sleep(1000);
|
||||
Threading::Sleep(1000);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
@ -84,9 +84,6 @@ extern u32 vudump;
|
|||
|
||||
#ifdef DEBUG_COMPARE2
|
||||
|
||||
#ifndef DEBUG_COMPARE
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
__aligned16 u8 backVUregs[sizeof(VURegs)];
|
||||
__aligned16 u8 cmpVUregs [sizeof(VURegs)];
|
||||
|
|
|
@ -238,20 +238,6 @@ static __forceinline s32 __fastcall GetNextDataBuffered( V_Core& thiscore, uint
|
|||
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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -63,8 +63,6 @@ StereoOut32 V_Core::ReadInput_HiFi()
|
|||
AdmaInProgress = 0;
|
||||
if(InputDataLeft >= 0x200)
|
||||
{
|
||||
u8 k = (InputDataLeft >= InputDataProgress);
|
||||
|
||||
#ifdef PCM24_S1_INTERLEAVE
|
||||
AutoDMAReadBuffer(1);
|
||||
#else
|
||||
|
|
|
@ -837,7 +837,7 @@ static void __fastcall RegWrite_Core( u16 value )
|
|||
{
|
||||
bool irqe = thiscore.IRQEnable;
|
||||
int bit0 = thiscore.AttrBit0;
|
||||
int bit4 = thiscore.AttrBit4;
|
||||
//int bit4 = thiscore.AttrBit4;
|
||||
|
||||
if( ((value>>15)&1) && (!thiscore.CoreEnabled) && (thiscore.InitDelay==0) ) // on init/reset
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue