| 582 | } |
| 583 | |
| 584 | bool DrainQueueForApp(uint32_t accountId, uint32_t appId) { |
| 585 | if (!g_provider) return true; |
| 586 | |
| 587 | std::string prefix = std::to_string(accountId) + "/" + std::to_string(appId) + "/"; |
| 588 | |
| 589 | constexpr int POLL_MS = 100; |
| 590 | constexpr int BASE_TIMEOUT_MS = 30000; |
| 591 | constexpr int PER_ITEM_MS = 3000; |
| 592 | constexpr int MAX_TIMEOUT_MS = 180000; |
| 593 | |
| 594 | // Scale timeout with pending work count. |
| 595 | int pendingCount = 0; |
| 596 | { |
| 597 | std::lock_guard<std::mutex> countLock(g_queueMutex); |
| 598 | for (const auto& item : g_workQueue) |
| 599 | if (!item.bestEffort && item.cloudPath.rfind(prefix, 0) == 0) |
| 600 | ++pendingCount; |
| 601 | } |
| 602 | int timeoutMs = (std::min)(MAX_TIMEOUT_MS, BASE_TIMEOUT_MS + pendingCount * PER_ITEM_MS); |
| 603 | |
| 604 | auto start = std::chrono::steady_clock::now(); |
| 605 | auto deadline = start + std::chrono::milliseconds(timeoutMs); |
| 606 | |
| 607 | std::unique_lock<std::mutex> lock(g_queueMutex); |
| 608 | RequeueFailedWorkForPrefixLocked(prefix); |
| 609 | g_queueCV.notify_all(); |
| 610 | |
| 611 | // Best-effort items continue in background. |
| 612 | bool completed = !HasPendingCommitWorkForPrefixLocked(prefix); |
| 613 | bool failed = HasFailedCommitWorkForPrefix(prefix); |
| 614 | if (completed && !failed) { |
| 615 | LOG("[CloudStorage] DrainQueueForApp: no pending commit work for %s", prefix.c_str()); |
| 616 | return true; |
| 617 | } |
| 618 | |
| 619 | LOG("[CloudStorage] DrainQueueForApp: waiting for %s", prefix.c_str()); |
| 620 | while (std::chrono::steady_clock::now() < deadline) { |
| 621 | completed = !HasPendingCommitWorkForPrefixLocked(prefix); |
| 622 | failed = HasFailedCommitWorkForPrefix(prefix); |
| 623 | if (completed || failed) break; |
| 624 | if (g_shuttingDown.load(std::memory_order_seq_cst)) break; |
| 625 | g_drainCV.wait_for(lock, std::chrono::milliseconds(POLL_MS)); |
| 626 | } |
| 627 | |
| 628 | auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( |
| 629 | std::chrono::steady_clock::now() - start).count(); |
| 630 | |
| 631 | if (completed && !failed) { |
| 632 | LOG("[CloudStorage] DrainQueueForApp: done for %s (%lld ms)", |
| 633 | prefix.c_str(), (long long)elapsed); |
| 634 | } else if (failed) { |
| 635 | LOG("[CloudStorage] DrainQueueForApp: failed work for %s after %lld ms", |
| 636 | prefix.c_str(), (long long)elapsed); |
| 637 | } else if (g_shuttingDown.load(std::memory_order_seq_cst)) { |
| 638 | LOG("[CloudStorage] DrainQueueForApp: shutdown for %s (%lld ms)", |
| 639 | prefix.c_str(), (long long)elapsed); |
| 640 | } else { |
| 641 | LOG("[CloudStorage] DrainQueueForApp: TIMEOUT for %s after %lld ms", |
no test coverage detected