diff --git a/common/build/Utilities/utilities.vcproj b/common/build/Utilities/utilities.vcproj
index 5aba44fe5..3fcfaea46 100644
--- a/common/build/Utilities/utilities.vcproj
+++ b/common/build/Utilities/utilities.vcproj
@@ -429,6 +429,10 @@
RelativePath="..\..\include\intrin_x86.h"
>
+
+
diff --git a/common/include/PluginCallbacks.h b/common/include/PluginCallbacks.h
index 7f4a0d24e..f6e2e85af 100644
--- a/common/include/PluginCallbacks.h
+++ b/common/include/PluginCallbacks.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;
diff --git a/common/include/Utilities/Assertions.h b/common/include/Utilities/Assertions.h
index 4abe4dd93..d09439651 100644
--- a/common/include/Utilities/Assertions.h
+++ b/common/include/Utilities/Assertions.h
@@ -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);
diff --git a/common/include/Utilities/Exceptions.h b/common/include/Utilities/Exceptions.h
index 1762e1fe9..0790ea614 100644
--- a/common/include/Utilities/Exceptions.h
+++ b/common/include/Utilities/Exceptions.h
@@ -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 ) \
{ \
diff --git a/common/include/Utilities/Listeners.h b/common/include/Utilities/Listeners.h
new file mode 100644
index 000000000..e1ee2b4ed
--- /dev/null
+++ b/common/include/Utilities/Listeners.h
@@ -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 .
+ */
+
+#pragma once
+
+#include
+
+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 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::ListenerType ListenerHandle;
+ typedef typename EventSource::Handle ConstIterator;
+
+protected:
+ EventSource& m_source;
+ const ListenerHandle m_listener;
+ ConstIterator m_iter;
+ bool m_attached;
+
+public:
+ EventListenerBinding( EventSource& 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 CmdEvt_Source;
+typedef EventListener CmdEvt_Listener;
+typedef EventListenerBinding CmdEvt_ListenerBinding;
+
+
+// Checks for duplicates before adding the event.
+template< typename EvtType >
+void EventSource::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::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 ) );
+}
+
diff --git a/common/include/Utilities/RedtapeWindows.h b/common/include/Utilities/RedtapeWindows.h
index 2551a1a1c..aa3cb1b86 100644
--- a/common/include/Utilities/RedtapeWindows.h
+++ b/common/include/Utilities/RedtapeWindows.h
@@ -15,6 +15,10 @@
#pragma once
+#ifdef __WXMSW__
+# include
+#else
+
//////////////////////////////////////////////////////////////////////////////////////////
// Windows Redtape! No windows.h should be included without it!
//
@@ -40,3 +44,4 @@
#undef max
#endif
+#endif
diff --git a/common/include/Utilities/Threading.h b/common/include/Utilities/Threading.h
index 08e3473cd..59c67314c 100644
--- a/common/include/Utilities/Threading.h
+++ b/common/include/Utilities/Threading.h
@@ -15,25 +15,14 @@
#pragma once
+#include
#include // EBUSY
#include
-#include
#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 );
};
//////////////////////////////////////////////////////////////////////////////////////////
diff --git a/common/include/Utilities/win_memzero.h b/common/include/Utilities/win_memzero.h
index 21dcc274a..3c26316b7 100644
--- a/common/include/Utilities/win_memzero.h
+++ b/common/include/Utilities/win_memzero.h
@@ -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.
diff --git a/common/src/Utilities/Console.cpp b/common/src/Utilities/Console.cpp
index 7b8c6fd5a..0a6db799d 100644
--- a/common/src/Utilities/Console.cpp
+++ b/common/src/Utilities/Console.cpp
@@ -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
diff --git a/common/src/Utilities/Exceptions.cpp b/common/src/Utilities/Exceptions.cpp
index c686c3c70..359097f66 100644
--- a/common/src/Utilities/Exceptions.cpp
+++ b/common/src/Utilities/Exceptions.cpp
@@ -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()
);
}
diff --git a/common/src/Utilities/ThreadTools.cpp b/common/src/Utilities/ThreadTools.cpp
index b6acb7b29..d2a87f429 100644
--- a/common/src/Utilities/ThreadTools.cpp
+++ b/common/src/Utilities/ThreadTools.cpp
@@ -15,24 +15,30 @@
#include "PrecompiledHeader.h"
-#include "Threading.h"
-#include "wxBaseTools.h"
-#include
-#include
+#ifdef _WIN32
+# include // for thread renaming features
+#endif
#include
#ifdef __LINUX__
# include // for pthread_kill, which is in pthread.h on w32-pthreads
#endif
+#include "Threading.h"
+#include "wxBaseTools.h"
+
+#include
+#include
+
+
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.
diff --git a/common/src/Utilities/Windows/WinThreads.cpp b/common/src/Utilities/Windows/WinThreads.cpp
index 7bccb2de4..91eb0bb84 100644
--- a/common/src/Utilities/Windows/WinThreads.cpp
+++ b/common/src/Utilities/Windows/WinThreads.cpp
@@ -18,7 +18,6 @@
#include "x86emitter/tools.h"
#include "Threading.h"
-
#ifdef _WIN32
#include "implement.h" // win32 pthreads implementations.
#endif
diff --git a/common/src/x86emitter/cpudetect.cpp b/common/src/x86emitter/cpudetect.cpp
index fc7c58c76..79463297a 100644
--- a/common/src/x86emitter/cpudetect.cpp
+++ b/common/src/x86emitter/cpudetect.cpp
@@ -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;
diff --git a/common/vsprops/BaseProperties.vsprops b/common/vsprops/BaseProperties.vsprops
index ef8f2c325..1286c1a2b 100644
--- a/common/vsprops/BaseProperties.vsprops
+++ b/common/vsprops/BaseProperties.vsprops
@@ -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)"
>
+# include
#endif
#include
diff --git a/pcsx2/CDVD/IsoFileTools.cpp b/pcsx2/CDVD/IsoFileTools.cpp
index c58af8171..ea5df1d96 100644
--- a/pcsx2/CDVD/IsoFileTools.cpp
+++ b/pcsx2/CDVD/IsoFileTools.cpp
@@ -17,7 +17,8 @@
#include "IsoFileTools.h"
#ifdef _WIN32
-#include
+# include
+
void *_openfile(const char *filename, int flags)
{
diff --git a/pcsx2/GS.h b/pcsx2/GS.h
index 83b31f613..e9f4277a5 100644
--- a/pcsx2/GS.h
+++ b/pcsx2/GS.h
@@ -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();
diff --git a/pcsx2/Gif.h b/pcsx2/Gif.h
index 40d7c89c6..7b0ab724f 100644
--- a/pcsx2/Gif.h
+++ b/pcsx2/Gif.h
@@ -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];
diff --git a/pcsx2/HostGui.h b/pcsx2/HostGui.h
index eb19e043f..e663e10c7 100644
--- a/pcsx2/HostGui.h
+++ b/pcsx2/HostGui.h
@@ -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);
diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp
index 7d941fc56..266690425 100644
--- a/pcsx2/MTGS.cpp
+++ b/pcsx2/MTGS.cpp
@@ -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.
diff --git a/pcsx2/PluginManager.cpp b/pcsx2/PluginManager.cpp
index 495d26dfc..f5a355c1f 100644
--- a/pcsx2/PluginManager.cpp
+++ b/pcsx2/PluginManager.cpp
@@ -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 ); }
};
diff --git a/pcsx2/Plugins.h b/pcsx2/Plugins.h
index aa91c8af5..a2e372263 100644
--- a/pcsx2/Plugins.h
+++ b/pcsx2/Plugins.h
@@ -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
// --------------------------------------------------------------------------------------
diff --git a/pcsx2/R5900Exceptions.h b/pcsx2/R5900Exceptions.h
index 24586fd21..1af6d48de 100644
--- a/pcsx2/R5900Exceptions.h
+++ b/pcsx2/R5900Exceptions.h
@@ -47,7 +47,7 @@ namespace R5900Exception
void Init( const char*msg )
{
- m_message = wxString::FromUTF8( msg );
+ m_message = fromUTF8( msg );
cpuState = cpuRegs;
}
};
diff --git a/pcsx2/RecoverySystem.cpp b/pcsx2/RecoverySystem.cpp
index 2b73c16ef..561df0427 100644
--- a/pcsx2/RecoverySystem.cpp
+++ b/pcsx2/RecoverySystem.cpp
@@ -13,57 +13,98 @@
* If not, see .
*/
-
#include "PrecompiledHeader.h"
#include "App.h"
#include "HostGui.h"
+
#include "zlib/zlib.h"
+
static SafeArray 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 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( 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;
- }*/
-}
-
diff --git a/pcsx2/SaveState.cpp b/pcsx2/SaveState.cpp
index 54a06ef30..011167a8b 100644
--- a/pcsx2/SaveState.cpp
+++ b/pcsx2/SaveState.cpp
@@ -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
// -----------------------------------------------
diff --git a/pcsx2/SaveState.h b/pcsx2/SaveState.h
index e0b5c062e..0c5f31e54 100644
--- a/pcsx2/SaveState.h
+++ b/pcsx2/SaveState.h
@@ -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();
diff --git a/pcsx2/System.cpp b/pcsx2/System.cpp
index 5c2b73487..0aa4cad53 100644
--- a/pcsx2/System.cpp
+++ b/pcsx2/System.cpp
@@ -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
diff --git a/pcsx2/System/SysThreads.cpp b/pcsx2/System/SysThreads.cpp
index e05ee901c..7c977a1cd 100644
--- a/pcsx2/System/SysThreads.cpp
+++ b/pcsx2/System/SysThreads.cpp
@@ -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();
}
diff --git a/pcsx2/System/SysThreads.h b/pcsx2/System/SysThreads.h
index 51204cfc3..241495a7f 100644
--- a/pcsx2/System/SysThreads.h
+++ b/pcsx2/System/SysThreads.h
@@ -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();
};
diff --git a/pcsx2/VUmicroMem.cpp b/pcsx2/VUmicroMem.cpp
index a89dd44ca..558d82813 100644
--- a/pcsx2/VUmicroMem.cpp
+++ b/pcsx2/VUmicroMem.cpp
@@ -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;
diff --git a/pcsx2/gui/App.h b/pcsx2/gui/App.h
index 0ccfc9ace..5553e2cf0 100644
--- a/pcsx2/gui/App.h
+++ b/pcsx2/gui/App.h
@@ -21,6 +21,8 @@
#include
#include
+#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();
diff --git a/pcsx2/gui/AppCoreThread.cpp b/pcsx2/gui/AppCoreThread.cpp
index e53b2fdd8..ecac3008d 100644
--- a/pcsx2/gui/AppCoreThread.cpp
+++ b/pcsx2/gui/AppCoreThread.cpp
@@ -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();
}
diff --git a/pcsx2/gui/AppInit.cpp b/pcsx2/gui/AppInit.cpp
index 4da439576..1e98a3c80 100644
--- a/pcsx2/gui/AppInit.cpp
+++ b/pcsx2/gui/AppInit.cpp
@@ -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 )
diff --git a/pcsx2/gui/AppMain.cpp b/pcsx2/gui/AppMain.cpp
index 025266bc7..cde9bdcb2 100644
--- a/pcsx2/gui/AppMain.cpp
+++ b/pcsx2/gui/AppMain.cpp
@@ -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();
diff --git a/pcsx2/gui/ConsoleLogger.cpp b/pcsx2/gui/ConsoleLogger.cpp
index 5836c39f5..387505685 100644
--- a/pcsx2/gui/ConsoleLogger.cpp
+++ b/pcsx2/gui/ConsoleLogger.cpp
@@ -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 );
}
}
diff --git a/pcsx2/gui/Dialogs/AboutBoxDialog.cpp b/pcsx2/gui/Dialogs/AboutBoxDialog.cpp
index ab0f695fc..99f98fde8 100644
--- a/pcsx2/gui/Dialogs/AboutBoxDialog.cpp
+++ b/pcsx2/gui/Dialogs/AboutBoxDialog.cpp
@@ -51,7 +51,7 @@ Dialogs::AboutBoxDialog::AboutBoxDialog( wxWindow* parent, int id ):
m_bitmap_dualshock( this, wxID_ANY, wxBitmap( EmbeddedImage().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)"
diff --git a/pcsx2/gui/Dialogs/ConfigurationDialog.cpp b/pcsx2/gui/Dialogs/ConfigurationDialog.cpp
index 8f7dfd1a5..1254f81bd 100644
--- a/pcsx2/gui/Dialogs/ConfigurationDialog.cpp
+++ b/pcsx2/gui/Dialogs/ConfigurationDialog.cpp
@@ -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 ),
diff --git a/pcsx2/gui/GlobalCommands.cpp b/pcsx2/gui/GlobalCommands.cpp
index ec872aaeb..cb53f1535 100644
--- a/pcsx2/gui/GlobalCommands.cpp
+++ b/pcsx2/gui/GlobalCommands.cpp
@@ -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
diff --git a/pcsx2/gui/MainFrame.cpp b/pcsx2/gui/MainFrame.cpp
index dd36f1f5d..0eba2da51 100644
--- a/pcsx2/gui/MainFrame.cpp
+++ b/pcsx2/gui/MainFrame.cpp
@@ -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
{
diff --git a/pcsx2/gui/Panels/PluginSelectorPanel.cpp b/pcsx2/gui/Panels/PluginSelectorPanel.cpp
index f601a3f94..8f4e3a243 100644
--- a/pcsx2/gui/Panels/PluginSelectorPanel.cpp
+++ b/pcsx2/gui/Panels/PluginSelectorPanel.cpp
@@ -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.
}
diff --git a/pcsx2/gui/Plugins.cpp b/pcsx2/gui/Plugins.cpp
index aa71caaa4..7241424f1 100644
--- a/pcsx2/gui/Plugins.cpp
+++ b/pcsx2/gui/Plugins.cpp
@@ -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 _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 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 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;
}
diff --git a/pcsx2/gui/Saveslots.cpp b/pcsx2/gui/Saveslots.cpp
index 386e374f3..1a6e72990 100644
--- a/pcsx2/gui/Saveslots.cpp
+++ b/pcsx2/gui/Saveslots.cpp
@@ -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 ) );
}
diff --git a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj
index 6f11e8097..13561b2b0 100644
--- a/pcsx2/windows/VCprojects/pcsx2_2008.vcproj
+++ b/pcsx2/windows/VCprojects/pcsx2_2008.vcproj
@@ -1804,6 +1804,10 @@
RelativePath="..\..\gui\IniInterface.cpp"
>
+
+
diff --git a/pcsx2/windows/WinConsolePipe.cpp b/pcsx2/windows/WinConsolePipe.cpp
index bcadd4fbf..892a4874a 100644
--- a/pcsx2/windows/WinConsolePipe.cpp
+++ b/pcsx2/windows/WinConsolePipe.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 )
{
diff --git a/pcsx2/x86/iVU1micro.cpp b/pcsx2/x86/iVU1micro.cpp
index 99055bf18..f76c2c04f 100644
--- a/pcsx2/x86/iVU1micro.cpp
+++ b/pcsx2/x86/iVU1micro.cpp
@@ -30,7 +30,6 @@ int mVUdebugNow = 0;
#ifdef DEBUG_COMPARE
-#include
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
-#endif
__aligned16 u8 backVUregs[sizeof(VURegs)];
__aligned16 u8 cmpVUregs [sizeof(VURegs)];
diff --git a/plugins/spu2-x/src/Mixer.cpp b/plugins/spu2-x/src/Mixer.cpp
index 244fa2a47..f2fcd24fd 100644
--- a/plugins/spu2-x/src/Mixer.cpp
+++ b/plugins/spu2-x/src/Mixer.cpp
@@ -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 );
}
diff --git a/plugins/spu2-x/src/ReadInput.cpp b/plugins/spu2-x/src/ReadInput.cpp
index a69fd0ac9..09129a772 100644
--- a/plugins/spu2-x/src/ReadInput.cpp
+++ b/plugins/spu2-x/src/ReadInput.cpp
@@ -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
diff --git a/plugins/spu2-x/src/spu2sys.cpp b/plugins/spu2-x/src/spu2sys.cpp
index 581deccda..c2f24c045 100644
--- a/plugins/spu2-x/src/spu2sys.cpp
+++ b/plugins/spu2-x/src/spu2sys.cpp
@@ -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
{