use uevent in transfer loops to reduce latency between exiting and updating progress time.
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user