SaveState: Simplify and convert to std::thread

This commit is contained in:
Connor McLaughlin 2021-09-18 13:09:31 +10:00 committed by Kojin
parent 3fdab1222b
commit 17c049d7e3
10 changed files with 189 additions and 416 deletions

View File

@ -1265,15 +1265,6 @@ set(pcsx2UtilitiesSources
set(pcsx2UtilitiesHeaders
Utilities/AsciiFile.h)
# Zip tools utilies sources
set(pcsx2ZipToolsSources
ZipTools/thread_gzip.cpp
ZipTools/thread_lzma.cpp)
# Zip tools utilies headers
set(pcsx2ZipToolsHeaders
ZipTools/ThreadedZipTools.h)
# Windows sources
set(pcsx2WindowsSources
@ -1408,9 +1399,7 @@ target_sources(PCSX2 PRIVATE
${pcsx2SystemSources}
${pcsx2SystemHeaders}
${pcsx2UtilitiesSources}
${pcsx2UtilitiesHeaders}
${pcsx2ZipToolsSources}
${pcsx2ZipToolsHeaders})
${pcsx2UtilitiesHeaders})
# platform sources
# Linux

View File

@ -28,8 +28,8 @@
#include "Elfheader.h"
#include "Counters.h"
#include "Patch.h"
#include "System/SysThreads.h"
#include "ZipTools/ThreadedZipTools.h"
#include "common/pxStreams.h"
#include "common/SafeArray.inl"
#include "SPU2/spu2.h"
@ -44,6 +44,14 @@
#include "gui/ConsoleLogger.h"
#ifndef PCSX2_CORE
#include "gui/App.h"
#endif
#include "common/pxStreams.h"
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
using namespace R5900;
@ -663,7 +671,7 @@ static void CheckVersion(pxInputStream& thr)
void SaveState_DownloadState(ArchiveEntryList* destlist)
{
if (!SysHasValidState())
if (!GetCoreThread().HasActiveMachine())
throw Exception::RuntimeError()
.SetDiagMsg(L"SysExecEvent_DownloadState: Cannot freeze/download an invalid VM state!")
.SetUserMsg(_("There is no active virtual machine state to download or save."));
@ -691,54 +699,13 @@ void SaveState_DownloadState(ArchiveEntryList* destlist)
// --------------------------------------------------------------------------------------
// CompressThread_VmState
// --------------------------------------------------------------------------------------
class VmStateCompressThread : public BaseCompressThread
static void ZipStateToDiskOnThread(std::unique_ptr<ArchiveEntryList> srclist, std::unique_ptr<wxFFileOutputStream> outbase, wxString filename, wxString tempfile)
{
typedef BaseCompressThread _parent;
#ifndef PCSX2_CORE
wxGetApp().StartPendingSave();
#endif
protected:
ScopedLock m_lock_Compress;
public:
VmStateCompressThread()
{
m_lock_Compress.Assign(mtx_CompressToDisk);
}
virtual ~VmStateCompressThread() = default;
protected:
void OnStartInThread()
{
_parent::OnStartInThread();
m_lock_Compress.Acquire();
}
void OnCleanupInThread()
{
m_lock_Compress.Release();
_parent::OnCleanupInThread();
}
};
void SaveState_ZipToDisk(ArchiveEntryList* srclist, const wxString& filename)
{
// Provisionals for scoped cleanup, in case of exception:
std::unique_ptr<ArchiveEntryList> elist(srclist);
wxString tempfile(filename + L".tmp");
wxFFileOutputStream* woot = new wxFFileOutputStream(tempfile);
if (!woot->IsOk())
throw Exception::CannotCreateStream(tempfile);
// Scheduler hint (yield) -- creating and saving the file is low priority compared to
// the emulator/vm thread. Sleeping the executor thread briefly before doing file
// transactions should help reduce overhead. --air
pxYield(4);
// Write the version and screenshot:
std::unique_ptr<pxOutputStream> out(new pxOutputStream(tempfile, new wxZipOutputStream(woot)));
std::unique_ptr<pxOutputStream> out(new pxOutputStream(tempfile, new wxZipOutputStream(outbase.release())));
wxZipOutputStream* gzfp = (wxZipOutputStream*)out->GetWxStreamBase();
{
@ -749,8 +716,9 @@ void SaveState_ZipToDisk(ArchiveEntryList* srclist, const wxString& filename)
gzfp->CloseEntry();
}
#if 0
// This was never actually initialized...
std::unique_ptr<wxImage> m_screenshot;
if (m_screenshot)
{
wxZipEntry* vent = new wxZipEntry(EntryFilename_Screenshot);
@ -759,16 +727,61 @@ void SaveState_ZipToDisk(ArchiveEntryList* srclist, const wxString& filename)
m_screenshot->SaveFile(*gzfp, wxBITMAP_TYPE_JPEG);
gzfp->CloseEntry();
}
#endif
(*new VmStateCompressThread())
.SetSource(srclist)
.SetOutStream(out.get())
.SetFinishedPath(filename)
.Start();
uint listlen = srclist->GetLength();
for (uint i = 0; i < listlen; ++i)
{
const ArchiveEntry& entry = (*srclist)[i];
if (!entry.GetDataSize())
continue;
// No errors? Release cleanup handlers:
elist.release();
out.release();
gzfp->PutNextEntry(entry.GetFilename());
static const uint BlockSize = 0x64000;
uint curidx = 0;
do
{
uint thisBlockSize = std::min(BlockSize, entry.GetDataSize() - curidx);
gzfp->Write(srclist->GetPtr(entry.GetDataIndex() + curidx), thisBlockSize);
curidx += thisBlockSize;
} while (curidx < entry.GetDataSize());
gzfp->CloseEntry();
}
gzfp->Close();
if (!wxRenameFile(out->GetStreamName(), filename, true))
{
Console.Error("Failed to rename save state '%s' to '%s'", static_cast<const char*>(out->GetStreamName().c_str()), static_cast<const char*>(filename.c_str()));
#ifndef PCSX2_CORE
Msgbox::Alert(_("The savestate was not properly saved. The temporary file was created successfully but could not be moved to its final resting place."));
#endif
}
else
{
Console.WriteLn("(gzipThread) Data saved to disk without error.");
}
#ifndef PCSX2_CORE
wxGetApp().ClearPendingSave();
#endif
}
void SaveState_ZipToDisk(ArchiveEntryList* srclist, const wxString& filename)
{
// Provisionals for scoped cleanup, in case of exception:
std::unique_ptr<ArchiveEntryList> elist(srclist);
wxString tempfile(filename + L".tmp");
std::unique_ptr<wxFFileOutputStream> out = std::make_unique<wxFFileOutputStream>(tempfile);
if (!out->IsOk())
throw Exception::CannotCreateStream(tempfile);
std::thread threaded_save(ZipStateToDiskOnThread, std::move(elist), std::move(out), filename, tempfile);
threaded_save.detach();
}
void SaveState_UnzipFromDisk(const wxString& filename)

View File

@ -179,6 +179,124 @@ protected:
void InputRecordingFreeze();
};
// --------------------------------------------------------------------------------------
// ArchiveEntry
// --------------------------------------------------------------------------------------
class ArchiveEntry
{
protected:
wxString m_filename;
uptr m_dataidx;
size_t m_datasize;
public:
ArchiveEntry(const wxString& filename = wxEmptyString)
: m_filename(filename)
{
m_dataidx = 0;
m_datasize = 0;
}
virtual ~ArchiveEntry() = default;
ArchiveEntry& SetDataIndex(uptr idx)
{
m_dataidx = idx;
return *this;
}
ArchiveEntry& SetDataSize(size_t size)
{
m_datasize = size;
return *this;
}
wxString GetFilename() const
{
return m_filename;
}
uptr GetDataIndex() const
{
return m_dataidx;
}
uint GetDataSize() const
{
return m_datasize;
}
};
typedef SafeArray< u8 > ArchiveDataBuffer;
// --------------------------------------------------------------------------------------
// ArchiveEntryList
// --------------------------------------------------------------------------------------
class ArchiveEntryList
{
DeclareNoncopyableObject(ArchiveEntryList);
protected:
std::vector<ArchiveEntry> m_list;
std::unique_ptr<ArchiveDataBuffer> m_data;
public:
virtual ~ArchiveEntryList() = default;
ArchiveEntryList() {}
ArchiveEntryList(ArchiveDataBuffer* data)
: m_data(data)
{
}
ArchiveEntryList(ArchiveDataBuffer& data)
: m_data(&data)
{
}
const VmStateBuffer* GetBuffer() const
{
return m_data.get();
}
VmStateBuffer* GetBuffer()
{
return m_data.get();
}
u8* GetPtr(uint idx)
{
return &(*m_data)[idx];
}
const u8* GetPtr(uint idx) const
{
return &(*m_data)[idx];
}
ArchiveEntryList& Add(const ArchiveEntry& src)
{
m_list.push_back(src);
return *this;
}
size_t GetLength() const
{
return m_list.size();
}
ArchiveEntry& operator[](uint idx)
{
return m_list[idx];
}
const ArchiveEntry& operator[](uint idx) const
{
return m_list[idx];
}
};
// --------------------------------------------------------------------------------------
// Saving and Loading Specialized Implementations...
// --------------------------------------------------------------------------------------

View File

@ -1,10 +0,0 @@
-------------------
ZipTools (folder)
-------------------
Contains C++ interfaces for zipping to/from various formats
(primarily gzip and 7zip).
Notice: This folder is intended to be moved to a utility folder
outside the main PCSX2 folders at a later date.

View File

@ -1,209 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "common/PersistentThread.h"
#include "common/pxStreams.h"
#include "wx/zipstrm.h"
using namespace Threading;
// --------------------------------------------------------------------------------------
// ArchiveEntry
// --------------------------------------------------------------------------------------
class ArchiveEntry
{
protected:
wxString m_filename;
uptr m_dataidx;
size_t m_datasize;
public:
ArchiveEntry( const wxString& filename=wxEmptyString )
: m_filename( filename )
{
m_dataidx = 0;
m_datasize = 0;
}
virtual ~ArchiveEntry() = default;
ArchiveEntry& SetDataIndex( uptr idx )
{
m_dataidx = idx;
return *this;
}
ArchiveEntry& SetDataSize( size_t size )
{
m_datasize = size;
return *this;
}
wxString GetFilename() const
{
return m_filename;
}
uptr GetDataIndex() const
{
return m_dataidx;
}
uint GetDataSize() const
{
return m_datasize;
}
};
typedef SafeArray< u8 > ArchiveDataBuffer;
// --------------------------------------------------------------------------------------
// ArchiveEntryList
// --------------------------------------------------------------------------------------
class ArchiveEntryList
{
DeclareNoncopyableObject( ArchiveEntryList );
protected:
std::vector<ArchiveEntry> m_list;
std::unique_ptr<ArchiveDataBuffer> m_data;
public:
virtual ~ArchiveEntryList() = default;
ArchiveEntryList() {}
ArchiveEntryList( ArchiveDataBuffer* data )
: m_data(data)
{
}
ArchiveEntryList( ArchiveDataBuffer& data )
: m_data(&data)
{
}
const VmStateBuffer* GetBuffer() const
{
return m_data.get();
}
VmStateBuffer* GetBuffer()
{
return m_data.get();
}
u8* GetPtr( uint idx )
{
return &(*m_data)[idx];
}
const u8* GetPtr( uint idx ) const
{
return &(*m_data)[idx];
}
ArchiveEntryList& Add( const ArchiveEntry& src )
{
m_list.push_back( src );
return *this;
}
size_t GetLength() const
{
return m_list.size();
}
ArchiveEntry& operator[](uint idx)
{
return m_list[idx];
}
const ArchiveEntry& operator[](uint idx) const
{
return m_list[idx];
}
};
// --------------------------------------------------------------------------------------
// BaseCompressThread
// --------------------------------------------------------------------------------------
class BaseCompressThread
: public pxThread
{
typedef pxThread _parent;
protected:
pxOutputStream* m_gzfp;
ArchiveEntryList* m_src_list;
bool m_PendingSaveFlag;
wxString m_final_filename;
public:
virtual ~BaseCompressThread();
BaseCompressThread& SetSource( ArchiveEntryList* srcdata )
{
m_src_list = srcdata;
return *this;
}
BaseCompressThread& SetSource( ArchiveEntryList& srcdata )
{
m_src_list = &srcdata;
return *this;
}
BaseCompressThread& SetOutStream( pxOutputStream* out )
{
m_gzfp = out;
return *this;
}
BaseCompressThread& SetOutStream( pxOutputStream& out )
{
m_gzfp = &out;
return *this;
}
BaseCompressThread& SetFinishedPath( const wxString& path )
{
m_final_filename = path;
return *this;
}
wxString GetStreamName() const { return m_gzfp->GetStreamName(); }
BaseCompressThread& SetTargetFilename(const wxString& filename)
{
m_final_filename = filename;
return *this;
}
protected:
BaseCompressThread()
{
m_gzfp = NULL;
m_src_list = NULL;
m_PendingSaveFlag = false;
}
void SetPendingSave();
void ExecuteTaskInThread();
void OnCleanupInThread();
};

View File

@ -1,98 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"
#include "gui/App.h"
#include "SaveState.h"
#include "ThreadedZipTools.h"
#include "common/SafeArray.inl"
#include "wx/wfstream.h"
BaseCompressThread::~BaseCompressThread()
{
try {
_parent::Cancel();
if( m_PendingSaveFlag )
{
wxGetApp().ClearPendingSave();
m_PendingSaveFlag = false;
}
}
DESTRUCTOR_CATCHALL
}
void BaseCompressThread::SetPendingSave()
{
wxGetApp().StartPendingSave();
m_PendingSaveFlag = true;
}
void BaseCompressThread::ExecuteTaskInThread()
{
// TODO : Add an API to PersistentThread for this! :) --air
//SetThreadPriority( THREAD_PRIORITY_BELOW_NORMAL );
// Notes:
// * Safeguard against corruption by writing to a temp file, and then copying the final
// result over the original.
if( !m_src_list ) return;
SetPendingSave();
Yield( 3 );
uint listlen = m_src_list->GetLength();
for( uint i=0; i<listlen; ++i )
{
const ArchiveEntry& entry = (*m_src_list)[i];
if (!entry.GetDataSize()) continue;
wxArchiveOutputStream& woot = *(wxArchiveOutputStream*)m_gzfp->GetWxStreamBase();
woot.PutNextEntry( entry.GetFilename() );
static const uint BlockSize = 0x64000;
uint curidx = 0;
do {
uint thisBlockSize = std::min( BlockSize, entry.GetDataSize() - curidx );
m_gzfp->Write(m_src_list->GetPtr( entry.GetDataIndex() + curidx ), thisBlockSize);
curidx += thisBlockSize;
Yield( 2 );
} while( curidx < entry.GetDataSize() );
woot.CloseEntry();
}
m_gzfp->Close();
if( !wxRenameFile( m_gzfp->GetStreamName(), m_final_filename, true ) )
throw Exception::BadStream( m_final_filename )
.SetDiagMsg(L"Failed to move or copy the temporary archive to the destination filename.")
.SetUserMsg(_("The savestate was not properly saved. The temporary file was created successfully but could not be moved to its final resting place."));
Console.WriteLn( "(gzipThread) Data saved to disk without error." );
}
void BaseCompressThread::OnCleanupInThread()
{
_parent::OnCleanupInThread();
wxGetApp().DeleteThread( this );
safe_delete(m_gzfp);
safe_delete(m_src_list);
}

View File

@ -1,17 +0,0 @@
/* PCSX2 - PS2 Emulator for PCs
* Copyright (C) 2002-2010 PCSX2 Dev Team
*
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include "PrecompiledHeader.h"

View File

@ -21,7 +21,6 @@
#include "SaveState.h"
#include "VUmicro.h"
#include "ZipTools/ThreadedZipTools.h"
#include "common/pxStreams.h"
#include "SPU2/spu2.h"
#include "USB/USB.h"

View File

@ -697,8 +697,6 @@
</ClCompile>
<ClCompile Include="gui\Saveslots.cpp" />
<ClCompile Include="gui\SysState.cpp" />
<ClCompile Include="ZipTools\thread_gzip.cpp" />
<ClCompile Include="ZipTools\thread_lzma.cpp" />
<ClCompile Include="windows\Optimus.cpp" />
</ItemGroup>
<ItemGroup>
@ -1089,7 +1087,6 @@
<ClInclude Include="gui\RecentIsoList.h" />
<ClInclude Include="PathDefs.h" />
<ClInclude Include="SysForwardDefs.h" />
<ClInclude Include="ZipTools\ThreadedZipTools.h" />
<ClInclude Include="cheatscpp.h" />
</ItemGroup>
<ItemGroup>

View File

@ -863,12 +863,6 @@
<ClCompile Include="gui\SysState.cpp">
<Filter>AppHost</Filter>
</ClCompile>
<ClCompile Include="ZipTools\thread_gzip.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="ZipTools\thread_lzma.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="GameDatabase.cpp">
<Filter>Misc</Filter>
</ClCompile>
@ -2017,9 +2011,6 @@
<ClInclude Include="SysForwardDefs.h">
<Filter>AppHost\Include</Filter>
</ClInclude>
<ClInclude Include="ZipTools\ThreadedZipTools.h">
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="GameDatabase.h">
<Filter>Misc</Filter>
</ClInclude>