Catch exceptions in background threads

This commit is contained in:
Dan Paulat 2024-06-12 01:03:58 -05:00
parent 863cdd0384
commit e1d61fccfa
13 changed files with 866 additions and 665 deletions

View file

@ -611,6 +611,8 @@ void MainWindow::on_actionCheckForUpdates_triggered()
boost::asio::post(
p->threadPool_,
[this]()
{
try
{
if (!p->updateManager_->CheckForUpdates(main::kVersionString_))
{
@ -627,6 +629,11 @@ void MainWindow::on_actionCheckForUpdates_triggered()
messageBox->show();
});
}
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
@ -662,10 +669,17 @@ void MainWindowImpl::AsyncSetup()
{
boost::asio::post(threadPool_,
[this]()
{
try
{
manager::UpdateManager::RemoveTemporaryReleases();
updateManager_->CheckForUpdates(
main::kVersionString_);
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
}

View file

@ -42,7 +42,17 @@ public:
[this](const types::TextEventKey& key, size_t messageIndex)
{
boost::asio::post(threadPool_,
[=, this]() { HandleAlert(key, messageIndex); });
[=, this]()
{
try
{
HandleAlert(key, messageIndex);
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
});
}

View file

@ -25,6 +25,8 @@ public:
~Impl() { threadPool_.join(); }
void DownloadSync(const std::shared_ptr<request::DownloadRequest>& request);
boost::asio::thread_pool threadPool_ {1u};
DownloadManager* self_;
@ -36,13 +38,25 @@ DownloadManager::~DownloadManager() = default;
void DownloadManager::Download(
const std::shared_ptr<request::DownloadRequest>& request)
{
boost::asio::post(
p->threadPool_,
boost::asio::post(p->threadPool_,
[=]()
{
try
{
p->DownloadSync(request);
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
void DownloadManager::Impl::DownloadSync(
const std::shared_ptr<request::DownloadRequest>& request)
{
// Prepare destination file
const std::filesystem::path& destinationPath =
request->destination_path();
const std::filesystem::path& destinationPath = request->destination_path();
if (!destinationPath.has_parent_path())
{
@ -95,8 +109,7 @@ void DownloadManager::Download(
std::ios_base::trunc};
if (!ofs.is_open() || !ofs.good())
{
logger_->error(
"Unable to open destination file for writing: \"{}\"",
logger_->error("Unable to open destination file for writing: \"{}\"",
destinationPath.string());
Q_EMIT request->RequestComplete(
@ -110,8 +123,8 @@ void DownloadManager::Download(
cpr::cpr_off_t lastDownloadTotal {};
// Download file
cpr::Response response =
cpr::Get(cpr::Url {request->url()},
cpr::Response response = cpr::Get(
cpr::Url {request->url()},
cpr::ProgressCallback(
[&](cpr::cpr_off_t downloadTotal,
cpr::cpr_off_t downloadNow,
@ -125,17 +138,14 @@ void DownloadManager::Download(
std::chrono::system_clock::now();
// Only emit an update every 100ms
if ((now > lastUpdated + 100ms ||
downloadNow == downloadTotal) &&
if ((now > lastUpdated + 100ms || downloadNow == downloadTotal) &&
(downloadNow != lastDownloadNow ||
downloadTotal != lastDownloadTotal))
{
logger_->trace("Downloaded: {} / {}",
downloadNow,
downloadTotal);
logger_->trace(
"Downloaded: {} / {}", downloadNow, downloadTotal);
Q_EMIT request->ProgressUpdated(downloadNow,
downloadTotal);
Q_EMIT request->ProgressUpdated(downloadNow, downloadTotal);
lastUpdated = now;
lastDownloadNow = downloadNow;
@ -156,8 +166,8 @@ void DownloadManager::Download(
ofs.close();
// Handle error response
if (response.error.code != cpr::ErrorCode::OK ||
request->IsCanceled() || !ofsGood)
if (response.error.code != cpr::ErrorCode::OK || request->IsCanceled() ||
!ofsGood)
{
request::DownloadRequest::CompleteReason reason =
request::DownloadRequest::CompleteReason::IOError;
@ -198,8 +208,7 @@ void DownloadManager::Download(
// Handle response
const auto contentMd5 = response.header.find("content-md5");
if (contentMd5 != response.header.cend() &&
!contentMd5->second.empty())
if (contentMd5 != response.header.cend() && !contentMd5->second.empty())
{
// Open file for reading
std::ifstream is {destinationPath,
@ -219,8 +228,7 @@ void DownloadManager::Download(
std::vector<std::uint8_t> digest {};
if (!util::ComputeDigest(EVP_md5(), is, digest))
{
logger_->error("Failed to compute MD5: {}",
destinationPath.string());
logger_->error("Failed to compute MD5: {}", destinationPath.string());
Q_EMIT request->RequestComplete(
request::DownloadRequest::CompleteReason::IOError);
@ -231,13 +239,13 @@ void DownloadManager::Download(
// Compare calculated MD5 with digest in response header
QByteArray expectedDigestArray =
QByteArray::fromBase64(contentMd5->second.c_str());
std::vector<std::uint8_t> expectedDigest(
expectedDigestArray.cbegin(), expectedDigestArray.cend());
std::vector<std::uint8_t> expectedDigest(expectedDigestArray.cbegin(),
expectedDigestArray.cend());
if (digest != expectedDigest)
{
QByteArray calculatedDigest(
reinterpret_cast<char*>(digest.data()), digest.size());
QByteArray calculatedDigest(reinterpret_cast<char*>(digest.data()),
digest.size());
logger_->error("Digest mismatch: {} != {}",
calculatedDigest.toBase64().toStdString(),
@ -253,7 +261,6 @@ void DownloadManager::Download(
logger_->info("Download complete: {}", request->url());
Q_EMIT request->RequestComplete(
request::DownloadRequest::CompleteReason::OK);
});
}
std::shared_ptr<DownloadManager> DownloadManager::Instance()

View file

@ -156,6 +156,8 @@ PlacefileManager::PlacefileManager() : p(std::make_unique<Impl>(this))
{
boost::asio::post(p->threadPool_,
[this]()
{
try
{
p->InitializePlacefileSettings();
@ -163,6 +165,11 @@ PlacefileManager::PlacefileManager() : p(std::make_unique<Impl>(this))
main::Application::WaitForInitialization();
p->ReadPlacefileSettings();
Q_EMIT PlacefilesInitialized();
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
@ -678,7 +685,7 @@ void PlacefileManager::Impl::PlacefileRecord::ScheduleRefresh()
}
else
{
Update();
UpdateAsync();
}
});
}
@ -691,7 +698,18 @@ void PlacefileManager::Impl::PlacefileRecord::CancelRefresh()
void PlacefileManager::Impl::PlacefileRecord::UpdateAsync()
{
boost::asio::post(threadPool_, [this]() { Update(); });
boost::asio::post(threadPool_,
[this]()
{
try
{
Update();
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
std::shared_ptr<PlacefileManager> PlacefileManager::Instance()

View file

@ -196,6 +196,7 @@ public:
std::shared_ptr<ProviderManager> providerManager,
bool enabled);
void RefreshData(std::shared_ptr<ProviderManager> providerManager);
void RefreshDataSync(std::shared_ptr<ProviderManager> providerManager);
std::tuple<std::shared_ptr<types::RadarProductRecord>,
std::chrono::system_clock::time_point>
@ -225,6 +226,8 @@ public:
void PopulateLevel3ProductTimes(const std::string& product,
std::chrono::system_clock::time_point time);
void UpdateAvailableProductsSync();
static void
PopulateProductTimes(std::shared_ptr<ProviderManager> providerManager,
RadarProductRecordMap& productRecordMap,
@ -575,6 +578,8 @@ void RadarProductManager::EnableRefresh(common::RadarProductGroup group,
boost::asio::post(
p->threadPool_,
[=, this]()
{
try
{
providerManager->provider_->RequestAvailableProducts();
auto availableProducts =
@ -587,6 +592,11 @@ void RadarProductManager::EnableRefresh(common::RadarProductGroup group,
{
p->EnableRefresh(uuid, providerManager, enabled);
}
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
}
@ -664,27 +674,37 @@ void RadarProductManagerImpl::RefreshData(
providerManager->refreshTimer_.cancel();
}
boost::asio::post(
threadPool_,
boost::asio::post(threadPool_,
[=, this]()
{
try
{
RefreshDataSync(providerManager);
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
void RadarProductManagerImpl::RefreshDataSync(
std::shared_ptr<ProviderManager> providerManager)
{
using namespace std::chrono_literals;
auto [newObjects, totalObjects] =
providerManager->provider_->Refresh();
auto [newObjects, totalObjects] = providerManager->provider_->Refresh();
std::chrono::milliseconds interval = kFastRetryInterval_;
if (totalObjects > 0)
{
std::string key = providerManager->provider_->FindLatestKey();
auto latestTime =
providerManager->provider_->GetTimePointByKey(key);
auto latestTime = providerManager->provider_->GetTimePointByKey(key);
auto updatePeriod = providerManager->provider_->update_period();
auto lastModified = providerManager->provider_->last_modified();
auto sinceLastModified =
std::chrono::system_clock::now() - lastModified;
auto sinceLastModified = std::chrono::system_clock::now() - lastModified;
// For the default interval, assume products are updated at a
// constant rate. Expect the next product at a time based on the
@ -707,9 +727,7 @@ void RadarProductManagerImpl::RefreshData(
if (newObjects > 0)
{
Q_EMIT providerManager->NewDataAvailable(
providerManager->group_,
providerManager->product_,
latestTime);
providerManager->group_, providerManager->product_, latestTime);
}
}
else if (providerManager->refreshEnabled_)
@ -752,7 +770,6 @@ void RadarProductManagerImpl::RefreshData(
});
}
}
});
}
std::set<std::chrono::system_clock::time_point>
@ -1009,7 +1026,16 @@ void RadarProductManagerImpl::LoadNexradFileAsync(
{
boost::asio::post(threadPool_,
[=, &mutex]()
{ LoadNexradFile(load, request, mutex, time); });
{
try
{
LoadNexradFile(load, request, mutex, time);
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
void RadarProductManagerImpl::LoadNexradFile(
@ -1441,31 +1467,41 @@ void RadarProductManager::UpdateAvailableProducts()
logger_->debug("UpdateAvailableProducts()");
boost::asio::post(
p->threadPool_,
boost::asio::post(p->threadPool_,
[this]()
{
try
{
p->UpdateAvailableProductsSync();
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
void RadarProductManagerImpl::UpdateAvailableProductsSync()
{
auto level3ProviderManager =
p->GetLevel3ProviderManager(kDefaultLevel3Product_);
GetLevel3ProviderManager(kDefaultLevel3Product_);
level3ProviderManager->provider_->RequestAvailableProducts();
auto updatedAwipsIdList =
level3ProviderManager->provider_->GetAvailableProducts();
std::unique_lock lock {p->availableCategoryMutex_};
std::unique_lock lock {availableCategoryMutex_};
for (common::Level3ProductCategory category :
common::Level3ProductCategoryIterator())
{
const auto& products =
common::GetLevel3ProductsByCategory(category);
const auto& products = common::GetLevel3ProductsByCategory(category);
std::unordered_map<std::string, std::vector<std::string>>
availableProducts;
for (const auto& product : products)
{
const auto& awipsIds =
common::GetLevel3AwipsIdsByProduct(product);
const auto& awipsIds = common::GetLevel3AwipsIdsByProduct(product);
std::vector<std::string> availableAwipsIds;
@ -1481,24 +1517,23 @@ void RadarProductManager::UpdateAvailableProducts()
if (!availableAwipsIds.empty())
{
availableProducts.insert_or_assign(
product, std::move(availableAwipsIds));
availableProducts.insert_or_assign(product,
std::move(availableAwipsIds));
}
}
if (!availableProducts.empty())
{
p->availableCategoryMap_.insert_or_assign(
category, std::move(availableProducts));
availableCategoryMap_.insert_or_assign(category,
std::move(availableProducts));
}
else
{
p->availableCategoryMap_.erase(category);
availableCategoryMap_.erase(category);
}
}
Q_EMIT Level3ProductsChanged();
});
Q_EMIT self_->Level3ProductsChanged();
}
std::shared_ptr<RadarProductManager>

View file

@ -49,10 +49,17 @@ public:
boost::asio::post(threadPool_,
[this]()
{
try
{
main::Application::WaitForInitialization();
logger_->debug("Start Refresh");
Refresh();
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
@ -70,6 +77,7 @@ public:
}
void HandleMessage(std::shared_ptr<awips::TextProductMessage> message);
void RefreshAsync();
void Refresh();
boost::asio::thread_pool threadPool_ {1u};
@ -130,6 +138,8 @@ void TextEventManager::LoadFile(const std::string& filename)
boost::asio::post(p->threadPool_,
[=, this]()
{
try
{
awips::TextProductFile file;
@ -146,6 +156,11 @@ void TextEventManager::LoadFile(const std::string& filename)
{
p->HandleMessage(message);
}
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
@ -212,6 +227,22 @@ void TextEventManager::Impl::HandleMessage(
}
}
void TextEventManager::Impl::RefreshAsync()
{
boost::asio::post(threadPool_,
[this]()
{
try
{
Refresh();
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
void TextEventManager::Impl::Refresh()
{
logger_->trace("Refresh");
@ -257,7 +288,7 @@ void TextEventManager::Impl::Refresh()
}
else
{
Refresh();
RefreshAsync();
}
});
}

View file

@ -69,11 +69,13 @@ public:
void Pause();
void Play();
void PlaySync();
void
SelectTimeAsync(std::chrono::system_clock::time_point selectedTime = {});
std::pair<bool, bool>
SelectTime(std::chrono::system_clock::time_point selectedTime = {});
void StepAsync(Direction direction);
void Step(Direction direction);
boost::asio::thread_pool playThreadPool_ {1};
boost::asio::thread_pool selectThreadPool_ {1};
@ -405,8 +407,6 @@ void TimelineManager::Impl::UpdateCacheLimit(
void TimelineManager::Impl::Play()
{
using namespace std::chrono_literals;
if (animationState_ != types::AnimationState::Play)
{
animationState_ = types::AnimationState::Play;
@ -418,10 +418,24 @@ void TimelineManager::Impl::Play()
animationTimer_.cancel();
}
boost::asio::post(
playThreadPool_,
boost::asio::post(playThreadPool_,
[this]()
{
try
{
PlaySync();
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
void TimelineManager::Impl::PlaySync()
{
using namespace std::chrono_literals;
// Take a lock for time selection
std::unique_lock lock {selectTimeMutex_};
@ -504,14 +518,23 @@ void TimelineManager::Impl::Play()
logger_->warn("Play timer error: {}", e.message());
}
});
});
}
void TimelineManager::Impl::SelectTimeAsync(
std::chrono::system_clock::time_point selectedTime)
{
boost::asio::post(selectThreadPool_,
[=, this]() { SelectTime(selectedTime); });
[=, this]()
{
try
{
SelectTime(selectedTime);
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
std::pair<bool, bool> TimelineManager::Impl::SelectTime(
@ -597,10 +620,22 @@ std::pair<bool, bool> TimelineManager::Impl::SelectTime(
void TimelineManager::Impl::StepAsync(Direction direction)
{
boost::asio::post(
selectThreadPool_,
boost::asio::post(selectThreadPool_,
[=, this]()
{
try
{
Step(direction);
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
void TimelineManager::Impl::Step(Direction direction)
{
// Take a lock for time selection
std::unique_lock lock {selectTimeMutex_};
@ -614,8 +649,7 @@ void TimelineManager::Impl::StepAsync(Direction direction)
// Request active volume times
auto radarProductManager =
manager::RadarProductManager::Instance(radarSite_);
auto volumeTimes =
radarProductManager->GetActiveVolumeTimes(queryTime);
auto volumeTimes = radarProductManager->GetActiveVolumeTimes(queryTime);
if (volumeTimes.empty())
{
@ -636,8 +670,7 @@ void TimelineManager::Impl::StepAsync(Direction direction)
else
{
// Get the current element in the set
it = scwx::util::GetBoundedElementIterator(volumeTimes,
adjustedTime_);
it = scwx::util::GetBoundedElementIterator(volumeTimes, adjustedTime_);
}
if (it == volumeTimes.cend())
@ -681,7 +714,6 @@ void TimelineManager::Impl::StepAsync(Direction direction)
Q_EMIT self_->SelectedTimeUpdated(adjustedTime_);
}
}
});
}
std::shared_ptr<TimelineManager> TimelineManager::Instance()

View file

@ -325,9 +325,16 @@ void AlertLayerHandler::UpdateAlerts()
logger_->warn("Alert update timer error: {}", e.message());
}
else
{
try
{
UpdateAlerts();
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
}
});
}

View file

@ -1637,6 +1637,8 @@ void MapWidgetImpl::RadarProductManagerConnect()
boost::asio::post(
threadPool_,
[=, this]()
{
try
{
if (group == common::RadarProductGroup::Level2)
{
@ -1648,6 +1650,11 @@ void MapWidgetImpl::RadarProductManagerConnect()
radarProductManager_->LoadLevel3Data(
product, latestTime, request);
}
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
},
@ -1672,7 +1679,10 @@ void MapWidgetImpl::InitializeNewRadarProductView(
boost::asio::post(threadPool_,
[=, this]()
{
auto radarProductView = context_->radar_product_view();
try
{
auto radarProductView =
context_->radar_product_view();
std::string colorTableFile =
settings::PaletteSettings::Instance()
@ -1688,6 +1698,11 @@ void MapWidgetImpl::InitializeNewRadarProductView(
}
radarProductView->Initialize();
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
if (map_ != nullptr)

View file

@ -45,6 +45,7 @@ public:
~Impl() { threadPool_.join(); }
void ConnectSignals();
void ReloadDataSync();
boost::asio::thread_pool threadPool_ {1};
@ -170,72 +171,77 @@ void PlacefileLayer::Deinitialize()
void PlacefileLayer::ReloadData()
{
boost::asio::post(
p->threadPool_,
boost::asio::post(p->threadPool_,
[this]()
{
logger_->debug("ReloadData: {}", p->placefileName_);
try
{
p->ReloadDataSync();
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
std::unique_lock lock {p->dataMutex_};
void PlacefileLayer::Impl::ReloadDataSync()
{
logger_->debug("ReloadData: {}", placefileName_);
std::unique_lock lock {dataMutex_};
std::shared_ptr<manager::PlacefileManager> placefileManager =
manager::PlacefileManager::Instance();
auto placefile = placefileManager->placefile(p->placefileName_);
auto placefile = placefileManager->placefile(placefileName_);
if (placefile == nullptr)
{
return;
}
// Start draw items
p->placefileIcons_->StartIcons();
p->placefileImages_->StartImages(placefile->name());
p->placefileLines_->StartLines();
p->placefilePolygons_->StartPolygons();
p->placefileTriangles_->StartTriangles();
p->placefileText_->StartText();
placefileIcons_->StartIcons();
placefileImages_->StartImages(placefile->name());
placefileLines_->StartLines();
placefilePolygons_->StartPolygons();
placefileTriangles_->StartTriangles();
placefileText_->StartText();
p->placefileIcons_->SetIconFiles(placefile->icon_files(),
placefile->name());
p->placefileText_->SetFonts(
placefileManager->placefile_fonts(p->placefileName_));
placefileIcons_->SetIconFiles(placefile->icon_files(), placefile->name());
placefileText_->SetFonts(placefileManager->placefile_fonts(placefileName_));
for (auto& drawItem : placefile->GetDrawItems())
{
switch (drawItem->itemType_)
{
case gr::Placefile::ItemType::Text:
p->placefileText_->AddText(
std::static_pointer_cast<gr::Placefile::TextDrawItem>(
drawItem));
placefileText_->AddText(
std::static_pointer_cast<gr::Placefile::TextDrawItem>(drawItem));
break;
case gr::Placefile::ItemType::Icon:
p->placefileIcons_->AddIcon(
std::static_pointer_cast<gr::Placefile::IconDrawItem>(
drawItem));
placefileIcons_->AddIcon(
std::static_pointer_cast<gr::Placefile::IconDrawItem>(drawItem));
break;
case gr::Placefile::ItemType::Line:
p->placefileLines_->AddLine(
std::static_pointer_cast<gr::Placefile::LineDrawItem>(
drawItem));
placefileLines_->AddLine(
std::static_pointer_cast<gr::Placefile::LineDrawItem>(drawItem));
break;
case gr::Placefile::ItemType::Polygon:
p->placefilePolygons_->AddPolygon(
std::static_pointer_cast<gr::Placefile::PolygonDrawItem>(
drawItem));
placefilePolygons_->AddPolygon(
std::static_pointer_cast<gr::Placefile::PolygonDrawItem>(drawItem));
break;
case gr::Placefile::ItemType::Image:
p->placefileImages_->AddImage(
std::static_pointer_cast<gr::Placefile::ImageDrawItem>(
drawItem));
placefileImages_->AddImage(
std::static_pointer_cast<gr::Placefile::ImageDrawItem>(drawItem));
break;
case gr::Placefile::ItemType::Triangles:
p->placefileTriangles_->AddTriangles(
placefileTriangles_->AddTriangles(
std::static_pointer_cast<gr::Placefile::TrianglesDrawItem>(
drawItem));
break;
@ -246,15 +252,14 @@ void PlacefileLayer::ReloadData()
}
// Finish draw items
p->placefileIcons_->FinishIcons();
p->placefileImages_->FinishImages();
p->placefileLines_->FinishLines();
p->placefilePolygons_->FinishPolygons();
p->placefileTriangles_->FinishTriangles();
p->placefileText_->FinishText();
placefileIcons_->FinishIcons();
placefileImages_->FinishImages();
placefileLines_->FinishLines();
placefilePolygons_->FinishPolygons();
placefileTriangles_->FinishTriangles();
placefileText_->FinishText();
Q_EMIT DataReloaded();
});
Q_EMIT self_->DataReloaded();
}
} // namespace map

View file

@ -119,9 +119,16 @@ void AlertProxyModelImpl::UpdateAlerts()
logger_->warn("Alert update timer error: {}", e.message());
}
else
{
try
{
UpdateAlerts();
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
}
});
}

View file

@ -237,10 +237,19 @@ void OverlayProductView::Impl::LoadProduct(
}
// Load file
boost::asio::post(
threadPool_,
boost::asio::post(threadPool_,
[=, this]()
{ radarProductManager_->LoadLevel3Data(product, time, request); });
{
try
{
radarProductManager_->LoadLevel3Data(
product, time, request);
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
void OverlayProductView::Impl::ResetProducts()

View file

@ -121,7 +121,18 @@ void RadarProductView::SelectTime(std::chrono::system_clock::time_point time)
void RadarProductView::Update()
{
boost::asio::post(thread_pool(), [this]() { ComputeSweep(); });
boost::asio::post(thread_pool(),
[this]()
{
try
{
ComputeSweep();
}
catch (const std::exception& ex)
{
logger_->error(ex.what());
}
});
}
bool RadarProductView::IsInitialized() const