use uevent in transfer loops to reduce latency between exiting and updating progress time.

This commit is contained in:
ITotalJustice
2025-07-19 18:33:47 +01:00
parent 159abfa246
commit 4421ac1ceb
4 changed files with 189 additions and 24 deletions

View File

@@ -77,16 +77,8 @@ struct ThreadData {
auto GetResults() volatile -> Result;
void WakeAllThreads();
void SetReadResult(Result result) {
read_result = result;
}
void SetWriteResult(Result result) {
write_result = result;
}
void SetPullResult(Result result) {
pull_result = result;
auto IsAnyRunning() volatile const -> bool {
return read_running || write_running;
}
auto GetWriteOffset() volatile const -> s64 {
@@ -97,6 +89,33 @@ struct ThreadData {
return write_size;
}
auto GetDoneEvent() {
return &m_uevent_done;
}
auto GetProgressEvent() {
return &m_uevent_progres;
}
void SetReadResult(Result result) {
read_result = result;
if (R_FAILED(result)) {
ueventSignal(GetDoneEvent());
}
}
void SetWriteResult(Result result) {
write_result = result;
ueventSignal(GetDoneEvent());
}
void SetPullResult(Result result) {
pull_result = result;
if (R_FAILED(result)) {
ueventSignal(GetDoneEvent());
}
}
Result Pull(void* data, s64 size, u64* bytes_read);
Result readFuncInternal();
Result writeFuncInternal();
@@ -124,6 +143,9 @@ private:
CondVar can_pull{};
CondVar can_pull_write{};
UEvent m_uevent_done{};
UEvent m_uevent_progres{};
RingBuf<2> write_buffers{};
std::vector<u8> pull_buffer{};
s64 pull_buffer_offset{};
@@ -138,6 +160,9 @@ private:
std::atomic<Result> read_result{};
std::atomic<Result> write_result{};
std::atomic<Result> pull_result{};
std::atomic_bool read_running{true};
std::atomic_bool write_running{true};
};
ThreadData::ThreadData(ui::ProgressBox* _pbox, s64 size, ReadCallback _rfunc, WriteCallback _wfunc, u64 buffer_size)
@@ -153,10 +178,13 @@ ThreadData::ThreadData(ui::ProgressBox* _pbox, s64 size, ReadCallback _rfunc, Wr
condvarInit(std::addressof(can_write));
condvarInit(std::addressof(can_pull));
condvarInit(std::addressof(can_pull_write));
ueventCreate(&m_uevent_done, false);
ueventCreate(&m_uevent_progres, true);
}
auto ThreadData::GetResults() volatile -> Result {
R_UNLESS(!pbox->ShouldExit(), Result_TransferCancelled);
R_TRY(pbox->ShouldExitResult());
R_TRY(read_result.load());
R_TRY(write_result.load());
R_TRY(pull_result.load());
@@ -178,6 +206,9 @@ Result ThreadData::SetWriteBuf(std::vector<u8>& buf, s64 size) {
mutexLock(std::addressof(mutex));
if (!write_buffers.ringbuf_free()) {
if (!write_running) {
R_SUCCEED();
}
R_TRY(condvarWait(std::addressof(can_read), std::addressof(mutex)));
}
@@ -190,6 +221,10 @@ Result ThreadData::SetWriteBuf(std::vector<u8>& buf, s64 size) {
Result ThreadData::GetWriteBuf(std::vector<u8>& buf_out, s64& off_out) {
mutexLock(std::addressof(mutex));
if (!write_buffers.ringbuf_size()) {
if (!read_running) {
buf_out.resize(0);
R_SUCCEED();
}
R_TRY(condvarWait(std::addressof(can_write), std::addressof(mutex)));
}
@@ -249,6 +284,8 @@ Result ThreadData::Pull(void* data, s64 size, u64* bytes_read) {
// read thread reads all data from the source
Result ThreadData::readFuncInternal() {
ON_SCOPE_EXIT( read_running = false; );
// the main buffer which data is read into.
std::vector<u8> buf;
buf.reserve(this->read_buffer_size);
@@ -260,8 +297,11 @@ Result ThreadData::readFuncInternal() {
u64 bytes_read{};
buf.resize(read_size);
R_TRY(this->Read(buf.data(), read_size, std::addressof(bytes_read)));
auto buf_size = bytes_read;
if (!bytes_read) {
break;
}
auto buf_size = bytes_read;
R_TRY(this->SetWriteBuf(buf, buf_size));
}
@@ -269,8 +309,10 @@ Result ThreadData::readFuncInternal() {
R_SUCCEED();
}
// write thread writes data to the nca placeholder.
// write thread writes data to wfunc.
Result ThreadData::writeFuncInternal() {
ON_SCOPE_EXIT( write_running = false; );
std::vector<u8> buf;
buf.reserve(this->read_buffer_size);
@@ -278,6 +320,10 @@ Result ThreadData::writeFuncInternal() {
s64 dummy_off;
R_TRY(this->GetWriteBuf(buf, dummy_off));
const auto size = buf.size();
if (!size) {
log_write("exiting write func early because no data was received\n");
break;
}
if (!this->wfunc) {
R_TRY(this->SetPullBuf(buf, buf.size()));
@@ -286,6 +332,7 @@ Result ThreadData::writeFuncInternal() {
}
this->write_offset += size;
ueventSignal(GetProgressEvent());
}
log_write("finished write thread success!\n");
@@ -339,6 +386,10 @@ Result TransferInternal(ui::ProgressBox* pbox, s64 size, ReadCallback rfunc, Wri
u64 bytes_read;
const auto rsize = std::min<s64>(buf.size(), size - offset);
R_TRY(rfunc(buf.data(), offset, rsize, &bytes_read));
if (!bytes_read) {
break;
}
R_TRY(wfunc(buf.data(), offset, bytes_read));
offset += bytes_read;
@@ -382,15 +433,27 @@ Result TransferInternal(ui::ProgressBox* pbox, s64 size, ReadCallback rfunc, Wri
R_TRY(start_threads());
log_write("[THREAD] started threads\n");
while (t_data.GetWriteOffset() != t_data.GetWriteSize() && R_SUCCEEDED(t_data.GetResults())) {
pbox->UpdateTransfer(t_data.GetWriteOffset(), t_data.GetWriteSize());
svcSleepThread(1e+6);
const auto waiter_progress = waiterForUEvent(t_data.GetProgressEvent());
const auto waiter_cancel = waiterForUEvent(pbox->GetCancelEvent());
const auto waiter_done = waiterForUEvent(t_data.GetDoneEvent());
for (;;) {
s32 idx;
if (R_FAILED(waitMulti(&idx, UINT64_MAX, waiter_progress, waiter_cancel, waiter_done))) {
break;
}
if (!idx) {
pbox->UpdateTransfer(t_data.GetWriteOffset(), t_data.GetWriteSize());
} else {
break;
}
}
}
// wait for all threads to close.
log_write("waiting for threads to close\n");
for (;;) {
while (t_data.IsAnyRunning()) {
t_data.WakeAllThreads();
pbox->Yield();