@@ -181,9 +181,8 @@ void GwDatArchive::StopWorker()
181181 std::scoped_lock<std::mutex> lock (m_task_mutex);
182182 m_worker_running = false ;
183183 }
184- // Drop any undelivered async reads; callers tolerate non-delivery across a worker stop.
185184 std::scoped_lock<std::mutex> lock (m_pending_mutex);
186- m_pending_reads.clear ();
185+ m_pending_reads.clear (); // undelivered async reads are dropped on stop
187186}
188187
189188void GwDatArchive::EnqueueTask (std::function<void ()> task)
@@ -418,10 +417,10 @@ void GwDatArchive::ReadFileAsync(uint32_t file_id, ReadCallback callback, uint32
418417 return ;
419418 if (!file_id) {
420419 std::vector<uint8_t > empty;
421- callback (empty); // nothing to look up
420+ callback (empty);
422421 return ;
423422 }
424- StartWorker (); // the poll loop must be running to deliver
423+ StartWorker (); // the poll loop delivers
425424 const uint32_t deadline = GetTickCount () + kAsyncReadTimeoutMs ;
426425 std::scoped_lock<std::mutex> lock (m_pending_mutex);
427426 m_pending_reads.push_back ({file_id, stream_id, deadline, std::move (callback), false });
@@ -433,41 +432,30 @@ void GwDatArchive::SetTrigger(TriggerFn trigger)
433432 m_trigger = std::move (trigger);
434433}
435434
436- // Worker-thread pass over the pending async reads: deliver any that now resolve, expire any past
437- // their deadline. Callbacks fire outside the lock so they can't stall other ReadFileAsync callers.
435+ // Worker pass: deliver resolved reads, time out stale ones, tickle the client once per pending file.
438436void GwDatArchive::ProcessPendingReads ()
439437{
440- std::vector<std::pair<ReadCallback, std::vector<uint8_t >>> ready;
441- std::vector<uint32_t > to_trigger; // file ids to ask the client to load, fired outside the lock
442- TriggerFn trigger;
438+ std::vector<std::pair<ReadCallback, std::vector<uint8_t >>> ready; // callbacks fire outside the lock
443439 {
444440 std::scoped_lock<std::mutex> lock (m_pending_mutex);
445- if (m_pending_reads.empty ())
446- return ;
447- trigger = m_trigger; // copy so we can call it after releasing the lock
448441 const uint32_t now = GetTickCount ();
449442 for (auto it = m_pending_reads.begin (); it != m_pending_reads.end ();) {
450443 std::vector<uint8_t > data;
451- if (ReadFile (it->file_id , data, it->stream_id )) {
444+ if (ReadFile (it->file_id , data, it->stream_id ))
452445 ready.emplace_back (std::move (it->callback ), std::move (data));
453- it = m_pending_reads.erase (it);
454- }
455- else if (static_cast <int32_t >(now - it->deadline_ms ) >= 0 ) { // deadline passed (wrap-safe)
446+ else if (static_cast <int32_t >(now - it->deadline_ms ) >= 0 ) // deadline passed (wrap-safe)
456447 ready.emplace_back (std::move (it->callback ), std::vector<uint8_t >{});
457- it = m_pending_reads.erase (it);
458- }
459448 else {
460- // Not resident yet: ask the client to fetch it, but only once per request.
461- if (trigger && !it->triggered ) {
462- to_trigger.push_back (it->file_id );
449+ if (m_trigger && !it->triggered ) { // ask the client to fetch it, once
450+ m_trigger (it->file_id );
463451 it->triggered = true ;
464452 }
465453 ++it;
454+ continue ;
466455 }
456+ it = m_pending_reads.erase (it);
467457 }
468458 }
469- for (uint32_t file_id : to_trigger)
470- trigger (file_id);
471459 for (auto & [callback, data] : ready)
472460 callback (data);
473461}
0 commit comments