Core Stop and Start: Added alternative separate thread timer/loop based waiting, instead of same thread loop waiting. You can try it with the SETUP_TIMER_WAITING option in Setup.h.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@2375 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
John Peterson 2009-02-22 21:16:12 +00:00
parent 769160dfbd
commit 927815bc9b
16 changed files with 367 additions and 31 deletions

View File

@ -49,6 +49,15 @@
// Build with playback rerecording options
//#define SETUP_AVOID_OPENGL_SCREEN_MESSAGE_HANG
// Use a timer to wait for threads for stop instead of WaitForEternity()
/* I tried that this worked with these options
SETUP_FREE_VIDEO_PLUGIN_ON_BOOT
SETUP_DONT_FREE_PLUGIN_ON_STOP
then the Confirm on Close message box doesn't hang, and we have a controlled Shutdown process
without any hanged threads. The downside is a few error messages in the ShutDown() of the
OpenGL plugin, so I still need FreeLibrary() to clean it, even with this option. */
//#define SETUP_TIMER_WAITING
// Build with playback rerecording options
//#define RERECORDING

View File

@ -15,7 +15,16 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include "Setup.h"
#include "Thread.h"
// -----------------------------------------
#ifdef SETUP_TIMER_WAITING
// -----------------
#include <windows.h>
#include "ConsoleWindow.h"
EventCallBack FunctionPointer[10];
#endif
// ------------------------
#define THREAD_DEBUG 1
@ -99,9 +108,18 @@ void Thread::SetCurrentThreadAffinity(int mask)
}
//////////////////////////////////////////////////////////////////////////////////////////
// Regular same thread loop based waiting
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Event::Event()
{
m_hEvent = 0;
#ifdef SETUP_TIMER_WAITING
DoneWaiting = false;
StartWait = false;
hTimer = NULL;
hTimerQueue = NULL;
#endif
}
void Event::Init()
@ -115,22 +133,125 @@ void Event::Shutdown()
m_hEvent = 0;
}
void Event::Set()
{
SetEvent(m_hEvent);
}
void Event::Wait()
{
WaitForSingleObject(m_hEvent, INFINITE);
}
/////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
/* Separate thread timer based waiting, instead of same thread loop waiting. The downside with this
is that it's less convenient to use because we can't stall any threads with a loop. The positive
is that we don't cause these incredibly annoying WaitForEternity() hangings. */
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
#ifdef SETUP_TIMER_WAITING
/* I could not figure out how to place this in the class to, CreateTimerQueueTimer() would complain
about some kind of type casting, anyone have any ideas about how to do it? */
VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
if (lpParam == NULL)
{
Console::Print("TimerRoutine lpParam is NULL\n");
}
else
{
// lpParam points to the argument; in this case it is an int
//Console::Print("Timer[%i] will call back\n", *(int*)lpParam);
}
// Call back
int Id = *(int*)lpParam;
if (FunctionPointer[Id]) FunctionPointer[Id]();
}
// Create a timer that will call back to the calling function
bool Event::TimerWait(EventCallBack WaitCB, int _Id, bool OptCondition)
{
Id = _Id;
//Console::Print("TimerWait[%i]: %i %i %i\n", Id, StartWait, DoneWaiting, OptCondition);
FunctionPointer[Id] = WaitCB;
// This means we are done waiting, so we wont call back again, and we also reset the variables for this Event
if (DoneWaiting && OptCondition)
{
StartWait = false;
DoneWaiting = false;
FunctionPointer[Id] = NULL;
// Delete all timers in the timer queue.
if (!DeleteTimerQueue(hTimerQueue))
Console::Print("DeleteTimerQueue failed (%d)\n", GetLastError());
hTimer = NULL;
hTimerQueue = NULL;
return true;
}
// Else start a new callback timer
StartWait = true;
// Create the timer queue if needed
if (!hTimerQueue)
{
hTimerQueue = CreateTimerQueue();
if (NULL == hTimerQueue)
{
Console::Print("CreateTimerQueue failed (%d)\n", GetLastError());
return false;
}
}
// Set a timer to call the timer routine in 10 seconds.
if (!CreateTimerQueueTimer( &hTimer, hTimerQueue,
(WAITORTIMERCALLBACK)TimerRoutine, &Id , 10, 0, 0))
{
Console::Print("CreateTimerQueueTimer failed (%d)\n", GetLastError());
return false;
}
return false;
}
// Check if we are done or not
bool Event::DoneWait()
{
if (StartWait && DoneWaiting)
return true;
else
return false;
}
// Tells the timer that we are done waiting
void Event::SetTimer()
{
// We can not be done before we have started waiting
if (StartWait) DoneWaiting = true;
}
#endif
////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
// Supporting functions
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
void SleepCurrentThread(int ms)
{
Sleep(ms);
}
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // must be 0x1000
@ -188,6 +309,8 @@ LONG SyncInterlockedExchange(LONG *Dest, LONG Val)
{
return InterlockedExchange(Dest, Val);
}
////////////////////////////////////////
#else // !WIN32, so must be POSIX threads

View File

@ -33,6 +33,15 @@
#include "Common.h"
// -----------------------------------------
#ifdef SETUP_TIMER_WAITING
// -----------------
typedef void (*EventCallBack)(void);
#endif
// ----------------------
namespace Common
{
class CriticalSection
@ -78,6 +87,7 @@ private:
};
class Event
{
public:
@ -89,6 +99,20 @@ public:
void Set();
void Wait();
// -----------------------------------------
#ifdef SETUP_TIMER_WAITING
// -----------------
bool TimerWait(EventCallBack WaitCB, int Id = 0, bool OptCondition = true);
bool DoneWait();
void SetTimer();
bool DoneWaiting;
bool StartWait;
int Id;
HANDLE hTimer;
HANDLE hTimerQueue;
#endif
// ---------------------------
private:
#ifdef _WIN32
HANDLE m_hEvent;

View File

@ -24,8 +24,8 @@
#else
#endif
#include "Thread.h" // Common
#include "Setup.h"
#include "Setup.h" // Common
#include "Thread.h"
#include "Timer.h"
#include "Common.h"
#include "ConsoleWindow.h"
@ -110,6 +110,19 @@ SCoreStartupParameter g_CoreStartupParameter;
// This event is set when the emuthread starts.
Common::Event emuThreadGoing;
Common::Event cpuRunloopQuit;
// -----------------------------------------
#ifdef SETUP_TIMER_WAITING
// -----------------
bool VideoThreadRunning = false;
bool StopUpToVideoDone = false;
bool EmuThreadReachedEnd = false;
bool StopReachedEnd = false;
static Common::Event VideoThreadEvent;
static Common::Event VideoThreadEvent2;
void EmuThreadEnd();
#endif
// ---------------------------
//////////////////////////////////////
@ -162,9 +175,24 @@ void ReconnectPad()
void ReconnectWiimote()
{
// This seems to be a hack that just sets some IPC registers to zero. Dubious.
/* JP: Yes, it's basically nothing right now, I could not figure out how to reset the Wiimote
for reconnection */
HW::InitWiimote();
Console::Print("ReconnectWiimote()\n");
}
// -----------------------------------------
#ifdef SETUP_TIMER_WAITING
// -----------------
void VideoThreadEnd()
{
VideoThreadRunning = false;
VideoThreadEvent.SetTimer();
VideoThreadEvent2.SetTimer();
//Console::Print("VideoThreadEnd\n");
}
#endif
// ---------------------------
/////////////////////////////////////
@ -201,6 +229,9 @@ bool Init()
g_EmuThread = new Common::Thread(EmuThread, NULL);
emuThreadGoing.Wait();
emuThreadGoing.Shutdown();
#ifdef SETUP_TIMER_WAITING
VideoThreadRunning = true;
#endif
// All right, the event is set and killed. We are now running.
Host_SetWaitCursor(false);
@ -208,17 +239,29 @@ bool Init()
}
// Called from GUI thread or VI thread (why VI??? That must be bad. Window close? TODO: Investigate.)
void Stop() // - Hammertime!
// JP: No, when you press Stop this is run from the Main Thread it seems
// - Hammertime!
void Stop()
{
const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter;
#ifdef SETUP_TIMER_WAITING
if (!StopUpToVideoDone)
{
Console::Print("--------------------------------------------------------------\n");
Console::Print("Stop [Main Thread]: Shutting down...\n");
// Reset variables
StopReachedEnd = false;
EmuThreadReachedEnd = false;
#endif
Host_SetWaitCursor(true);
if (PowerPC::GetState() == PowerPC::CPU_POWERDOWN)
return;
// stop the CPU
// Stop the CPU
PowerPC::Stop();
CCPU::StepOpcode(); //kick it if it's waiting
CCPU::StepOpcode(); // Kick it if it's waiting
cpuRunloopQuit.Wait();
cpuRunloopQuit.Shutdown();
@ -229,10 +272,22 @@ void Stop() // - Hammertime!
// If dual core mode, the CPU thread should immediately exit here.
Console::Print("Stop [Main Thread]: Wait for Video Loop...\n");
// Should be moved inside the plugin.
if (_CoreParameter.bUseDualCore)
CPluginManager::GetInstance().GetVideo()->Video_ExitLoop();
#ifdef SETUP_TIMER_WAITING
StopUpToVideoDone = true;
}
// Call this back
//if (!VideoThreadEvent.TimerWait(Stop, 1, EmuThreadReachedEnd) || !EmuThreadReachedEnd) return;
if (!VideoThreadEvent.TimerWait(Stop, 1)) return;
//Console::Print("Stop() will continue\n");
#endif
/* Video_EnterLoop() should now exit so that EmuThread() will continue concurrently with the rest
of the commands in this function */
@ -240,24 +295,37 @@ void Stop() // - Hammertime!
And since we have no while(GetMessage()) loop we can't wait for this to happen, or say exactly when
the loop has ended */
#ifdef _WIN32
PostMessage((HWND)g_pWindowHandle, WM_QUIT, 0, 0);
//PostMessage((HWND)g_pWindowHandle, WM_QUIT, 0, 0);
#endif
// Close the trace file
Core::StopTrace();
#ifndef SETUP_TIMER_WAITING // This hangs
LogManager::Shutdown();
#endif
// Update mouse pointer
Host_SetWaitCursor(false);
#ifdef SETUP_AVOID_CHILD_WINDOW_RENDERING_HANG
/* This may hang when we are rendering to a child window, but currently it doesn't, at least
not on my system, but I'll leave this option for a while anyway */
if (GetParent((HWND)g_pWindowHandle) == NULL)
#endif
#ifndef SETUP_TIMER_WAITING // This is moved
delete g_EmuThread; // Wait for emuthread to close.
g_EmuThread = 0;
#endif
#ifdef SETUP_TIMER_WAITING
Host_UpdateGUI();
StopUpToVideoDone = false;
StopReachedEnd = true;
//Console::Print("Stop() reached the end\n");
if (EmuThreadReachedEnd) Console::Print("--------------------------------------------------------------\n");
#endif
}
//////////////////////////////////////////////////////////////////////////////////////////
// Create the CPU thread
// Create the CPU thread. For use with Single Core mode only.
// ---------------
THREAD_RETURN CpuThread(void *pArg)
{
@ -465,7 +533,35 @@ THREAD_RETURN EmuThread(void *pArg)
// I bet that many of our stopping problems come from this loop not properly exiting.
Plugins.GetVideo()->Video_EnterLoop();
}
#ifdef SETUP_TIMER_WAITING
VideoThreadEvent2.TimerWait(EmuThreadEnd, 2);
//Console::Print("Video loop [Video Thread]: Stopped\n");
return 0;
}
void EmuThreadEnd()
{
CPluginManager &Plugins = CPluginManager::GetInstance();
const SCoreStartupParameter& _CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter;
//Console::Print("Video loop [Video Thread]: EmuThreadEnd [StopEnd:%i]\n", StopReachedEnd);
//if (!VideoThreadEvent2.TimerWait(EmuThreadEnd, 2)) return;
if (!VideoThreadEvent2.TimerWait(EmuThreadEnd, 2, StopReachedEnd) || !StopReachedEnd)
{
Console::Print("Stop [Video Thread]: Waiting for Stop() and Video Loop to end...\n");
return;
}
//Console::Print("EmuThreadEnd() will continue\n");
/* There will be a few problems with the OpenGL ShutDown() after this, for example the "Release
Device Context Failed" error message */
#endif
Console::Print("Stop [Video Thread]: Stop() and Video Loop Ended\n");
Console::Print("Stop [Video Thread]: Shutting down HW and Plugins\n");
// We have now exited the Video Loop and will shut down
// does this comment still apply?
@ -484,6 +580,7 @@ THREAD_RETURN EmuThread(void *pArg)
if (g_pUpdateFPSDisplay != NULL)
g_pUpdateFPSDisplay("Stopping...");
#ifndef SETUP_TIMER_WAITING
if (cpuThread)
{
// There is a CPU thread - join it.
@ -491,10 +588,11 @@ THREAD_RETURN EmuThread(void *pArg)
// Returns after game exited
cpuThread = NULL;
}
#endif
// The hardware is uninitialized
g_bHwInit = false;
HW::Shutdown();
Plugins.ShutdownPlugins();
@ -503,8 +601,18 @@ THREAD_RETURN EmuThread(void *pArg)
// so we can restart the plugins (or load new ones) for the next game.
if (_CoreParameter.hMainWindow == g_pWindowHandle)
Host_UpdateMainFrame();
#ifdef SETUP_TIMER_WAITING
EmuThreadReachedEnd = true;
//Console::Print("EmuThread() reached the end\n");
Host_UpdateGUI();
Console::Print("Stop [Video Thread]: Done\n");
if (StopReachedEnd) Console::Print("--------------------------------------------------------------\n");
delete g_EmuThread; // Wait for emuthread to close.
g_EmuThread = 0;
#endif
#ifndef SETUP_TIMER_WAITING
return 0;
#endif
}

View File

@ -61,7 +61,7 @@ namespace Core
bool GetRealWiimote();
void ReconnectWiimote();
void ReconnectPad();
extern bool bReadTrace;
extern bool bWriteTrace;
@ -73,7 +73,17 @@ namespace Core
void SetBlockStart(u32 addr);
void StopTrace();
// -----------------------------------------
#ifdef SETUP_TIMER_WAITING
// -----------------
// Thread shutdown
void VideoThreadEnd();
#endif
// ---------------------------
// -----------------------------------------
#ifdef RERECORDING
// -----------------
void FrameUpdate();
void FrameAdvance();
void FrameStepOnOff();
@ -84,6 +94,7 @@ namespace Core
extern int g_FrameCounter;
extern bool g_FrameStep;
#endif
// ---------------------------
} // namespace

View File

@ -47,6 +47,9 @@ void Host_SetDebugMode(bool enable);
void Host_SetWaitCursor(bool enable);
void Host_UpdateStatusBar(const char* _pText, int Filed = 0);
#ifdef SETUP_TIMER_WAITING
void Host_UpdateGUI();
#endif
void Host_SysMessage(const char *fmt, ...);
void Host_SetWiiMoteConnectionState(int _State);

View File

@ -194,15 +194,29 @@ int abc = 0;
Core::ReconnectWiimote();
return 0;
// -----------------------------------------
#ifdef RERECORDING
case INPUT_FRAME_COUNTER:
// Wind back the frame counter after a save state has been loaded
Core::WindBack((int)lParam);
return 0;
// -----------------
case INPUT_FRAME_COUNTER:
// Wind back the frame counter after a save state has been loaded
Core::WindBack((int)lParam);
return 0;
#endif
// -----------------------------
// -----------------------------------------
#ifdef SETUP_TIMER_WAITING
// -----------------
case OPENGL_VIDEO_STOP:
// The Video thread has been shut down
Core::VideoThreadEnd();
//Console::Print("OPENGL_VIDEO_STOP\n");
return 0;
#endif
// -----------------------------
}
break;
//default:
// return wxPanel::MSWWindowProc(nMsg, wParam, lParam);
}

View File

@ -71,6 +71,7 @@ class CFrame : public wxFrame
void InitBitmaps();
void DoStop();
bool bRenderToMain;
void UpdateGUI();
// ---------------------------------------
// Wiimote leds
@ -220,7 +221,6 @@ class CFrame : public wxFrame
wxMenuItem* m_pMenuItemSave;
wxToolBarToolBase* m_pToolPlay;
void UpdateGUI();
void BootGame();
// Double click and mouse move options

View File

@ -31,16 +31,17 @@
#include <sys/param.h>
#endif
#include "Globals.h" // Core
#include "Host.h"
#include "Common.h" // Common
#include "CPUDetect.h"
#include "IniFile.h"
#include "FileUtil.h"
#include "ConsoleWindow.h"
#include "Setup.h"
#include "Main.h" // Local
#include "Host.h" // Core
#include "Globals.h" // Local
#include "Main.h"
#include "Frame.h"
#include "ConfigManager.h"
#include "CodeWindow.h"
@ -458,6 +459,13 @@ void Host_UpdateStatusBar(const char* _pText, int Field)
wxPostEvent(main_frame, event);
}
#ifdef SETUP_TIMER_WAITING
void Host_UpdateGUI()
{
main_frame->UpdateGUI();
}
#endif
void Host_SysMessage(const char *fmt, ...)
{
va_list list;

View File

@ -17,10 +17,14 @@
#include <string.h>
#include "Setup.h"
#ifdef SETUP_TIMER_WAITING
#include "../../../Plugins/Plugin_VideoOGL/Src/OS/Win32.h"
#endif
#include "MemoryUtil.h"
#include "Thread.h"
#include "OpcodeDecoding.h"
#include "Setup.h"
#include "ConsoleWindow.h"
#include "Fifo.h"
@ -96,8 +100,18 @@ void Video_SendFifoData(u8* _uData, u32 len)
void Fifo_ExitLoop()
{
fifoStateRun = false;
fifo_exit_event.Wait();
fifo_exit_event.Shutdown();
#ifndef SETUP_TIMER_WAITING
fifo_exit_event.Wait();
fifo_exit_event.Shutdown();
#else
//Console::Print("Video: Fifo_ExitLoop: Done:%i\n", fifo_exit_event.DoneWait());
if (fifo_exit_event.TimerWait(Fifo_ExitLoop))
{
//Console::Print("Video: Fifo_Shutdown: Done:%i\n\n", fifo_exit_event.DoneWait());
fifo_exit_event.Shutdown();
PostMessage(EmuWindow::GetParentWnd(), WM_USER, OPENGL_VIDEO_STOP, 0);
}
#endif
}
void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
@ -111,6 +125,7 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
#if defined(_WIN32) && !defined(SETUP_AVOID_OPENGL_SCREEN_MESSAGE_HANG)
video_initialize.pPeekMessages();
#endif
if (_fifo.CPReadWriteDistance == 0)
Common::SleepCurrentThread(1);
@ -187,4 +202,7 @@ void Fifo_EnterLoop(const SVideoInitialize &video_initialize)
}
}
fifo_exit_event.Set();
#ifdef SETUP_TIMER_WAITING
fifo_exit_event.SetTimer();
#endif
}

View File

@ -28,7 +28,8 @@ enum PLUGIN_COMM
OPENGL_WM_USER_CREATE,
NJOY_RELOAD, // Reload nJoy if DirectInput has failed
WIIMOTE_RECONNECT, // Reconnect the Wiimote if it has disconnected
INPUT_FRAME_COUNTER // Wind back the frame counter for rerecording
INPUT_FRAME_COUNTER, // Wind back the frame counter for rerecording
OPENGL_VIDEO_STOP
};
///////////////////////////////

View File

@ -19,6 +19,7 @@
#include "Config.h"
#include "IniFile.h"
#include "svnrev.h"
#include "Setup.h"
#include "Render.h"
@ -768,9 +769,11 @@ void OpenGL_Shutdown()
hRC = NULL; // Set RC To NULL
}
if (hDC && !ReleaseDC(EmuWindow::GetWnd(),hDC)) // Are We Able To Release The DC
if (hDC && !ReleaseDC(EmuWindow::GetWnd(), hDC)) // Are We Able To Release The DC
{
#ifndef SETUP_TIMER_WAITING // This fails
MessageBox(NULL,"Release Device Context Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION);
#endif
hDC = NULL; // Set DC To NULL
}
#elif defined(HAVE_X11) && HAVE_X11

View File

@ -83,7 +83,7 @@ void __Log(const char *fmt, ...)
if (pfLog != NULL && LocalLogFile)
fwrite(Msg, strlen(Msg), 1, pfLog);
Console::Print(Msg);
Console::Print(Msg);
}
@ -100,7 +100,7 @@ void __Log(int type, const char *fmt, ...)
va_end(ap);
g_VideoInitialize.pLog(Msg, FALSE);
Console::Print(Msg);
Console::Print(Msg);
}
#endif

View File

@ -148,6 +148,11 @@ HWND GetParentWnd()
return m_hParent;
}
HWND GetChildParentWnd()
{
return m_hMain;
}
LRESULT CALLBACK WndProc( HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam )
{
HDC hdc;

View File

@ -25,6 +25,7 @@ namespace EmuWindow
HWND GetWnd();
HWND GetParentWnd();
HWND GetChildParentWnd();
HWND Create(HWND hParent, HINSTANCE hInstance, const TCHAR *title);
void Show();
void Close();

View File

@ -299,7 +299,9 @@ void Video_Prepare(void)
void Shutdown(void)
{
Fifo_Shutdown();
TextureConverter::Shutdown();
#ifndef SETUP_TIMER_WAITING // This is not compatible, it crashes after the second Stop
TextureConverter::Shutdown();
#endif
VertexLoaderManager::Shutdown();
VertexShaderCache::Shutdown();
VertexShaderManager::Shutdown();
@ -308,8 +310,14 @@ void Shutdown(void)
VertexManager::Shutdown();
TextureMngr::Shutdown();
OpcodeDecoder_Shutdown();
Renderer::Shutdown();
#ifndef SETUP_TIMER_WAITING // This is not compatible, it may crashes after a Stop
Renderer::Shutdown();
#endif
OpenGL_Shutdown();
#ifdef SETUP_TIMER_WAITING
// Do we ever destroy the window?
EmuWindow::Close();
#endif
}