From 62f2eb908c6771f9ac89aa518c55fbc101df3447 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Wed, 18 Dec 2024 14:13:39 -0500 Subject: Handle exceptions when starting IPC connection Also fixes bug where the IPC thread would double-lock the state mutex if the connection timed out and reached the maximum backoff interval. --- src/ipc_state.cpp | 68 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/src/ipc_state.cpp b/src/ipc_state.cpp index 24d0115..62b1828 100644 --- a/src/ipc_state.cpp +++ b/src/ipc_state.cpp @@ -112,8 +112,6 @@ struct IPCState { private: void Thread() { for (;;) { - SetStatusMessage("Disconnected from game."); - // initialized is definitely true because it is set to true when the thread // is created and only set to false within this block, when the thread is // killed. Thus, a call to Connect would always at most set @@ -127,6 +125,8 @@ struct IPCState { { std::lock_guard state_guard(state_mutex); + SetStatusMessage("Disconnected from game."); + should_disconnect = false; slot_matches = false; @@ -142,34 +142,49 @@ struct IPCState { } ipc_address = address; + + SetStatusMessage("Connecting to game..."); } int backoff_amount = 0; - SetStatusMessage("Connecting to game..."); TrackerLog(fmt::format("Looking for game over IPC ({})...", ipc_address)); - while (!TryConnect(ipc_address) || !connected) { - int backoff_limit = (backoff_amount + 1) * 10; - - for (int i = 0; i < backoff_limit && !connected; i++) { - // If Connect is called right before this block, we will see and handle - // should_disconnect. If it is called right after, we will do one bad - // poll, one sleep, and then grab the mutex again right after. - { - std::lock_guard state_guard(state_mutex); - if (should_disconnect) { - break; + while (!connected) { + if (TryConnect(ipc_address)) { + int backoff_limit = (backoff_amount + 1) * 10; + + for (int i = 0; i < backoff_limit && !connected; i++) { + // If Connect is called right before this block, we will see and + // handle should_disconnect. If it is called right after, we will do + // one bad poll, one sleep, and then grab the mutex again right + // after. + { + std::lock_guard state_guard(state_mutex); + if (should_disconnect) { + break; + } } + + ws->poll(); + + // Back off + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - ws->poll(); + backoff_amount++; + } else { + std::lock_guard state_guard(state_mutex); - // Back off - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } + if (!should_disconnect) { + should_disconnect = true; + address.clear(); - backoff_amount++; + SetStatusMessage("Disconnected from game."); + } + + break; + } // If Connect is called right before this block, we will see and handle // should_disconnect. If it is called right after, and the connection @@ -236,6 +251,8 @@ struct IPCState { [this](const std::string& s) { OnError(s); }); return true; } catch (const std::exception& ex) { + TrackerLog(fmt::format("Error connecting to Lingo: {}", ex.what())); + wxMessageBox(ex.what(), "Error connecting to Lingo", wxOK | wxICON_ERROR); ws.reset(); return false; } @@ -303,26 +320,23 @@ struct IPCState { void OnError(const std::string& s) {} + // Assumes mutex is locked. void CheckIfSlotMatches() { slot_matches = (tracker_ap_server == game_ap_server && tracker_ap_user == game_ap_user); if (slot_matches) { - status_message = "Connected to game."; + SetStatusMessage("Connected to game."); Sync(); } else if (connected) { - status_message = "Local game doesn't match AP slot."; + SetStatusMessage("Local game doesn't match AP slot."); } - - tracker_frame->UpdateStatusMessage(); } + // Assumes mutex is locked. void SetStatusMessage(std::optional msg) { - { - std::lock_guard state_guard(state_mutex); - status_message = msg; - } + status_message = msg; tracker_frame->UpdateStatusMessage(); } -- cgit 1.4.1