From 32cae72412f4e1503d124ee00a09832022a4840f Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Fri, 7 Feb 2014 00:17:34 +0100 Subject: [PATCH] package daemon: Handle location info request in app thread * ... instead of queuing it for the job thread. The advantage is that the request will be handled immediately and clients won't have to wait for transactions (which may even require user feedback) to finish. It complicates Volume a bit, since there are now two threads that may access it. The shared data have been moved to a State object which is protected by a lock. * For commit transaction requests check whether another package request is already pending/in progress before queuing a job. Fail immediately, if there is. Fixes bug #10039. --- headers/private/package/DaemonDefs.h | 1 + src/kits/package/DaemonClient.cpp | 5 +- src/servers/package/JobQueue.cpp | 27 ++- src/servers/package/JobQueue.h | 15 +- src/servers/package/PackageManager.cpp | 2 +- src/servers/package/Root.cpp | 166 +++++++++++++---- src/servers/package/Root.h | 10 +- src/servers/package/Volume.cpp | 248 ++++++++++++++++++++----- src/servers/package/Volume.h | 33 +++- 9 files changed, 408 insertions(+), 99 deletions(-) diff --git a/headers/private/package/DaemonDefs.h b/headers/private/package/DaemonDefs.h index 76a323133a..0f9f6e62bd 100644 --- a/headers/private/package/DaemonDefs.h +++ b/headers/private/package/DaemonDefs.h @@ -18,6 +18,7 @@ namespace BPrivate { enum BDaemonError { B_DAEMON_OK = 0, + B_DAEMON_INSTALLATION_LOCATION_BUSY, B_DAEMON_CHANGE_COUNT_MISMATCH, B_DAEMON_BAD_REQUEST, B_DAEMON_NO_SUCH_PACKAGE, diff --git a/src/kits/package/DaemonClient.cpp b/src/kits/package/DaemonClient.cpp index b0c235ec92..bbca3c6aae 100644 --- a/src/kits/package/DaemonClient.cpp +++ b/src/kits/package/DaemonClient.cpp @@ -320,6 +320,9 @@ BDaemonClient::BCommitTransactionResult::FullErrorMessage() const const char* errorString; if (fError > 0) { switch ((BDaemonError)fError) { + case B_DAEMON_INSTALLATION_LOCATION_BUSY: + errorString = "another package operation already in progress"; + break; case B_DAEMON_CHANGE_COUNT_MISMATCH: errorString = "transaction out of date"; break; @@ -339,7 +342,7 @@ BDaemonClient::BCommitTransactionResult::FullErrorMessage() const } } else errorString = strerror(fError); - + BString result; if (!fErrorMessage.IsEmpty()) { result = fErrorMessage; diff --git a/src/servers/package/JobQueue.cpp b/src/servers/package/JobQueue.cpp index 8572a088c3..c46f9725c1 100644 --- a/src/servers/package/JobQueue.cpp +++ b/src/servers/package/JobQueue.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Copyright 2013-2014, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -12,6 +12,9 @@ #include +// #pragma mark - JobQueue + + JobQueue::JobQueue() : fMutexInitialized(false), @@ -97,3 +100,25 @@ JobQueue::DequeueJob() return NULL; } + + +void +JobQueue::DeleteJobs(Filter* filter) +{ + PthreadMutexLocker mutexLocker(fMutex); + + for (JobList::Iterator it = fJobs.GetIterator(); Job* job = it.Next();) { + if (filter->FilterJob(job)) { + it.Remove(); + delete job; + } + } +} + + +// #pragma mark - Filter + + +JobQueue::Filter::~Filter() +{ +} diff --git a/src/servers/package/JobQueue.h b/src/servers/package/JobQueue.h index 4d6d93e5fa..a878d6a018 100644 --- a/src/servers/package/JobQueue.h +++ b/src/servers/package/JobQueue.h @@ -1,5 +1,5 @@ /* - * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Copyright 2013-2014, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -15,6 +15,9 @@ class JobQueue { +public: + class Filter; + public: JobQueue(); ~JobQueue(); @@ -27,6 +30,8 @@ public: Job* DequeueJob(); // returns a reference + void DeleteJobs(Filter* filter); + private: typedef DoublyLinkedList JobList; @@ -40,4 +45,12 @@ private: }; +class JobQueue::Filter { +public: + virtual ~Filter(); + + virtual bool FilterJob(Job* job) = 0; +}; + + #endif // JOB_QUEUE_H diff --git a/src/servers/package/PackageManager.cpp b/src/servers/package/PackageManager.cpp index 02222020d6..97deb2cf7e 100644 --- a/src/servers/package/PackageManager.cpp +++ b/src/servers/package/PackageManager.cpp @@ -132,7 +132,7 @@ PackageManager::InitInstalledRepository(InstalledRepository& repository) if (Volume* volume = fRoot->GetVolume(repository.Location())) { for (PackageFileNameHashTable::Iterator it - = volume->PackagesByFileName().GetIterator(); it.HasNext();) { + = volume->PackagesByFileNameIterator(); it.HasNext();) { Package* package = it.Next(); if (package->IsActive()) { BSolverPackage* solverPackage; diff --git a/src/servers/package/Root.cpp b/src/servers/package/Root.cpp index 437d42a316..b43346558f 100644 --- a/src/servers/package/Root.cpp +++ b/src/servers/package/Root.cpp @@ -30,13 +30,36 @@ using namespace BPackageKit::BPrivate; using namespace BPackageKit::BManager::BPrivate; +static const bigtime_t kCommunicationTimeout = 1000000; + + +// #pragma mark - AbstractVolumeJob + + +struct Root::AbstractVolumeJob : public Job { + AbstractVolumeJob(Volume* volume) + : + fVolume(volume) + { + } + + Volume* GetVolume() const + { + return fVolume; + } + +protected: + Volume* fVolume; +}; + + // #pragma mark - VolumeJob -struct Root::VolumeJob : public Job { +struct Root::VolumeJob : public AbstractVolumeJob { VolumeJob(Volume* volume, void (Root::*method)(Volume*)) : - fVolume(volume), + AbstractVolumeJob(volume), fMethod(method) { } @@ -47,25 +70,49 @@ struct Root::VolumeJob : public Job { } private: - Volume* fVolume; void (Root::*fMethod)(Volume*); }; -// #pragma mark - RequestJob +// #pragma mark - ProcessNodeMonitorEventsJob -struct Root::RequestJob : public Job { - RequestJob(Root* root, BMessage* message) +struct Root::ProcessNodeMonitorEventsJob : public VolumeJob { + ProcessNodeMonitorEventsJob(Volume* volume, void (Root::*method)(Volume*)) : + VolumeJob(volume, method) + { + fVolume->PackageJobPending(); + } + + ~ProcessNodeMonitorEventsJob() + { + fVolume->PackageJobFinished(); + } +}; + + +// #pragma mark - CommitTransactionJob + + +struct Root::CommitTransactionJob : public AbstractVolumeJob { + CommitTransactionJob(Root* root, Volume* volume, BMessage* message) + : + AbstractVolumeJob(volume), fRoot(root), fMessage(message) { + fVolume->PackageJobPending(); + } + + ~CommitTransactionJob() + { + fVolume->PackageJobFinished(); } virtual void Do() { - fRoot->_HandleRequest(fMessage.Get()); + fRoot->_CommitTransaction(fVolume, fMessage.Get()); } private: @@ -74,6 +121,27 @@ private: }; +// #pragma mark - VolumeJobFilter + + +struct Root::VolumeJobFilter : public ::JobQueue::Filter { + VolumeJobFilter(Volume* volume) + : + fVolume(volume) + { + } + + virtual bool FilterJob(Job* job) + { + AbstractVolumeJob* volumeJob = dynamic_cast(job); + return volumeJob != NULL && volumeJob->GetVolume() == fVolume; + } + +private: + Volume* fVolume; +}; + + // #pragma mark - Root @@ -236,21 +304,64 @@ Root::GetVolume(BPackageInstallationLocation location) void Root::HandleRequest(BMessage* message) { - RequestJob* job = new(std::nothrow) RequestJob(this, message); - if (job == NULL) { - delete message; + ObjectDeleter messageDeleter(message); + + // get the location and the volume + int32 location; + if (message->FindInt32("location", &location) != B_OK + || location < 0 + || location >= B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT) { return; } - _QueueJob(job); + AutoLocker locker(fLock); + + Volume* volume = GetVolume((BPackageInstallationLocation)location); + if (volume == NULL) + return; + + switch (message->what) { + case B_MESSAGE_GET_INSTALLATION_LOCATION_INFO: + volume->HandleGetLocationInfoRequest(message); + break; + + case B_MESSAGE_COMMIT_TRANSACTION: + { + // The B_MESSAGE_COMMIT_TRANSACTION request must be handled in the + // job thread. But only queue a job, if there aren't package jobs + // pending already. + if (volume->IsPackageJobPending()) { + BMessage reply(B_MESSAGE_COMMIT_TRANSACTION_REPLY); + if (reply.AddInt32("error", B_DAEMON_INSTALLATION_LOCATION_BUSY) + == B_OK) { + message->SendReply(&reply, (BHandler*)NULL, + kCommunicationTimeout); + } + return; + } + + CommitTransactionJob* job = new(std::nothrow) CommitTransactionJob( + this, volume, message); + if (job == NULL) + return; + + messageDeleter.Detach(); + + _QueueJob(job); + break; + } + + default: + break; + } } void Root::VolumeNodeMonitorEventOccurred(Volume* volume) { - _QueueJob( - new(std::nothrow) VolumeJob(volume, &Root::_ProcessNodeMonitorEvents)); + _QueueJob(new(std::nothrow) ProcessNodeMonitorEventsJob(volume, + &Root::_ProcessNodeMonitorEvents)); } @@ -318,6 +429,10 @@ Root::_InitPackages(Volume* volume) void Root::_DeleteVolume(Volume* volume) { + // delete all pending jobs for that volume + VolumeJobFilter filter(volume); + fJobQueue.DeleteJobs(&filter); + delete volume; } @@ -364,30 +479,9 @@ Root::_ProcessNodeMonitorEvents(Volume* volume) void -Root::_HandleRequest(BMessage* message) +Root::_CommitTransaction(Volume* volume, BMessage* message) { - int32 location; - if (message->FindInt32("location", &location) != B_OK - || location < 0 - || location >= B_PACKAGE_INSTALLATION_LOCATION_ENUM_COUNT) { - return; - } - - // get the volume and let it handle the message - AutoLocker locker(fLock); - Volume* volume = GetVolume((BPackageInstallationLocation)location); - locker.Unlock(); - - if (volume != NULL) { - switch (message->what) { - case B_MESSAGE_GET_INSTALLATION_LOCATION_INFO: - volume->HandleGetLocationInfoRequest(message); - break; - case B_MESSAGE_COMMIT_TRANSACTION: - volume->HandleCommitTransactionRequest(message); - break; - } - } + volume->HandleCommitTransactionRequest(message); } diff --git a/src/servers/package/Root.h b/src/servers/package/Root.h index 1ca6b96c6e..0d67ab7e65 100644 --- a/src/servers/package/Root.h +++ b/src/servers/package/Root.h @@ -55,10 +55,13 @@ protected: virtual void LastReferenceReleased(); private: + struct AbstractVolumeJob; struct VolumeJob; - struct RequestJob; + struct ProcessNodeMonitorEventsJob; + struct CommitTransactionJob; + struct VolumeJobFilter; - friend struct RequestJob; + friend struct CommitTransactionJob; private: Volume** _GetVolume(PackageFSMountType mountType); @@ -67,7 +70,8 @@ private: void _InitPackages(Volume* volume); void _DeleteVolume(Volume* volume); void _ProcessNodeMonitorEvents(Volume* volume); - void _HandleRequest(BMessage* message); + void _CommitTransaction(Volume* volume, + BMessage* message); status_t _QueueJob(Job* job); diff --git a/src/servers/package/Volume.cpp b/src/servers/package/Volume.cpp index 2d445ca162..5f0708471f 100644 --- a/src/servers/package/Volume.cpp +++ b/src/servers/package/Volume.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Copyright 2013-2014, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -105,6 +105,146 @@ private: }; +// #pragma mark - State + + +struct Volume::State { + + State() + : + fLock("volume state"), + fPackagesByFileName(), + fPackagesByNodeRef(), + fChangeCount(0), + fPendingPackageJobCount(0) + { + } + + ~State() + { + fPackagesByFileName.Clear(); + + Package* package = fPackagesByNodeRef.Clear(true); + while (package != NULL) { + Package* next = package->NodeRefHashTableNext(); + delete package; + package = next; + } + } + + bool Init() + { + return fLock.InitCheck() == B_OK && fPackagesByFileName.Init() == B_OK + && fPackagesByNodeRef.Init() == B_OK; + } + + bool Lock() + { + return fLock.Lock(); + } + + void Unlock() + { + fLock.Unlock(); + } + + int64 ChangeCount() const + { + return fChangeCount; + } + + Package* FindPackage(const char* name) const + { + return fPackagesByFileName.Lookup(name); + } + + Package* FindPackage(const node_ref& nodeRef) const + { + return fPackagesByNodeRef.Lookup(nodeRef); + } + + PackageFileNameHashTable::Iterator ByFileNameIterator() const + { + return fPackagesByFileName.GetIterator(); + } + + PackageNodeRefHashTable::Iterator ByNodeRefIterator() const + { + return fPackagesByNodeRef.GetIterator(); + } + + void AddPackage(Package* package) + { + AutoLocker locker(fLock); + fPackagesByFileName.Insert(package); + fPackagesByNodeRef.Insert(package); + } + + void RemovePackage(Package* package) + { + AutoLocker locker(fLock); + _RemovePackage(package); + } + + void SetPackageActive(Package* package, bool active) + { + AutoLocker locker(fLock); + package->SetActive(active); + } + + void ActivationChanged(const PackageSet& activatedPackage, + const PackageSet& deactivatePackages) + { + AutoLocker locker(fLock); + + for (PackageSet::iterator it = activatedPackage.begin(); + it != activatedPackage.end(); ++it) { + (*it)->SetActive(true); + fChangeCount++; + } + + for (PackageSet::iterator it = deactivatePackages.begin(); + it != deactivatePackages.end(); ++it) { + Package* package = *it; + _RemovePackage(package); + delete package; + } + } + + void PackageJobPending() + { + atomic_add(&fPendingPackageJobCount, 1); + } + + + void PackageJobFinished() + { + atomic_add(&fPendingPackageJobCount, -1); + } + + + bool IsPackageJobPending() const + { + return fPendingPackageJobCount != 0; + } + +private: + void _RemovePackage(Package* package) + { + fPackagesByFileName.Remove(package); + fPackagesByNodeRef.Remove(package); + fChangeCount++; + } + +private: + BLocker fLock; + PackageFileNameHashTable fPackagesByFileName; + PackageNodeRefHashTable fPackagesByNodeRef; + int64 fChangeCount; + int32 fPendingPackageJobCount; +}; + + // #pragma mark - CommitTransactionHandler @@ -159,7 +299,7 @@ struct Volume::CommitTransactionHandler { BMessage* reply) { // check the change count - if (transaction.ChangeCount() != fVolume->fChangeCount) + if (transaction.ChangeCount() != fVolume->fState->ChangeCount()) throw Exception(B_DAEMON_CHANGE_COUNT_MISMATCH); // collect the packages to deactivate @@ -230,7 +370,7 @@ private: for (int32 i = 0; i < packagesToDeactivateCount; i++) { BString packageName = packagesToDeactivate.StringAt(i); - Package* package = fVolume->fPackagesByFileName.Lookup(packageName); + Package* package = fVolume->fState->FindPackage(packageName); if (package == NULL) { throw Exception(B_DAEMON_NO_SUCH_PACKAGE, "no such package", packageName); @@ -285,7 +425,7 @@ private: BString packageName = packagesToActivate.StringAt(i); // make sure it doesn't clash with an already existing package - Package* package = fVolume->fPackagesByFileName.Lookup(packageName); + Package* package = fVolume->fState->FindPackage(packageName); if (package != NULL) { if (fPackagesAlreadyAdded.find(package) != fPackagesAlreadyAdded.end()) { @@ -1341,14 +1481,12 @@ Volume::Volume(BLooper* looper) fPackagesDirectoryRef(), fRoot(NULL), fListener(NULL), - fPackagesByFileName(), - fPackagesByNodeRef(), + fState(NULL), fPendingNodeMonitorEventsLock("pending node monitor events"), fPendingNodeMonitorEvents(), fNodeMonitorEventHandleTime(0), fPackagesToBeActivated(), fPackagesToBeDeactivated(), - fChangeCount(0), fLocationInfoReply(B_MESSAGE_GET_INSTALLATION_LOCATION_INFO_REPLY) { looper->AddHandler(this); @@ -1360,21 +1498,15 @@ Volume::~Volume() Unmounted(); // need for error case in InitPackages() - fPackagesByFileName.Clear(); - - Package* package = fPackagesByNodeRef.Clear(true); - while (package != NULL) { - Package* next = package->NodeRefHashTableNext(); - delete package; - package = next; - } + delete fState; } status_t Volume::Init(const node_ref& rootDirectoryRef, node_ref& _packageRootRef) { - if (fPackagesByFileName.Init() != B_OK || fPackagesByNodeRef.Init() != B_OK) + fState = new(std::nothrow) State; + if (fState == NULL || !fState->Init()) RETURN_ERROR(B_NO_MEMORY); fRootDirectoryRef = rootDirectoryRef; @@ -1481,8 +1613,8 @@ Volume::InitPackages(Listener* listener) status_t Volume::AddPackagesToRepository(BSolverRepository& repository, bool activeOnly) { - for (PackageFileNameHashTable::Iterator it - = fPackagesByFileName.GetIterator(); it.HasNext();) { + for (PackageFileNameHashTable::Iterator it = fState->ByFileNameIterator(); + it.HasNext();) { Package* package = it.Next(); if (activeOnly && !package->IsActive()) continue; @@ -1589,10 +1721,13 @@ INFORM("Volume::InitialVerify(%p, %p)\n", nextVolume, nextNextVolume); void Volume::HandleGetLocationInfoRequest(BMessage* message) { + AutoLocker stateLocker(fState); + // If the cached reply message is up-to-date, just send it. int64 changeCount; if (fLocationInfoReply.FindInt64("change count", &changeCount) == B_OK - && changeCount == fChangeCount) { + && changeCount == fState->ChangeCount()) { + stateLocker.Unlock(); message->SendReply(&fLocationInfoReply, (BHandler*)NULL, kCommunicationTimeout); return; @@ -1612,8 +1747,8 @@ Volume::HandleGetLocationInfoRequest(BMessage* message) return; } - for (PackageFileNameHashTable::Iterator it - = fPackagesByFileName.GetIterator(); it.HasNext();) { + for (PackageFileNameHashTable::Iterator it = fState->ByFileNameIterator(); + it.HasNext();) { Package* package = it.Next(); const char* fieldName = package->IsActive() ? "active packages" : "inactive packages"; @@ -1625,8 +1760,12 @@ Volume::HandleGetLocationInfoRequest(BMessage* message) } } - if (fLocationInfoReply.AddInt64("change count", fChangeCount) != B_OK) + if (fLocationInfoReply.AddInt64("change count", fState->ChangeCount()) + != B_OK) { return; + } + + stateLocker.Unlock(); message->SendReply(&fLocationInfoReply, (BHandler*)NULL, kCommunicationTimeout); @@ -1670,6 +1809,27 @@ Volume::HandleCommitTransactionRequest(BMessage* message) } +void +Volume::PackageJobPending() +{ + fState->PackageJobPending(); +} + + +void +Volume::PackageJobFinished() +{ + fState->PackageJobFinished(); +} + + +bool +Volume::IsPackageJobPending() const +{ + return fState->IsPackageJobPending(); +} + + void Volume::Unmounted() { @@ -1738,6 +1898,13 @@ Volume::Location() const } +PackageFileNameHashTable::Iterator +Volume::PackagesByFileNameIterator() const +{ + return fState->ByFileNameIterator(); +} + + int Volume::OpenRootDirectory() const { @@ -1852,7 +2019,7 @@ Volume::CreateTransaction(BPackageInstallationLocation location, } // init the transaction - error = _transaction.SetTo(location, fChangeCount, directoryName); + error = _transaction.SetTo(location, fState->ChangeCount(), directoryName); if (error != B_OK) { BEntry entry; _transactionDirectory.GetEntry(&entry); @@ -1979,7 +2146,7 @@ Volume::_PackagesEntryCreated(const char* name) { INFORM("Volume::_PackagesEntryCreated(\"%s\")\n", name); // Ignore the event, if the package is already known. - Package* package = fPackagesByFileName.Lookup(name); + Package* package = fState->FindPackage(name); if (package != NULL) { if (package->EntryCreatedIgnoreLevel() > 0) { package->DecrementEntryCreatedIgnoreLevel(); @@ -2029,7 +2196,7 @@ void Volume::_PackagesEntryRemoved(const char* name) { INFORM("Volume::_PackagesEntryRemoved(\"%s\")\n", name); - Package* package = fPackagesByFileName.Lookup(name); + Package* package = fState->FindPackage(name); if (package == NULL) return; @@ -2081,16 +2248,13 @@ Volume::_FillInActivationChangeItem(PackageFSActivationChangeItem* item, void Volume::_AddPackage(Package* package) { - fPackagesByFileName.Insert(package); - fPackagesByNodeRef.Insert(package); + fState->AddPackage(package); } void Volume::_RemovePackage(Package* package) { - fPackagesByFileName.Remove(package); - fPackagesByNodeRef.Remove(package); - fChangeCount++; + fState->RemovePackage(package); } @@ -2157,7 +2321,7 @@ Volume::_GetActivePackages(int fd) // mark the returned packages active for (uint32 i = 0; i < request->packageCount; i++) { - Package* package = fPackagesByNodeRef.Lookup( + Package* package = fState->FindPackage( node_ref(request->infos[i].packageDeviceID, request->infos[i].packageNodeID)); if (package == NULL) { @@ -2169,18 +2333,17 @@ Volume::_GetActivePackages(int fd) continue; } - package->SetActive(true); + fState->SetPackageActive(package, true); INFORM("active package: \"%s\"\n", package->FileName().String()); } -for (PackageNodeRefHashTable::Iterator it = fPackagesByNodeRef.GetIterator(); +for (PackageNodeRefHashTable::Iterator it = fState->ByNodeRefIterator(); it.HasNext();) { Package* package = it.Next(); if (!package->IsActive()) INFORM("inactive package: \"%s\"\n", package->FileName().String()); } - PackageNodeRefHashTable fPackagesByNodeRef; // INFORM("%" B_PRIu32 " active packages:\n", request->packageCount); // for (uint32 i = 0; i < request->packageCount; i++) { // INFORM(" dev: %" B_PRIdDEV ", node: %" B_PRIdINO "\n", @@ -2280,8 +2443,8 @@ Volume::_CreateActivationFileContent(const PackageSet& toActivate, const PackageSet& toDeactivate, BString& _content) { BString activationFileContent; - for (PackageFileNameHashTable::Iterator it - = fPackagesByFileName.GetIterator(); it.HasNext();) { + for (PackageFileNameHashTable::Iterator it = fState->ByFileNameIterator(); + it.HasNext();) { Package* package = it.Next(); if (package->IsActive() && toDeactivate.find(package) == toDeactivate.end()) { @@ -2446,16 +2609,5 @@ packagesToActivate.size(), packagesToDeactivate.size()); // Update our state, i.e. remove deactivated packages and mark activated // packages accordingly. - for (PackageSet::iterator it = packagesToActivate.begin(); - it != packagesToActivate.end(); ++it) { - (*it)->SetActive(true); - fChangeCount++; - } - - for (PackageSet::iterator it = packagesToDeactivate.begin(); - it != packagesToDeactivate.end(); ++it) { - Package* package = *it; - _RemovePackage(package); - delete package; - } + fState->ActivationChanged(packagesToActivate, packagesToDeactivate); } diff --git a/src/servers/package/Volume.h b/src/servers/package/Volume.h index 9ad756bcdf..356bfb81da 100644 --- a/src/servers/package/Volume.h +++ b/src/servers/package/Volume.h @@ -1,5 +1,5 @@ /* - * Copyright 2013, Haiku, Inc. All Rights Reserved. + * Copyright 2013-2014, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -23,6 +23,21 @@ #include "Package.h" +// Locking Policy +// ============== +// +// A Volume object is accessed by two threads: +// 1. The application thread: initially (c'tor and Init()) and when handling a +// location info request (HandleGetLocationInfoRequest()). +// 2. The corresponding Root object's job thread (any other operation). +// +// The only thread synchronization needed is for the status information accessed +// by HandleGetLocationInfoRequest() and modified by the job thread. The data +// are encapsulated in a Volume::State object which contains a lock. The lock +// must be held by the app thread when accessing the data (it reads only) and +// by the job thread when modifying the data (not needed when reading). + + using BPackageKit::BPrivate::BActivationTransaction; using BPackageKit::BPrivate::BDaemonClient; @@ -61,6 +76,10 @@ public: void HandleCommitTransactionRequest( BMessage* message); + void PackageJobPending(); + void PackageJobFinished(); + bool IsPackageJobPending() const; + void Unmounted(); virtual void MessageReceived(BMessage* message); @@ -90,10 +109,8 @@ public: void SetRoot(Root* root) { fRoot = root; } - const PackageFileNameHashTable& PackagesByFileName() const - { return fPackagesByFileName; } - const PackageNodeRefHashTable& PackagesByNodeRef() const - { return fPackagesByNodeRef; } + PackageFileNameHashTable::Iterator PackagesByFileNameIterator() + const; int OpenRootDirectory() const; @@ -120,6 +137,7 @@ public: private: struct NodeMonitorEvent; + struct State; struct CommitTransactionHandler; friend struct CommitTransactionHandler; @@ -196,15 +214,14 @@ private: node_ref fPackagesDirectoryRef; Root* fRoot; Listener* fListener; - PackageFileNameHashTable fPackagesByFileName; - PackageNodeRefHashTable fPackagesByNodeRef; + State* fState; BLocker fPendingNodeMonitorEventsLock; NodeMonitorEventList fPendingNodeMonitorEvents; bigtime_t fNodeMonitorEventHandleTime; PackageSet fPackagesToBeActivated; PackageSet fPackagesToBeDeactivated; - int64 fChangeCount; BMessage fLocationInfoReply; + // only accessed in the application thread };