aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xREADME.md2
-rwxr-xr-xsrc/common/CMakeLists.txt2
-rwxr-xr-xsrc/common/fs/file.cpp29
-rwxr-xr-xsrc/common/fs/file.h11
-rwxr-xr-xsrc/common/logging/backend.cpp19
-rwxr-xr-xsrc/common/thread_worker.h102
-rwxr-xr-xsrc/common/unique_function.h64
-rwxr-xr-xsrc/core/hle/service/mii/manager.cpp5
-rwxr-xr-xsrc/tests/CMakeLists.txt1
-rwxr-xr-xsrc/tests/common/unique_function.cpp108
10 files changed, 320 insertions, 23 deletions
diff --git a/README.md b/README.md
index d67ca7fb4..a20ab038b 100755
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
1yuzu emulator early access 1yuzu emulator early access
2============= 2=============
3 3
4This is the source code for early-access 1846. 4This is the source code for early-access 1847.
5 5
6## Legal Notice 6## Legal Notice
7 7
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index a6fa9a85d..e03fffd8d 100755
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -180,7 +180,6 @@ add_library(common STATIC
180 thread.cpp 180 thread.cpp
181 thread.h 181 thread.h
182 thread_queue_list.h 182 thread_queue_list.h
183 thread_worker.cpp
184 thread_worker.h 183 thread_worker.h
185 threadsafe_queue.h 184 threadsafe_queue.h
186 time_zone.cpp 185 time_zone.cpp
@@ -188,6 +187,7 @@ add_library(common STATIC
188 tiny_mt.h 187 tiny_mt.h
189 tree.h 188 tree.h
190 uint128.h 189 uint128.h
190 unique_function.h
191 uuid.cpp 191 uuid.cpp
192 uuid.h 192 uuid.h
193 vector_math.h 193 vector_math.h
diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp
index 077f34995..274f57659 100755
--- a/src/common/fs/file.cpp
+++ b/src/common/fs/file.cpp
@@ -306,9 +306,9 @@ bool IOFile::Flush() const {
306 errno = 0; 306 errno = 0;
307 307
308#ifdef _WIN32 308#ifdef _WIN32
309 const auto flush_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0; 309 const auto flush_result = std::fflush(file) == 0;
310#else 310#else
311 const auto flush_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0; 311 const auto flush_result = std::fflush(file) == 0;
312#endif 312#endif
313 313
314 if (!flush_result) { 314 if (!flush_result) {
@@ -320,6 +320,28 @@ bool IOFile::Flush() const {
320 return flush_result; 320 return flush_result;
321} 321}
322 322
323bool IOFile::Commit() const {
324 if (!IsOpen()) {
325 return false;
326 }
327
328 errno = 0;
329
330#ifdef _WIN32
331 const auto commit_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0;
332#else
333 const auto commit_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0;
334#endif
335
336 if (!commit_result) {
337 const auto ec = std::error_code{errno, std::generic_category()};
338 LOG_ERROR(Common_Filesystem, "Failed to commit the file at path={}, ec_message={}",
339 PathToUTF8String(file_path), ec.message());
340 }
341
342 return commit_result;
343}
344
323bool IOFile::SetSize(u64 size) const { 345bool IOFile::SetSize(u64 size) const {
324 if (!IsOpen()) { 346 if (!IsOpen()) {
325 return false; 347 return false;
@@ -347,6 +369,9 @@ u64 IOFile::GetSize() const {
347 return 0; 369 return 0;
348 } 370 }
349 371
372 // Flush any unwritten buffered data into the file prior to retrieving the file size.
373 std::fflush(file);
374
350 std::error_code ec; 375 std::error_code ec;
351 376
352 const auto file_size = fs::file_size(file_path, ec); 377 const auto file_size = fs::file_size(file_path, ec);
diff --git a/src/common/fs/file.h b/src/common/fs/file.h
index 588fe619d..2c4ab4332 100755
--- a/src/common/fs/file.h
+++ b/src/common/fs/file.h
@@ -396,13 +396,22 @@ public:
396 [[nodiscard]] size_t WriteString(std::span<const char> string) const; 396 [[nodiscard]] size_t WriteString(std::span<const char> string) const;
397 397
398 /** 398 /**
399 * Attempts to flush any unwritten buffered data into the file and flush the file into the disk. 399 * Attempts to flush any unwritten buffered data into the file.
400 * 400 *
401 * @returns True if the flush was successful, false otherwise. 401 * @returns True if the flush was successful, false otherwise.
402 */ 402 */
403 bool Flush() const; 403 bool Flush() const;
404 404
405 /** 405 /**
406 * Attempts to commit the file into the disk.
407 * Note that this is an expensive operation as this forces the operating system to write
408 * the contents of the file associated with the file descriptor into the disk.
409 *
410 * @returns True if the commit was successful, false otherwise.
411 */
412 bool Commit() const;
413
414 /**
406 * Resizes the file to a given size. 415 * Resizes the file to a given size.
407 * If the file is resized to a smaller size, the remainder of the file is discarded. 416 * If the file is resized to a smaller size, the remainder of the file is discarded.
408 * If the file is resized to a larger size, the new area appears as if zero-filled. 417 * If the file is resized to a larger size, the new area appears as if zero-filled.
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index b6fa4affb..61dddab3f 100755
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -171,19 +171,22 @@ FileBackend::FileBackend(const std::filesystem::path& filename) {
171FileBackend::~FileBackend() = default; 171FileBackend::~FileBackend() = default;
172 172
173void FileBackend::Write(const Entry& entry) { 173void FileBackend::Write(const Entry& entry) {
174 if (!file->IsOpen()) {
175 return;
176 }
177
174 using namespace Common::Literals; 178 using namespace Common::Literals;
175 // prevent logs from going over the maximum size (in case its spamming and the user doesn't 179 // Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
176 // know)
177 constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB; 180 constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB;
178 constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB; 181 constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB;
179 182
180 if (!file->IsOpen()) { 183 const bool write_limit_exceeded =
181 return; 184 bytes_written > MAX_BYTES_WRITTEN_EXTENDED ||
182 } 185 (bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging);
183 186
184 if (Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN_EXTENDED) { 187 // Close the file after the write limit is exceeded.
185 return; 188 if (write_limit_exceeded) {
186 } else if (!Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN) { 189 file->Close();
187 return; 190 return;
188 } 191 }
189 192
diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h
index f1859971f..ddd73220a 100755
--- a/src/common/thread_worker.h
+++ b/src/common/thread_worker.h
@@ -7,24 +7,110 @@
7#include <atomic> 7#include <atomic>
8#include <functional> 8#include <functional>
9#include <mutex> 9#include <mutex>
10#include <stop_token>
10#include <string> 11#include <string>
12#include <thread>
13#include <type_traits>
11#include <vector> 14#include <vector>
12#include <queue> 15#include <queue>
13 16
17#include "common/thread.h"
18#include "common/unique_function.h"
19
14namespace Common { 20namespace Common {
15 21
16class ThreadWorker final { 22template <class StateType = void>
23class StatefulThreadWorker {
24 static constexpr bool with_state = !std::is_same_v<StateType, void>;
25
26 struct DummyCallable {
27 int operator()() const noexcept {
28 return 0;
29 }
30 };
31
32 using Task =
33 std::conditional_t<with_state, UniqueFunction<void, StateType*>, UniqueFunction<void>>;
34 using StateMaker = std::conditional_t<with_state, std::function<StateType()>, DummyCallable>;
35
17public: 36public:
18 explicit ThreadWorker(std::size_t num_workers, const std::string& name); 37 explicit StatefulThreadWorker(size_t num_workers, std::string name, StateMaker func = {})
19 ~ThreadWorker(); 38 : workers_queued{num_workers}, thread_name{std::move(name)} {
20 void QueueWork(std::function<void()>&& work); 39 const auto lambda = [this, func](std::stop_token stop_token) {
40 Common::SetCurrentThreadName(thread_name.c_str());
41 {
42 std::conditional_t<with_state, StateType, int> state{func()};
43 while (!stop_token.stop_requested()) {
44 Task task;
45 {
46 std::unique_lock lock{queue_mutex};
47 if (requests.empty()) {
48 wait_condition.notify_all();
49 }
50 condition.wait(lock, stop_token, [this] { return !requests.empty(); });
51 if (stop_token.stop_requested()) {
52 break;
53 }
54 task = std::move(requests.front());
55 requests.pop();
56 }
57 if constexpr (with_state) {
58 task(&state);
59 } else {
60 task();
61 }
62 ++work_done;
63 }
64 }
65 ++workers_stopped;
66 wait_condition.notify_all();
67 };
68 threads.reserve(num_workers);
69 for (size_t i = 0; i < num_workers; ++i) {
70 threads.emplace_back(lambda);
71 }
72 }
73
74 StatefulThreadWorker& operator=(const StatefulThreadWorker&) = delete;
75 StatefulThreadWorker(const StatefulThreadWorker&) = delete;
76
77 StatefulThreadWorker& operator=(StatefulThreadWorker&&) = delete;
78 StatefulThreadWorker(StatefulThreadWorker&&) = delete;
79
80 void QueueWork(Task work) {
81 {
82 std::unique_lock lock{queue_mutex};
83 requests.emplace(std::move(work));
84 ++work_scherduled;
85 }
86 condition.notify_one();
87 }
88
89 void WaitForRequests(std::stop_token stop_token = {}) {
90 std::stop_callback callback(stop_token, [this] {
91 for (auto& thread : threads) {
92 thread.request_stop();
93 }
94 });
95 std::unique_lock lock{queue_mutex};
96 wait_condition.wait(lock, [this] {
97 return workers_stopped >= workers_queued || work_done >= work_scherduled;
98 });
99 }
21 100
22private: 101private:
23 std::vector<std::thread> threads; 102 std::queue<Task> requests;
24 std::queue<std::function<void()>> requests;
25 std::mutex queue_mutex; 103 std::mutex queue_mutex;
26 std::condition_variable condition; 104 std::condition_variable_any condition;
27 std::atomic_bool stop{}; 105 std::condition_variable wait_condition;
106 std::atomic<size_t> work_scherduled{};
107 std::atomic<size_t> work_done{};
108 std::atomic<size_t> workers_stopped{};
109 std::atomic<size_t> workers_queued{};
110 std::string thread_name;
111 std::vector<std::jthread> threads;
28}; 112};
29 113
114using ThreadWorker = StatefulThreadWorker<>;
115
30} // namespace Common 116} // namespace Common
diff --git a/src/common/unique_function.h b/src/common/unique_function.h
new file mode 100755
index 000000000..0a2ee9bb5
--- /dev/null
+++ b/src/common/unique_function.h
@@ -0,0 +1,64 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <utility>
9
10namespace Common {
11
12template <typename ResultType, typename... Args>
13class UniqueFunction {
14 class CallableBase {
15 public:
16 virtual ~CallableBase() = default;
17 virtual ResultType operator()(Args...) = 0;
18 };
19
20 template <typename Functor>
21 class Callable final : public CallableBase {
22 public:
23 Callable(Functor&& functor_) : functor{std::move(functor_)} {}
24 ~Callable() override = default;
25
26 ResultType operator()(Args... args) override {
27 return functor(std::forward<Args>(args)...);
28 }
29
30 private:
31 Functor functor;
32 };
33
34public:
35 UniqueFunction() = default;
36
37 template <typename Functor>
38 UniqueFunction(Functor&& functor)
39 : callable{std::make_unique<Callable<Functor>>(std::move(functor))} {}
40
41 UniqueFunction& operator=(UniqueFunction<ResultType, Args...>&& rhs) noexcept {
42 callable = std::move(rhs.callable);
43 return *this;
44 }
45
46 UniqueFunction(UniqueFunction<ResultType, Args...>&& rhs) noexcept
47 : callable{std::move(rhs.callable)} {}
48
49 ResultType operator()(Args... args) const {
50 return (*callable)(std::forward<Args>(args)...);
51 }
52
53 explicit operator bool() const noexcept {
54 return callable != nullptr;
55 }
56
57 UniqueFunction& operator=(const UniqueFunction<ResultType, Args...>&) = delete;
58 UniqueFunction(const UniqueFunction<ResultType, Args...>&) = delete;
59
60private:
61 std::unique_ptr<CallableBase> callable;
62};
63
64} // namespace Common
diff --git a/src/core/hle/service/mii/manager.cpp b/src/core/hle/service/mii/manager.cpp
index 114aff31c..869d2763f 100755
--- a/src/core/hle/service/mii/manager.cpp
+++ b/src/core/hle/service/mii/manager.cpp
@@ -20,6 +20,7 @@ namespace {
20 20
21constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4}; 21constexpr ResultCode ERROR_CANNOT_FIND_ENTRY{ErrorModule::Mii, 4};
22 22
23constexpr std::size_t BaseMiiCount{2};
23constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()}; 24constexpr std::size_t DefaultMiiCount{RawData::DefaultMii.size()};
24 25
25constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'}; 26constexpr MiiStoreData::Name DefaultMiiName{u'y', u'u', u'z', u'u'};
@@ -415,7 +416,7 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const {
415 count += 0; 416 count += 0;
416 } 417 }
417 if ((source_flag & SourceFlag::Default) != SourceFlag::None) { 418 if ((source_flag & SourceFlag::Default) != SourceFlag::None) {
418 count += DefaultMiiCount; 419 count += (DefaultMiiCount - BaseMiiCount);
419 } 420 }
420 return static_cast<u32>(count); 421 return static_cast<u32>(count);
421} 422}
@@ -445,7 +446,7 @@ ResultVal<std::vector<MiiInfoElement>> MiiManager::GetDefault(SourceFlag source_
445 return MakeResult(std::move(result)); 446 return MakeResult(std::move(result));
446 } 447 }
447 448
448 for (std::size_t index = 0; index < DefaultMiiCount; index++) { 449 for (std::size_t index = BaseMiiCount; index < DefaultMiiCount; index++) {
449 result.emplace_back(BuildDefault(index), Source::Default); 450 result.emplace_back(BuildDefault(index), Source::Default);
450 } 451 }
451 452
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 96bc30cac..c4c012f3d 100755
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -5,6 +5,7 @@ add_executable(tests
5 common/host_memory.cpp 5 common/host_memory.cpp
6 common/param_package.cpp 6 common/param_package.cpp
7 common/ring_buffer.cpp 7 common/ring_buffer.cpp
8 common/unique_function.cpp
8 core/core_timing.cpp 9 core/core_timing.cpp
9 core/network/network.cpp 10 core/network/network.cpp
10 tests.cpp 11 tests.cpp
diff --git a/src/tests/common/unique_function.cpp b/src/tests/common/unique_function.cpp
new file mode 100755
index 000000000..ac9912738
--- /dev/null
+++ b/src/tests/common/unique_function.cpp
@@ -0,0 +1,108 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6
7#include <catch2/catch.hpp>
8
9#include "common/unique_function.h"
10
11namespace {
12struct Noisy {
13 Noisy() : state{"Default constructed"} {}
14 Noisy(Noisy&& rhs) noexcept : state{"Move constructed"} {
15 rhs.state = "Moved away";
16 }
17 Noisy& operator=(Noisy&& rhs) noexcept {
18 state = "Move assigned";
19 rhs.state = "Moved away";
20 }
21 Noisy(const Noisy&) : state{"Copied constructed"} {}
22 Noisy& operator=(const Noisy&) {
23 state = "Copied assigned";
24 }
25
26 std::string state;
27};
28} // Anonymous namespace
29
30TEST_CASE("UniqueFunction", "[common]") {
31 SECTION("Capture reference") {
32 int value = 0;
33 Common::UniqueFunction<void> func = [&value] { value = 5; };
34 func();
35 REQUIRE(value == 5);
36 }
37 SECTION("Capture pointer") {
38 int value = 0;
39 int* pointer = &value;
40 Common::UniqueFunction<void> func = [pointer] { *pointer = 5; };
41 func();
42 REQUIRE(value == 5);
43 }
44 SECTION("Move object") {
45 Noisy noisy;
46 REQUIRE(noisy.state == "Default constructed");
47
48 Common::UniqueFunction<void> func = [noisy = std::move(noisy)] {
49 REQUIRE(noisy.state == "Move constructed");
50 };
51 REQUIRE(noisy.state == "Moved away");
52 func();
53 }
54 SECTION("Move construct function") {
55 int value = 0;
56 Common::UniqueFunction<void> func = [&value] { value = 5; };
57 Common::UniqueFunction<void> new_func = std::move(func);
58 new_func();
59 REQUIRE(value == 5);
60 }
61 SECTION("Move assign function") {
62 int value = 0;
63 Common::UniqueFunction<void> func = [&value] { value = 5; };
64 Common::UniqueFunction<void> new_func;
65 new_func = std::move(func);
66 new_func();
67 REQUIRE(value == 5);
68 }
69 SECTION("Default construct then assign function") {
70 int value = 0;
71 Common::UniqueFunction<void> func;
72 func = [&value] { value = 5; };
73 func();
74 REQUIRE(value == 5);
75 }
76 SECTION("Pass arguments") {
77 int result = 0;
78 Common::UniqueFunction<void, int, int> func = [&result](int a, int b) { result = a + b; };
79 func(5, 4);
80 REQUIRE(result == 9);
81 }
82 SECTION("Pass arguments and return value") {
83 Common::UniqueFunction<int, int, int> func = [](int a, int b) { return a + b; };
84 REQUIRE(func(5, 4) == 9);
85 }
86 SECTION("Destructor") {
87 int num_destroyed = 0;
88 struct Foo {
89 Foo(int* num_) : num{num_} {}
90 Foo(Foo&& rhs) : num{std::exchange(rhs.num, nullptr)} {}
91 Foo(const Foo&) = delete;
92
93 ~Foo() {
94 if (num) {
95 ++*num;
96 }
97 }
98
99 int* num = nullptr;
100 };
101 Foo object{&num_destroyed};
102 {
103 Common::UniqueFunction<void> func = [object = std::move(object)] {};
104 REQUIRE(num_destroyed == 0);
105 }
106 REQUIRE(num_destroyed == 1);
107 }
108}