From db259584cacc8003271f839e49bc2963420af1b4 Mon Sep 17 00:00:00 2001 From: Rene Gollent Date: Thu, 19 Sep 2013 11:45:21 +0200 Subject: [PATCH] HaikuDepot: Flesh out PackageActions a bit. - Add background worker thread for asynchronous processing of package actions. - Implement InstallPackageAction. As such, installing packages now actually works. - Wrap requests in exception handlers, as the package manager uses those for various error cases. Fixes several cases of HaikuDepot spontaneously closing if a problem occurred. Still TODO: user error reporting, errors are currently reported on the console. --- src/apps/haiku-depot/MainWindow.cpp | 27 ++++-- src/apps/haiku-depot/PackageInfoView.cpp | 9 +- src/apps/haiku-depot/PackageManager.cpp | 115 +++++++++++++++++++++-- src/apps/haiku-depot/PackageManager.h | 24 ++++- 4 files changed, 155 insertions(+), 20 deletions(-) diff --git a/src/apps/haiku-depot/MainWindow.cpp b/src/apps/haiku-depot/MainWindow.cpp index c25eaeeed3..e44c7a9dc8 100644 --- a/src/apps/haiku-depot/MainWindow.cpp +++ b/src/apps/haiku-depot/MainWindow.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include "package/RepositoryCache.h" @@ -47,6 +48,8 @@ enum { using namespace BPackageKit; +using BManager::BPrivate::BException; +using BManager::BPrivate::BFatalErrorException; typedef std::map PackageInfoMap; @@ -252,11 +255,17 @@ MainWindow::_RefreshRepositories(bool force) if (roster.GetRepositoryCache(repoName, &cache) != B_OK || force) { - BRefreshRepositoryRequest refreshRequest(context, repoConfig); - result = refreshRequest.Process(); - if (result != B_OK) { - // TODO: notify user - continue; + try { + BRefreshRepositoryRequest refreshRequest(context, repoConfig); + + result = refreshRequest.Process(); + } catch (BFatalErrorException ex) { + fprintf(stderr, "Fatal error occurred while refreshing " + "repository: %s (%s)\n", ex.Message().String(), + ex.Details().String()); + } catch (BException ex) { + fprintf(stderr, "Exception occurred while refreshing " + "repository: %s\n", ex.Message().String()); } } } @@ -279,8 +288,14 @@ MainWindow::_RefreshPackageList() depots[repoName] = DepotInfo(repoName); } - fPackageManager.Init(PackageManager::B_ADD_INSTALLED_REPOSITORIES + try { + fPackageManager.Init(PackageManager::B_ADD_INSTALLED_REPOSITORIES | PackageManager::B_ADD_REMOTE_REPOSITORIES); + } catch (BException ex) { + fprintf(stderr, "Exception occurred while initializing PackageManager:" + "%s\n", ex.Message().String()); + return; + } BObjectList packages; result = fPackageManager.Solver()->FindPackages("", diff --git a/src/apps/haiku-depot/PackageInfoView.cpp b/src/apps/haiku-depot/PackageInfoView.cpp index 75691ca67f..0244894cf4 100644 --- a/src/apps/haiku-depot/PackageInfoView.cpp +++ b/src/apps/haiku-depot/PackageInfoView.cpp @@ -514,10 +514,13 @@ public: const PackageActionRef& action = fPackageActions.ItemAt(index); if (action.Get() != NULL) { - status_t result = action->Perform(); + PackageActionList actions; + actions.Add(action); + status_t result = fPackageManager + ->SchedulePackageActions(actions); if (result != B_OK) { - fprintf(stderr, "Package action failed: %s '%s'\n", - action->Label(), + fprintf(stderr, "Failed to schedule action: " + "%s '%s'\n", action->Label(), action->Package().Title().String()); } } diff --git a/src/apps/haiku-depot/PackageManager.cpp b/src/apps/haiku-depot/PackageManager.cpp index 54e47d7580..acab1d1001 100644 --- a/src/apps/haiku-depot/PackageManager.cpp +++ b/src/apps/haiku-depot/PackageManager.cpp @@ -11,14 +11,19 @@ #include "PackageManager.h" +#include + #include #include +#include #include #include #include #include +#include "AutoLocker.h" + #undef B_TRANSLATION_CONTEXT #define B_TRANSLATION_CONTEXT "PackageManager" @@ -27,13 +32,17 @@ using BPackageKit::BRefreshRepositoryRequest; using BPackageKit::DownloadFileRequest; using namespace BPackageKit::BPrivate; +using BPackageKit::BManager::BPrivate::BException; +using BPackageKit::BManager::BPrivate::BFatalErrorException; // #pragma mark - PackageAction -PackageAction::PackageAction(const PackageInfo& package) +PackageAction::PackageAction(const PackageInfo& package, + PackageManager* manager) : + fPackageManager(manager), fPackage(package) { } @@ -49,9 +58,9 @@ PackageAction::~PackageAction() class InstallPackageAction : public PackageAction { public: - InstallPackageAction(const PackageInfo& package) + InstallPackageAction(const PackageInfo& package, PackageManager* manager) : - PackageAction(package) + PackageAction(package, manager) { } @@ -62,8 +71,20 @@ public: virtual status_t Perform() { - // TODO: Trigger asynchronous installation of the package - return B_ERROR; + const char* packageName = Package().Title().String(); + try { + fPackageManager->Install(&packageName, 1); + } catch (BFatalErrorException ex) { + fprintf(stderr, "Fatal error occurred while installing package " + "%s: %s (%s)\n", packageName, ex.Message().String(), + ex.Details().String()); + return ex.Error(); + } catch (BException ex) { + fprintf(stderr, "Exception occurred while installing package " + "%s: %s\n", packageName, ex.Message().String()); + } + + return B_OK; } }; @@ -83,14 +104,26 @@ PackageManager::PackageManager(BPackageInstallationLocation location) fInstallationInterface = &fClientInstallationInterface; fRequestHandler = this; fUserInteractionHandler = this; + + fPendingActionsSem = create_sem(0, "PendingPackageActions"); + if (fPendingActionsSem >= 0) { + fPendingActionsWorker = spawn_thread(&_PackageActionWorker, + "Planet Express", B_NORMAL_PRIORITY, this); + if (fPendingActionsWorker >= 0) + resume_thread(fPendingActionsWorker); + } } PackageManager::~PackageManager() { + delete_sem(fPendingActionsSem); + status_t result; + wait_for_thread(fPendingActionsWorker, &result); } + PackageState PackageManager::GetPackageState(const PackageInfo& package) { @@ -113,16 +146,44 @@ PackageManager::GetPackageActions(const PackageInfo& package) // * activated // If the package is not installed, it can be // * installed (and it will be automatically activated) - actionList.Add(PackageActionRef(new InstallPackageAction(package), true)); + actionList.Add(PackageActionRef(new InstallPackageAction(package, this), + true)); return actionList; } +status_t +PackageManager::SchedulePackageActions(PackageActionList& list) +{ + AutoLocker lock(&fPendingActionsLock); + for (int32 i = 0; i < list.CountItems(); i++) { + if (!fPendingActions.Add(list.ItemAtFast(i))) { + return B_NO_MEMORY; + } + } + + return release_sem_etc(fPendingActionsSem, list.CountItems(), 0); +} + + status_t PackageManager::RefreshRepository(const BRepositoryConfig& repoConfig) { - return BRefreshRepositoryRequest(fContext, repoConfig).Process(); + status_t result; + try { + result = BRefreshRepositoryRequest(fContext, repoConfig).Process(); + } catch (BFatalErrorException ex) { + fprintf(stderr, "Fatal error occurred while refreshing repository: " + "%s (%s)\n", ex.Message().String(), ex.Details().String()); + result = ex.Error(); + } catch (BException ex) { + fprintf(stderr, "Exception occurred while refreshing " + "repository: %s\n", ex.Message().String()); + result = B_ERROR; + } + + return result; } @@ -130,8 +191,22 @@ status_t PackageManager::DownloadPackage(const BString& fileURL, const BEntry& targetEntry, const BString& checksum) { - return DownloadFileRequest(fContext, fileURL, targetEntry, checksum) - .Process(); + status_t result; + try { + result = DownloadFileRequest(fContext, fileURL, targetEntry, checksum) + .Process(); + } catch (BFatalErrorException ex) { + fprintf(stderr, "Fatal error occurred while downloading package: " + "%s: %s (%s)\n", fileURL.String(), ex.Message().String(), + ex.Details().String()); + result = ex.Error(); + } catch (BException ex) { + fprintf(stderr, "Exception occurred while downloading package " + "%s: %s\n", fileURL.String(), ex.Message().String()); + result = B_ERROR; + } + + return result; } @@ -176,3 +251,25 @@ PackageManager::ProgressApplyingChangesDone(InstalledRepository& repository) { // TODO: implement } + + +status_t +PackageManager::_PackageActionWorker(void* arg) +{ + PackageManager* manager = reinterpret_cast(arg); + + while (acquire_sem(manager->fPendingActionsSem) == B_OK) { + PackageActionRef ref; + { + AutoLocker lock(&manager->fPendingActionsLock); + ref = manager->fPendingActions.ItemAt(0); + if (ref.Get() == NULL) + break; + manager->fPendingActions.Remove(0); + } + + ref->Perform(); + } + + return 0; +} diff --git a/src/apps/haiku-depot/PackageManager.h b/src/apps/haiku-depot/PackageManager.h index 93a39e2972..a469594698 100644 --- a/src/apps/haiku-depot/PackageManager.h +++ b/src/apps/haiku-depot/PackageManager.h @@ -8,6 +8,8 @@ #ifndef PACKAGE_MANAGER_H #define PACKAGE_MANAGER_H +#include + #include #include @@ -16,9 +18,13 @@ #include "PackageInfo.h" +class PackageManager; + + class PackageAction : public BReferenceable { public: - PackageAction(const PackageInfo& package); + PackageAction(const PackageInfo& package, + PackageManager* manager); virtual ~PackageAction(); virtual const char* Label() const = 0; @@ -31,6 +37,9 @@ public: const PackageInfo& Package() const { return fPackage; } +protected: + PackageManager* fPackageManager; + private: PackageInfo fPackage; }; @@ -57,6 +66,9 @@ public: virtual PackageState GetPackageState(const PackageInfo& package); virtual PackageActionList GetPackageActions(const PackageInfo& package); + status_t SchedulePackageActions( + PackageActionList& list); + private: // RequestHandler virtual status_t RefreshRepository( @@ -79,12 +91,20 @@ private: virtual void ProgressApplyingChangesDone( InstalledRepository& repository); +private: + static status_t _PackageActionWorker(void* arg); + private: DecisionProvider fDecisionProvider; JobStateListener fJobStateListener; BContext fContext; BPackageManager::ClientInstallationInterface - fClientInstallationInterface; + fClientInstallationInterface; + + thread_id fPendingActionsWorker; + PackageActionList fPendingActions; + BLocker fPendingActionsLock; + sem_id fPendingActionsSem; }; #endif // PACKAGE_MANAGER_H