| 602 | } |
| 603 | |
| 604 | __attribute__((destructor)) |
| 605 | static void OnUnload() |
| 606 | { |
| 607 | DebugLog("[CR] OnUnload: shutting down\n"); |
| 608 | |
| 609 | // Wait for the init thread to finish so we don't unmap code it's executing. |
| 610 | // The thread runs for ~2s (usleep) + init time, so this is bounded. |
| 611 | if (g_hookAttempted.load(std::memory_order_acquire)) { |
| 612 | if (!g_initThreadDone.load(std::memory_order_acquire)) { |
| 613 | DebugLog("[CR] OnUnload: waiting for init thread\n"); |
| 614 | pthread_join(g_initThread, nullptr); |
| 615 | } |
| 616 | } |
| 617 | |
| 618 | // No-op (sync-icon state writer was never wired up); kept for contract. |
| 619 | CloudIntercept::FlushPendingSyncStates(); |
| 620 | |
| 621 | // Shut down cloud storage (signals workers, drains queue with timeout) |
| 622 | CloudStorage::Shutdown(); |
| 623 | |
| 624 | // Stop the HTTP server (joins accept + client threads) |
| 625 | HttpServer::Stop(); |
| 626 | |
| 627 | if (g_initialized.load(std::memory_order_acquire)) |
| 628 | { |
| 629 | CloudHooks::BeginShutdown(); |
| 630 | VtableHook::RemoveHooks(g_vtableInfo); |
| 631 | VtableHook::RemoveCloudEnabledHook(g_cloudEnabledInfo); |
| 632 | Log::Info("cloud_redirect.so unloaded"); |
| 633 | } |
| 634 | if (g_debugFd >= 0) |
| 635 | close(g_debugFd); |
| 636 | } |
| 637 |
nothing calls this directly
no test coverage detected