1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_
6 #define EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_
10 #include "base/containers/hash_tables.h"
11 #include "base/memory/linked_ptr.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/scoped_observer.h"
15 #include "base/threading/non_thread_safe.h"
16 #include "components/keyed_service/core/keyed_service.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "extensions/browser/browser_context_keyed_api_factory.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/browser/extension_registry_observer.h"
21 #include "extensions/browser/process_manager.h"
22 #include "extensions/browser/process_manager_observer.h"
23 #include "extensions/common/extension.h"
25 namespace extensions
{
26 class CastChannelAsyncApiFunction
;
29 class BluetoothSocketApiFunction
;
30 class BluetoothSocketEventDispatcher
;
31 class SerialEventDispatcher
;
32 class TCPServerSocketEventDispatcher
;
33 class TCPSocketEventDispatcher
;
34 class UDPSocketEventDispatcher
;
38 struct NamedThreadTraits
{
39 static bool IsCalledOnValidThread() {
40 return content::BrowserThread::CurrentlyOn(T::kThreadId
);
43 static bool IsMessageLoopValid() {
44 return content::BrowserThread::IsMessageLoopValid(T::kThreadId
);
47 static scoped_refptr
<base::SequencedTaskRunner
> GetSequencedTaskRunner() {
48 return content::BrowserThread::GetMessageLoopProxyForThread(T::kThreadId
);
52 // An ApiResourceManager manages the lifetime of a set of resources that
53 // that live on named threads (i.e. BrowserThread::IO) which ApiFunctions use.
54 // Examples of such resources are sockets or USB connections.
56 // Users of this class should define kThreadId to be the thread that
57 // ApiResourceManager to works on. The default is defined in ApiResource.
58 // The user must also define a static const char* service_name() that returns
59 // the name of the service, and in order for ApiResourceManager to use
60 // service_name() friend this class.
62 // In the cc file the user must define a GetFactoryInstance() and manage their
63 // own instances (typically using LazyInstance or Singleton).
69 // static const BrowserThread::ID kThreadId = BrowserThread::FILE;
71 // friend class ApiResourceManager<Resource>;
72 // static const char* service_name() {
73 // return "ResourceManager";
79 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
80 // ApiResourceManager<Resource> > >
81 // g_factory = LAZY_INSTANCE_INITIALIZER;
85 // BrowserContextKeyedAPIFactory<ApiResourceManager<Resource> >*
86 // ApiResourceManager<Resource>::GetFactoryInstance() {
87 // return g_factory.Pointer();
89 template <class T
, typename ThreadingTraits
= NamedThreadTraits
<T
>>
90 class ApiResourceManager
: public BrowserContextKeyedAPI
,
91 public base::NonThreadSafe
,
92 public ExtensionRegistryObserver
,
93 public ProcessManagerObserver
{
95 explicit ApiResourceManager(content::BrowserContext
* context
)
96 : data_(new ApiResourceData()),
97 extension_registry_observer_(this),
98 process_manager_observer_(this) {
99 extension_registry_observer_
.Add(ExtensionRegistry::Get(context
));
100 process_manager_observer_
.Add(ProcessManager::Get(context
));
103 virtual ~ApiResourceManager() {
104 DCHECK(CalledOnValidThread());
105 DCHECK(ThreadingTraits::IsMessageLoopValid())
106 << "A unit test is using an ApiResourceManager but didn't provide "
107 "the thread message loop needed for that kind of resource. "
108 "Please ensure that the appropriate message loop is operational.";
110 data_
->InititateCleanup();
114 int Add(T
* api_resource
) { return data_
->Add(api_resource
); }
116 void Remove(const std::string
& extension_id
, int api_resource_id
) {
117 data_
->Remove(extension_id
, api_resource_id
);
120 T
* Get(const std::string
& extension_id
, int api_resource_id
) {
121 return data_
->Get(extension_id
, api_resource_id
);
124 base::hash_set
<int>* GetResourceIds(const std::string
& extension_id
) {
125 return data_
->GetResourceIds(extension_id
);
128 // BrowserContextKeyedAPI implementation.
129 static BrowserContextKeyedAPIFactory
<ApiResourceManager
<T
> >*
130 GetFactoryInstance();
132 // Convenience method to get the ApiResourceManager for a profile.
133 static ApiResourceManager
<T
>* Get(content::BrowserContext
* context
) {
134 return BrowserContextKeyedAPIFactory
<ApiResourceManager
<T
> >::Get(context
);
137 // BrowserContextKeyedAPI implementation.
138 static const char* service_name() { return T::service_name(); }
140 // Change the resource mapped to this |extension_id| at this
141 // |api_resource_id| to |resource|. Returns true and succeeds unless
142 // |api_resource_id| does not already identify a resource held by
144 bool Replace(const std::string
& extension_id
,
147 return data_
->Replace(extension_id
, api_resource_id
, resource
);
151 // ProcessManagerObserver:
152 void OnBackgroundHostClose(const std::string
& extension_id
) override
{
153 data_
->InitiateExtensionSuspendedCleanup(extension_id
);
156 // ExtensionRegistryObserver:
157 void OnExtensionUnloaded(content::BrowserContext
* browser_context
,
158 const Extension
* extension
,
159 UnloadedExtensionInfo::Reason reason
) override
{
160 data_
->InitiateExtensionUnloadedCleanup(extension
->id());
164 // TODO(rockot): ApiResourceData could be moved out of ApiResourceManager and
165 // we could avoid maintaining a friends list here.
166 friend class BluetoothAPI
;
167 friend class CastChannelAsyncApiFunction
;
168 friend class api::BluetoothSocketApiFunction
;
169 friend class api::BluetoothSocketEventDispatcher
;
170 friend class api::SerialEventDispatcher
;
171 friend class api::TCPServerSocketEventDispatcher
;
172 friend class api::TCPSocketEventDispatcher
;
173 friend class api::UDPSocketEventDispatcher
;
174 friend class BrowserContextKeyedAPIFactory
<ApiResourceManager
<T
> >;
176 static const bool kServiceHasOwnInstanceInIncognito
= true;
177 static const bool kServiceIsNULLWhileTesting
= true;
179 // ApiResourceData class handles resource bookkeeping on a thread
180 // where resource lifetime is handled.
181 class ApiResourceData
: public base::RefCountedThreadSafe
<ApiResourceData
> {
183 typedef std::map
<int, linked_ptr
<T
> > ApiResourceMap
;
184 // Lookup map from extension id's to allocated resource id's.
185 typedef std::map
<std::string
, base::hash_set
<int> > ExtensionToResourceMap
;
187 ApiResourceData() : next_id_(1) {}
189 int Add(T
* api_resource
) {
190 DCHECK(ThreadingTraits::IsCalledOnValidThread());
191 int id
= GenerateId();
193 linked_ptr
<T
> resource_ptr(api_resource
);
194 api_resource_map_
[id
] = resource_ptr
;
196 const std::string
& extension_id
= api_resource
->owner_extension_id();
197 ExtensionToResourceMap::iterator it
=
198 extension_resource_map_
.find(extension_id
);
199 if (it
== extension_resource_map_
.end()) {
200 it
= extension_resource_map_
.insert(
201 std::make_pair(extension_id
, base::hash_set
<int>())).first
;
203 it
->second
.insert(id
);
209 void Remove(const std::string
& extension_id
, int api_resource_id
) {
210 DCHECK(ThreadingTraits::IsCalledOnValidThread());
211 if (GetOwnedResource(extension_id
, api_resource_id
)) {
212 ExtensionToResourceMap::iterator it
=
213 extension_resource_map_
.find(extension_id
);
214 it
->second
.erase(api_resource_id
);
215 api_resource_map_
.erase(api_resource_id
);
219 T
* Get(const std::string
& extension_id
, int api_resource_id
) {
220 DCHECK(ThreadingTraits::IsCalledOnValidThread());
221 return GetOwnedResource(extension_id
, api_resource_id
);
224 // Change the resource mapped to this |extension_id| at this
225 // |api_resource_id| to |resource|. Returns true and succeeds unless
226 // |api_resource_id| does not already identify a resource held by
228 bool Replace(const std::string
& extension_id
,
231 DCHECK(ThreadingTraits::IsCalledOnValidThread());
232 T
* old_resource
= api_resource_map_
[api_resource_id
].get();
233 if (old_resource
&& extension_id
== old_resource
->owner_extension_id()) {
234 api_resource_map_
[api_resource_id
] = linked_ptr
<T
>(api_resource
);
240 base::hash_set
<int>* GetResourceIds(const std::string
& extension_id
) {
241 DCHECK(ThreadingTraits::IsCalledOnValidThread());
242 return GetOwnedResourceIds(extension_id
);
245 void InitiateExtensionUnloadedCleanup(const std::string
& extension_id
) {
246 if (ThreadingTraits::IsCalledOnValidThread()) {
247 CleanupResourcesFromUnloadedExtension(extension_id
);
249 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
251 base::Bind(&ApiResourceData::CleanupResourcesFromUnloadedExtension
,
257 void InitiateExtensionSuspendedCleanup(const std::string
& extension_id
) {
258 if (ThreadingTraits::IsCalledOnValidThread()) {
259 CleanupResourcesFromSuspendedExtension(extension_id
);
261 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
263 base::Bind(&ApiResourceData::CleanupResourcesFromSuspendedExtension
,
269 void InititateCleanup() {
270 if (ThreadingTraits::IsCalledOnValidThread()) {
273 ThreadingTraits::GetSequencedTaskRunner()->PostTask(
274 FROM_HERE
, base::Bind(&ApiResourceData::Cleanup
, this));
279 friend class base::RefCountedThreadSafe
<ApiResourceData
>;
281 virtual ~ApiResourceData() {}
283 T
* GetOwnedResource(const std::string
& extension_id
, int api_resource_id
) {
284 linked_ptr
<T
> ptr
= api_resource_map_
[api_resource_id
];
285 T
* resource
= ptr
.get();
286 if (resource
&& extension_id
== resource
->owner_extension_id())
291 base::hash_set
<int>* GetOwnedResourceIds(const std::string
& extension_id
) {
292 DCHECK(ThreadingTraits::IsCalledOnValidThread());
293 ExtensionToResourceMap::iterator it
=
294 extension_resource_map_
.find(extension_id
);
295 if (it
== extension_resource_map_
.end())
297 return &(it
->second
);
300 void CleanupResourcesFromUnloadedExtension(
301 const std::string
& extension_id
) {
302 CleanupResourcesFromExtension(extension_id
, true);
305 void CleanupResourcesFromSuspendedExtension(
306 const std::string
& extension_id
) {
307 CleanupResourcesFromExtension(extension_id
, false);
310 void CleanupResourcesFromExtension(const std::string
& extension_id
,
312 DCHECK(ThreadingTraits::IsCalledOnValidThread());
314 ExtensionToResourceMap::iterator it
=
315 extension_resource_map_
.find(extension_id
);
316 if (it
== extension_resource_map_
.end())
319 // Remove all resources, or the non persistent ones only if |remove_all|
321 base::hash_set
<int>& resource_ids
= it
->second
;
322 for (base::hash_set
<int>::iterator it
= resource_ids
.begin();
323 it
!= resource_ids
.end();) {
328 linked_ptr
<T
> ptr
= api_resource_map_
[*it
];
329 T
* resource
= ptr
.get();
330 erase
= (resource
&& !resource
->IsPersistent());
334 api_resource_map_
.erase(*it
);
335 resource_ids
.erase(it
++);
341 // Remove extension entry if we removed all its resources.
342 if (resource_ids
.size() == 0) {
343 extension_resource_map_
.erase(extension_id
);
348 DCHECK(ThreadingTraits::IsCalledOnValidThread());
350 api_resource_map_
.clear();
351 extension_resource_map_
.clear();
354 int GenerateId() { return next_id_
++; }
357 ApiResourceMap api_resource_map_
;
358 ExtensionToResourceMap extension_resource_map_
;
361 content::NotificationRegistrar registrar_
;
362 scoped_refptr
<ApiResourceData
> data_
;
364 ScopedObserver
<ExtensionRegistry
, ExtensionRegistryObserver
>
365 extension_registry_observer_
;
366 ScopedObserver
<ProcessManager
, ProcessManagerObserver
>
367 process_manager_observer_
;
370 // With WorkerPoolThreadTraits, ApiResourceManager can be used to manage the
371 // lifetime of a set of resources that live on sequenced task runner threads
372 // which ApiFunctions use. Examples of such resources are temporary file
373 // resources produced by certain API calls.
375 // Instead of kThreadId. classes used for tracking such resources should define
376 // kSequenceToken and kShutdownBehavior to identify sequence task runner for
377 // ApiResourceManager to work on and how pending tasks should behave on
379 // The user must also define a static const char* service_name() that returns
380 // the name of the service, and in order for ApiWorkerPoolResourceManager to use
381 // service_name() friend this class.
383 // In the cc file the user must define a GetFactoryInstance() and manage their
384 // own instances (typically using LazyInstance or Singleton).
388 // class PoolResource {
390 // static const char kSequenceToken[] = "temp_files";
391 // static const base::SequencedWorkerPool::WorkerShutdown kShutdownBehavior =
392 // base::SequencedWorkerPool::BLOCK_SHUTDOWN;
394 // friend class ApiResourceManager<WorkerPoolResource,
395 // WorkerPoolThreadTraits>;
396 // static const char* service_name() {
397 // return "TempFilesResourceManager";
403 // static base::LazyInstance<BrowserContextKeyedAPIFactory<
404 // ApiResourceManager<Resource, WorkerPoolThreadTraits> > >
405 // g_factory = LAZY_INSTANCE_INITIALIZER;
409 // BrowserContextKeyedAPIFactory<ApiResourceManager<WorkerPoolResource> >*
410 // ApiResourceManager<WorkerPoolPoolResource,
411 // WorkerPoolThreadTraits>::GetFactoryInstance() {
412 // return g_factory.Pointer();
414 template <typename T
>
415 struct WorkerPoolThreadTraits
{
416 static bool IsCalledOnValidThread() {
417 return content::BrowserThread::GetBlockingPool()
418 ->IsRunningSequenceOnCurrentThread(
419 content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
423 static bool IsMessageLoopValid() {
424 return content::BrowserThread::GetBlockingPool() != NULL
;
427 static scoped_refptr
<base::SequencedTaskRunner
> GetSequencedTaskRunner() {
428 return content::BrowserThread::GetBlockingPool()
429 ->GetSequencedTaskRunnerWithShutdownBehavior(
430 content::BrowserThread::GetBlockingPool()->GetNamedSequenceToken(
432 T::kShutdownBehavior
);
436 } // namespace extensions
438 #endif // EXTENSIONS_BROWSER_API_API_RESOURCE_MANAGER_H_