diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index 2f3f340c14..73f6c8815b 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -110,6 +110,7 @@ + diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index a55b5d26d6..f9f84f8426 100644 --- a/Source/Core/Common/Common.vcxproj.filters +++ b/Source/Core/Common/Common.vcxproj.filters @@ -53,6 +53,7 @@ + diff --git a/Source/Core/Common/ScopeGuard.h b/Source/Core/Common/ScopeGuard.h new file mode 100644 index 0000000000..ab021a178c --- /dev/null +++ b/Source/Core/Common/ScopeGuard.h @@ -0,0 +1,50 @@ +// Copyright 2015 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +namespace Common +{ + +class ScopeGuard final +{ +public: + template + ScopeGuard(Callable&& finalizer) : m_finalizer(std::forward(finalizer)) {} + + ScopeGuard(ScopeGuard&& other) : m_finalizer(std::move(other.m_finalizer)) + { + other.m_finalizer = nullptr; + } + + ~ScopeGuard() + { + Exit(); + } + + void Dismiss() + { + m_finalizer = nullptr; + } + + void Exit() + { + if (m_finalizer) + { + m_finalizer(); // must not throw + m_finalizer = nullptr; + } + } + + ScopeGuard(const ScopeGuard&) = delete; + + void operator=(const ScopeGuard&) = delete; + +private: + std::function m_finalizer; +}; + +} // Namespace Common diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index e74a88b500..129aaa15ea 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -9,6 +9,7 @@ #include "Common/CommonTypes.h" #include "Common/Event.h" +#include "Common/ScopeGuard.h" #include "Common/StringUtil.h" #include "Common/Thread.h" #include "Common/Timer.h" @@ -280,8 +281,20 @@ struct CompressAndDumpState_args static void CompressAndDumpState(CompressAndDumpState_args save_args) { std::lock_guard lk(*save_args.buffer_mutex); - if (!save_args.wait) + + // ScopeGuard is used here to ensure that g_compressAndDumpStateSyncEvent.Set() + // will be called and that it will happen after the IOFile is closed. + // Both ScopeGuard's and IOFile's finalization occur at respective object destruction time. + // As Local (stack) objects are destructed in the reverse order of construction and "ScopeGuard on_exit" + // is created before the "IOFile f", it is guaranteed that the file will be finalized before + // the ScopeGuard's finalization (i.e. "g_compressAndDumpStateSyncEvent.Set()" call). + Common::ScopeGuard on_exit([]() + { g_compressAndDumpStateSyncEvent.Set(); + }); + // If it is not required to wait, we call finalizer early (and it won't be called again at destruction). + if (!save_args.wait) + on_exit.Exit(); const u8* const buffer_data = &(*(save_args.buffer_vector))[0]; const size_t buffer_size = (save_args.buffer_vector)->size(); @@ -313,7 +326,6 @@ static void CompressAndDumpState(CompressAndDumpState_args save_args) if (!f) { Core::DisplayMessage("Could not save state", 2000); - g_compressAndDumpStateSyncEvent.Set(); return; } @@ -361,7 +373,6 @@ static void CompressAndDumpState(CompressAndDumpState_args save_args) } Core::DisplayMessage(StringFromFormat("Saved State to %s", filename.c_str()), 2000); - g_compressAndDumpStateSyncEvent.Set(); } void SaveAs(const std::string& filename, bool wait)