Chromium Code Reviews| Index: extensions/browser/api/api_resource_manager.h |
| diff --git a/extensions/browser/api/api_resource_manager.h b/extensions/browser/api/api_resource_manager.h |
| index 7cff1732c1b068112c2f3bb78331a14745134f62..cd99031d6648aec0ef5f53355313dc7bb1f7af33 100644 |
| --- a/extensions/browser/api/api_resource_manager.h |
| +++ b/extensions/browser/api/api_resource_manager.h |
| @@ -36,8 +36,45 @@ class TCPSocketEventDispatcher; |
| class UDPSocketEventDispatcher; |
| } |
| +template <typename T> |
| +struct NamedThreadTraits { |
| + static bool IsCalledOnValidThread() { |
| + return content::BrowserThread::CurrentlyOn(T::kThreadId); |
| + } |
| + |
| + static bool IsMessageLoopValid() { |
| + return content::BrowserThread::IsMessageLoopValid(T::kThreadId); |
| + } |
| + |
| + static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() { |
| + return content::BrowserThread::GetMessageLoopProxyForThread(T::kThreadId); |
| + } |
| +}; |
| + |
| +template <typename T> |
| +struct TestThreadTraits { |
| + static bool IsCalledOnValidThread() { |
| + return content::BrowserThread::CurrentlyOn(thread_id_); |
| + } |
| + |
| + static bool IsMessageLoopValid() { |
| + return content::BrowserThread::IsMessageLoopValid(thread_id_); |
| + } |
| + |
| + static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() { |
| + return content::BrowserThread::GetMessageLoopProxyForThread(thread_id_); |
| + } |
| + |
| + static content::BrowserThread::ID thread_id_; |
| +}; |
| + |
| +template <typename T> |
| +content::BrowserThread::ID TestThreadTraits<T>::thread_id_ = |
| + content::BrowserThread::IO; |
| + |
| // An ApiResourceManager manages the lifetime of a set of resources that |
| -// ApiFunctions use. Examples are sockets or USB connections. |
| +// that live on named threads (i.e. BrowserThread::IO) which ApiFunctions use. |
| +// Examples of such resources are sockets or USB connections. |
| // |
| // Users of this class should define kThreadId to be the thread that |
| // ApiResourceManager to works on. The default is defined in ApiResource. |
| @@ -72,13 +109,13 @@ class UDPSocketEventDispatcher; |
| // ApiResourceManager<Resource>::GetFactoryInstance() { |
| // return g_factory.Pointer(); |
| // } |
| -template <class T> |
| +template <class T, typename ThreadingTraits = NamedThreadTraits<T> > |
| class ApiResourceManager : public BrowserContextKeyedAPI, |
| public base::NonThreadSafe, |
| public content::NotificationObserver { |
| public: |
| explicit ApiResourceManager(content::BrowserContext* context) |
| - : thread_id_(T::kThreadId), data_(new ApiResourceData(thread_id_)) { |
| + : data_(new ApiResourceData()) { |
| registrar_.Add(this, |
| chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, |
| content::NotificationService::AllSources()); |
| @@ -86,20 +123,19 @@ class ApiResourceManager : public BrowserContextKeyedAPI, |
| chrome::NOTIFICATION_EXTENSION_HOST_DESTROYED, |
| content::NotificationService::AllSources()); |
| } |
| - |
| // For Testing. |
| - static ApiResourceManager<T>* CreateApiResourceManagerForTest( |
| - content::BrowserContext* context, |
| - content::BrowserThread::ID thread_id) { |
| - ApiResourceManager* manager = new ApiResourceManager<T>(context); |
| - manager->thread_id_ = thread_id; |
| - manager->data_ = new ApiResourceData(thread_id); |
| + static ApiResourceManager<T, TestThreadTraits<T> >* |
| + CreateApiResourceManagerForTest(content::BrowserContext* context, |
| + content::BrowserThread::ID thread_id) { |
| + TestThreadTraits<T>::thread_id_ = thread_id; |
| + ApiResourceManager<T, TestThreadTraits<T> >* manager = |
| + new ApiResourceManager<T, TestThreadTraits<T> >(context); |
| return manager; |
| } |
| virtual ~ApiResourceManager() { |
| DCHECK(CalledOnValidThread()); |
| - DCHECK(content::BrowserThread::IsMessageLoopValid(thread_id_)) |
| + DCHECK(ThreadingTraits::IsMessageLoopValid()) |
| << "A unit test is using an ApiResourceManager but didn't provide " |
| "the thread message loop needed for that kind of resource. " |
| "Please ensure that the appropriate message loop is operational."; |
| @@ -107,15 +143,6 @@ class ApiResourceManager : public BrowserContextKeyedAPI, |
| data_->InititateCleanup(); |
| } |
| - // BrowserContextKeyedAPI implementation. |
| - static BrowserContextKeyedAPIFactory<ApiResourceManager<T> >* |
| - GetFactoryInstance(); |
| - |
| - // Convenience method to get the ApiResourceManager for a profile. |
| - static ApiResourceManager<T>* Get(content::BrowserContext* context) { |
| - return BrowserContextKeyedAPIFactory<ApiResourceManager<T> >::Get(context); |
| - } |
| - |
| // Takes ownership. |
| int Add(T* api_resource) { return data_->Add(api_resource); } |
| @@ -131,6 +158,18 @@ class ApiResourceManager : public BrowserContextKeyedAPI, |
| return data_->GetResourceIds(extension_id); |
| } |
| + // BrowserContextKeyedAPI implementation. |
| + static BrowserContextKeyedAPIFactory<ApiResourceManager<T> >* |
| + GetFactoryInstance(); |
| + |
| + // Convenience method to get the ApiResourceManager for a profile. |
| + static ApiResourceManager<T>* Get(content::BrowserContext* context) { |
| + return BrowserContextKeyedAPIFactory<ApiResourceManager<T> >::Get(context); |
| + } |
| + |
| + // BrowserContextKeyedAPI implementation. |
| + static const char* service_name() { return T::service_name(); } |
| + |
| protected: |
| // content::NotificationObserver: |
| virtual void Observe(int type, |
| @@ -163,9 +202,6 @@ class ApiResourceManager : public BrowserContextKeyedAPI, |
| friend class core_api::UDPSocketEventDispatcher; |
| friend class BrowserContextKeyedAPIFactory<ApiResourceManager<T> >; |
| - // BrowserContextKeyedAPI implementation. |
| - static const char* service_name() { return T::service_name(); } |
| - |
| static const bool kServiceHasOwnInstanceInIncognito = true; |
| static const bool kServiceIsNULLWhileTesting = true; |
| @@ -177,11 +213,10 @@ class ApiResourceManager : public BrowserContextKeyedAPI, |
| // Lookup map from extension id's to allocated resource id's. |
| typedef std::map<std::string, base::hash_set<int> > ExtensionToResourceMap; |
| - explicit ApiResourceData(const content::BrowserThread::ID thread_id) |
| - : next_id_(1), thread_id_(thread_id) {} |
| + ApiResourceData() : next_id_(1) {} |
| int Add(T* api_resource) { |
| - DCHECK_CURRENTLY_ON(thread_id_); |
| + DCHECK(ThreadingTraits::IsCalledOnValidThread()); |
| int id = GenerateId(); |
| if (id > 0) { |
| linked_ptr<T> resource_ptr(api_resource); |
| @@ -200,7 +235,7 @@ class ApiResourceManager : public BrowserContextKeyedAPI, |
| } |
| void Remove(const std::string& extension_id, int api_resource_id) { |
| - DCHECK_CURRENTLY_ON(thread_id_); |
| + DCHECK(ThreadingTraits::IsCalledOnValidThread()); |
| if (GetOwnedResource(extension_id, api_resource_id) != NULL) { |
| DCHECK(extension_resource_map_.find(extension_id) != |
| extension_resource_map_.end()); |
| @@ -210,21 +245,20 @@ class ApiResourceManager : public BrowserContextKeyedAPI, |
| } |
| T* Get(const std::string& extension_id, int api_resource_id) { |
| - DCHECK_CURRENTLY_ON(thread_id_); |
| + DCHECK(ThreadingTraits::IsCalledOnValidThread()); |
| return GetOwnedResource(extension_id, api_resource_id); |
| } |
| base::hash_set<int>* GetResourceIds(const std::string& extension_id) { |
| - DCHECK_CURRENTLY_ON(thread_id_); |
| + DCHECK(ThreadingTraits::IsCalledOnValidThread()); |
| return GetOwnedResourceIds(extension_id); |
| } |
| void InitiateExtensionUnloadedCleanup(const std::string& extension_id) { |
| - if (content::BrowserThread::CurrentlyOn(thread_id_)) { |
| + if (ThreadingTraits::IsCalledOnValidThread()) { |
| CleanupResourcesFromUnloadedExtension(extension_id); |
| } else { |
| - content::BrowserThread::PostTask( |
| - thread_id_, |
| + ThreadingTraits::GetSequencedTaskRunner()->PostTask( |
| FROM_HERE, |
| base::Bind(&ApiResourceData::CleanupResourcesFromUnloadedExtension, |
| this, |
| @@ -233,11 +267,10 @@ class ApiResourceManager : public BrowserContextKeyedAPI, |
| } |
| void InitiateExtensionSuspendedCleanup(const std::string& extension_id) { |
| - if (content::BrowserThread::CurrentlyOn(thread_id_)) { |
| + if (ThreadingTraits::IsCalledOnValidThread()) { |
| CleanupResourcesFromSuspendedExtension(extension_id); |
| } else { |
| - content::BrowserThread::PostTask( |
| - thread_id_, |
| + ThreadingTraits::GetSequencedTaskRunner()->PostTask( |
| FROM_HERE, |
| base::Bind(&ApiResourceData::CleanupResourcesFromSuspendedExtension, |
| this, |
| @@ -246,11 +279,11 @@ class ApiResourceManager : public BrowserContextKeyedAPI, |
| } |
| void InititateCleanup() { |
| - if (content::BrowserThread::CurrentlyOn(thread_id_)) { |
| + if (ThreadingTraits::IsCalledOnValidThread()) { |
| Cleanup(); |
| } else { |
| - content::BrowserThread::PostTask( |
| - thread_id_, FROM_HERE, base::Bind(&ApiResourceData::Cleanup, this)); |
| + ThreadingTraits::GetSequencedTaskRunner()->PostTask( |
| + FROM_HERE, base::Bind(&ApiResourceData::Cleanup, this)); |
| } |
| } |
| @@ -268,7 +301,7 @@ class ApiResourceManager : public BrowserContextKeyedAPI, |
| } |
| base::hash_set<int>* GetOwnedResourceIds(const std::string& extension_id) { |
| - DCHECK_CURRENTLY_ON(thread_id_); |
| + DCHECK(ThreadingTraits::IsCalledOnValidThread()); |
| if (extension_resource_map_.find(extension_id) == |
| extension_resource_map_.end()) |
| return NULL; |
| @@ -288,7 +321,7 @@ class ApiResourceManager : public BrowserContextKeyedAPI, |
| void CleanupResourcesFromExtension(const std::string& extension_id, |
| bool remove_all) { |
| - DCHECK_CURRENTLY_ON(thread_id_); |
| + DCHECK(ThreadingTraits::IsCalledOnValidThread()); |
| if (extension_resource_map_.find(extension_id) == |
| extension_resource_map_.end()) { |
| @@ -324,7 +357,7 @@ class ApiResourceManager : public BrowserContextKeyedAPI, |
| } |
| void Cleanup() { |
| - DCHECK_CURRENTLY_ON(thread_id_); |
| + DCHECK(ThreadingTraits::IsCalledOnValidThread()); |
| api_resource_map_.clear(); |
| extension_resource_map_.clear(); |
| @@ -333,16 +366,80 @@ class ApiResourceManager : public BrowserContextKeyedAPI, |
| int GenerateId() { return next_id_++; } |
| int next_id_; |
| - const content::BrowserThread::ID thread_id_; |
| ApiResourceMap api_resource_map_; |
| ExtensionToResourceMap extension_resource_map_; |
| }; |
| - content::BrowserThread::ID thread_id_; |
| content::NotificationRegistrar registrar_; |
| scoped_refptr<ApiResourceData> data_; |
| }; |
| +// With WorkerPoolThreadTraits, ApiResourceManager can be used to manage the |
| +// lifetime of a set of resources that live on sequenced task runner threads |
| +// which ApiFunctions use. Examples of such resources are temporary file |
| +// resources produced by certain API calls. |
| +// |
| +// Instead of kThreadId. classes used for tracking identifying such resources |
|
Ken Rockot(use gerrit already)
2014/07/22 15:33:59
nit: -identifying? (or +and)
zel
2014/07/22 22:45:03
Done.
|
| +// should define kSequenceToken and kShutdownBehavior to identify sequence task |
| +// runner for ApiResourceManager to work on and how pending tasks should behave |
| +// on shutdown. |
| +// The user must also define a static const char* service_name() that returns |
| +// the name of the service, and in order for ApiWorkerPoolResourceManager to use |
| +// service_name() friend this class. |
| +// |
| +// In the cc file the user must define a GetFactoryInstance() and manage their |
| +// own instances (typically using LazyInstance or Singleton). |
| +// |
| +// E.g.: |
| +// |
| +// class PoolResource { |
| +// public: |
| +// static const char kSequenceToken[] = "temp_files"; |
| +// static const base::SequencedWorkerPool::WorkerShutdown kShutdownBehavior = |
| +// base::SequencedWorkerPool::BLOCK_SHUTDOWN; |
| +// private: |
| +// friend class ApiResourceManager<WorkerPoolResource, |
| +// WorkerPoolThreadTraits>; |
| +// static const char* service_name() { |
| +// return "TempFilesResourceManager"; |
| +// } |
| +// }; |
| +// |
| +// In the cc file: |
| +// |
| +// static base::LazyInstance<BrowserContextKeyedAPIFactory< |
| +// ApiResourceManager<Resource, WorkerPoolThreadTraits> > > |
| +// g_factory = LAZY_INSTANCE_INITIALIZER; |
| +// |
| +// |
| +// template <> |
| +// BrowserContextKeyedAPIFactory<ApiResourceManager<WorkerPoolResource> >* |
| +// ApiResourceManager<WorkerPoolPoolResource, |
| +// WorkerPoolThreadTraits>::GetFactoryInstance() { |
| +// return g_factory.Pointer(); |
| +// } |
| +template <typename T> |
| +struct WorkerPoolThreadTraits { |
| + static bool IsCalledOnValidThread() { |
| + return content::BrowserThread::GetBlockingPool() |
| + ->IsRunningSequenceOnCurrentThread( |
| + content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken( |
| + T::kSequenceToken)); |
| + } |
| + |
| + static bool IsMessageLoopValid() { |
| + return content::BrowserThread::GetBlockingPool() != NULL; |
| + } |
| + |
| + static scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() { |
| + return content::BrowserThread::GetBlockingPool() |
| + ->GetSequencedTaskRunnerWithShutdownBehavior( |
| + content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken( |
| + T::kSequenceToken), |
| + T::kShutdownBehavior); |
| + } |
| +}; |
| + |
| } // namespace extensions |
| #endif // EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_ |