| 702 | } |
| 703 | |
| 704 | void Shutdown() { |
| 705 | g_shuttingDown.store(true, std::memory_order_seq_cst); |
| 706 | g_workerRunning = false; |
| 707 | g_queueCV.notify_all(); |
| 708 | |
| 709 | // 5s bounded wait for workers; detach stragglers blocked on cloud I/O. |
| 710 | auto joinDeadline = std::chrono::steady_clock::now() + std::chrono::seconds(5); |
| 711 | for (auto& t : g_workerThreads) { |
| 712 | if (!t.joinable()) continue; |
| 713 | auto remaining = joinDeadline - std::chrono::steady_clock::now(); |
| 714 | if (remaining <= std::chrono::milliseconds(0)) { |
| 715 | LOG("[CloudStorage] WorkQueue shutdown: detaching worker (join deadline exceeded)"); |
| 716 | t.detach(); |
| 717 | continue; |
| 718 | } |
| 719 | #ifdef _WIN32 |
| 720 | DWORD waitMs = (DWORD)std::chrono::duration_cast<std::chrono::milliseconds>(remaining).count(); |
| 721 | HANDLE h = t.native_handle(); |
| 722 | if (WaitForSingleObject(h, waitMs) == WAIT_TIMEOUT) { |
| 723 | LOG("[CloudStorage] WorkQueue shutdown: detaching worker (join timed out after %lu ms)", waitMs); |
| 724 | t.detach(); |
| 725 | } else { |
| 726 | t.join(); |
| 727 | } |
| 728 | #else |
| 729 | // No timed join on POSIX; best-effort sleep then join. |
| 730 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); |
| 731 | if (t.joinable()) { |
| 732 | t.join(); |
| 733 | } |
| 734 | #endif |
| 735 | } |
| 736 | g_workerThreads.clear(); |
| 737 | |
| 738 | int spinCount = 0; |
| 739 | while (g_activeWorkers.load(std::memory_order_acquire) > 0) { |
| 740 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); |
| 741 | if (++spinCount > 500) { // 5 seconds max |
| 742 | LOG("[CloudStorage] WorkQueue shutdown: timed out waiting for %d active workers", |
| 743 | g_activeWorkers.load()); |
| 744 | break; |
| 745 | } |
| 746 | } |
| 747 | |
| 748 | { |
| 749 | std::lock_guard<std::mutex> lock(g_queueMutex); |
| 750 | g_workQueue.clear(); |
| 751 | g_uploadIndex.clear(); |
| 752 | g_activePaths.clear(); |
| 753 | g_activeDeletes.clear(); |
| 754 | g_activeBestEffortPaths.clear(); |
| 755 | g_recentUploadFingerprints.clear(); |
| 756 | g_failedPaths.clear(); |
| 757 | g_failedWorkItems.clear(); |
| 758 | } |
| 759 | g_drainCV.notify_all(); |
| 760 | |
| 761 | LOG("[CloudStorage] Background work queue shutdown complete"); |
nothing calls this directly
no outgoing calls
no test coverage detected