use condivar in install stream to wait until the buffer is empty
This commit is contained in:
@@ -35,10 +35,11 @@ private:
|
|||||||
std::stop_token m_token{};
|
std::stop_token m_token{};
|
||||||
std::vector<u8> m_buffer{};
|
std::vector<u8> m_buffer{};
|
||||||
CondVar m_can_read{};
|
CondVar m_can_read{};
|
||||||
|
CondVar m_can_write{};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Mutex m_mutex{};
|
Mutex m_mutex{};
|
||||||
bool m_active{};
|
std::atomic_bool m_active{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Menu : MenuBase {
|
struct Menu : MenuBase {
|
||||||
|
|||||||
@@ -20,6 +20,16 @@ constexpr u64 MAX_BUFFER_SIZE = 1024ULL*1024ULL*8ULL;
|
|||||||
constexpr u64 MAX_BUFFER_RESERVE_SIZE = 1024ULL*1024ULL*32ULL;
|
constexpr u64 MAX_BUFFER_RESERVE_SIZE = 1024ULL*1024ULL*32ULL;
|
||||||
std::atomic<InstallState> INSTALL_STATE{InstallState::None};
|
std::atomic<InstallState> INSTALL_STATE{InstallState::None};
|
||||||
|
|
||||||
|
// don't use condivar here as windows mtp is very broken.
|
||||||
|
// stalling for too longer (3s+) and having too varied transfer speeds
|
||||||
|
// results in windows stalling the transfer for 1m until it kills it via timeout.
|
||||||
|
// the workaround is to always accept new data, but stall for 1s.
|
||||||
|
// UPDATE: it seems possible to trigger this bug during normal file transfer
|
||||||
|
// including using stock haze.
|
||||||
|
// it seems random, and ive been unable to trigger it personally.
|
||||||
|
// for this reason, use condivar rather than trying to work around the issue.
|
||||||
|
#define USE_CONDI_VAR 1
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Stream::Stream(const fs::FsPath& path, std::stop_token token) {
|
Stream::Stream(const fs::FsPath& path, std::stop_token token) {
|
||||||
@@ -30,6 +40,7 @@ Stream::Stream(const fs::FsPath& path, std::stop_token token) {
|
|||||||
|
|
||||||
mutexInit(&m_mutex);
|
mutexInit(&m_mutex);
|
||||||
condvarInit(&m_can_read);
|
condvarInit(&m_can_read);
|
||||||
|
condvarInit(&m_can_write);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result Stream::ReadChunk(void* buf, s64 size, u64* bytes_read) {
|
Result Stream::ReadChunk(void* buf, s64 size, u64* bytes_read) {
|
||||||
@@ -41,7 +52,7 @@ Result Stream::ReadChunk(void* buf, s64 size, u64* bytes_read) {
|
|||||||
while (!m_token.stop_requested()) {
|
while (!m_token.stop_requested()) {
|
||||||
SCOPED_MUTEX(&m_mutex);
|
SCOPED_MUTEX(&m_mutex);
|
||||||
if (m_active && m_buffer.empty()) {
|
if (m_active && m_buffer.empty()) {
|
||||||
R_TRY(condvarWait(&m_can_read, &m_mutex));
|
R_TRY(condvarWait(std::addressof(m_can_read), std::addressof(m_mutex)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((!m_active && m_buffer.empty()) || m_token.stop_requested()) {
|
if ((!m_active && m_buffer.empty()) || m_token.stop_requested()) {
|
||||||
@@ -52,7 +63,7 @@ Result Stream::ReadChunk(void* buf, s64 size, u64* bytes_read) {
|
|||||||
std::memcpy(buf, m_buffer.data(), size);
|
std::memcpy(buf, m_buffer.data(), size);
|
||||||
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + size);
|
m_buffer.erase(m_buffer.begin(), m_buffer.begin() + size);
|
||||||
*bytes_read = size;
|
*bytes_read = size;
|
||||||
R_SUCCEED();
|
return condvarWakeOne(&m_can_write);
|
||||||
}
|
}
|
||||||
|
|
||||||
log_write("[Stream::ReadChunk] failed to read\n");
|
log_write("[Stream::ReadChunk] failed to read\n");
|
||||||
@@ -71,11 +82,12 @@ bool Stream::Push(const void* buf, s64 size) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't use condivar here as windows mtp is very broken.
|
|
||||||
// stalling for too longer (3s+) and having too varied transfer speeds
|
|
||||||
// results in windows stalling the transfer for 1m until it kills it via timeout.
|
|
||||||
// the workaround is to always accept new data, but stall for 1s.
|
|
||||||
SCOPED_MUTEX(&m_mutex);
|
SCOPED_MUTEX(&m_mutex);
|
||||||
|
#if USE_CONDI_VAR
|
||||||
|
if (m_active && m_buffer.size() >= MAX_BUFFER_SIZE) {
|
||||||
|
R_TRY(condvarWait(std::addressof(m_can_write), std::addressof(m_mutex)));
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (m_active && m_buffer.size() >= MAX_BUFFER_SIZE) {
|
if (m_active && m_buffer.size() >= MAX_BUFFER_SIZE) {
|
||||||
// unlock the mutex and wait for 1s to bring transfer speed down to 1MiB/s.
|
// unlock the mutex and wait for 1s to bring transfer speed down to 1MiB/s.
|
||||||
log_write("[Stream::Push] buffer is full, delaying\n");
|
log_write("[Stream::Push] buffer is full, delaying\n");
|
||||||
@@ -84,6 +96,7 @@ bool Stream::Push(const void* buf, s64 size) {
|
|||||||
|
|
||||||
svcSleepThread(1e+9);
|
svcSleepThread(1e+9);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!m_active) {
|
if (!m_active) {
|
||||||
log_write("[Stream::Push] file not active\n");
|
log_write("[Stream::Push] file not active\n");
|
||||||
@@ -107,6 +120,7 @@ void Stream::Disable() {
|
|||||||
SCOPED_MUTEX(&m_mutex);
|
SCOPED_MUTEX(&m_mutex);
|
||||||
m_active = false;
|
m_active = false;
|
||||||
condvarWakeOne(&m_can_read);
|
condvarWakeOne(&m_can_read);
|
||||||
|
condvarWakeOne(&m_can_write);
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu::Menu(const std::string& title, u32 flags) : MenuBase{title, flags} {
|
Menu::Menu(const std::string& title, u32 flags) : MenuBase{title, flags} {
|
||||||
|
|||||||
Reference in New Issue
Block a user