diff options
author | scottmg@chromium.org <scottmg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-02 04:41:24 +0000 |
---|---|---|
committer | scottmg@chromium.org <scottmg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-02 04:41:24 +0000 |
commit | 1ced72566c94c3e7f18b16236ef4ccaa0ec6e04b (patch) | |
tree | 47b7d60ed3580341e6a682712d0211f251ce27bf /content/browser/gamepad | |
parent | 02fea642579c5e875ae65dd1a51a08e000237931 (diff) | |
download | chromium_src-1ced72566c94c3e7f18b16236ef4ccaa0ec6e04b.zip chromium_src-1ced72566c94c3e7f18b16236ef4ccaa0ec6e04b.tar.gz chromium_src-1ced72566c94c3e7f18b16236ef4ccaa0ec6e04b.tar.bz2 |
Add GamepadService, owns 1 gamepad bg thread
BUG=79050
Review URL: http://codereview.chromium.org/8760023
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@112638 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser/gamepad')
-rw-r--r-- | content/browser/gamepad/gamepad_provider.cc | 67 | ||||
-rw-r--r-- | content/browser/gamepad/gamepad_provider.h | 24 | ||||
-rw-r--r-- | content/browser/gamepad/gamepad_provider_unittest.cc | 12 | ||||
-rw-r--r-- | content/browser/gamepad/gamepad_service.cc | 76 | ||||
-rw-r--r-- | content/browser/gamepad/gamepad_service.h | 70 |
5 files changed, 206 insertions, 43 deletions
diff --git a/content/browser/gamepad/gamepad_provider.cc b/content/browser/gamepad/gamepad_provider.cc index c8eed65..c16245b 100644 --- a/content/browser/gamepad/gamepad_provider.cc +++ b/content/browser/gamepad/gamepad_provider.cc @@ -23,8 +23,6 @@ namespace content { -GamepadProvider* GamepadProvider::instance_ = NULL; - // Define the default data fetcher that GamepadProvider will use if none is // supplied. (GamepadPlatformDataFetcher). #if defined(OS_WIN) @@ -44,9 +42,9 @@ typedef GamepadEmptyDataFetcher GamepadPlatformDataFetcher; #endif GamepadProvider::GamepadProvider(GamepadDataFetcher* fetcher) - : creator_loop_(MessageLoop::current()->message_loop_proxy()), - provided_fetcher_(fetcher), + : is_paused_(false), devices_changed_(true), + provided_fetcher_(fetcher), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { size_t data_size = sizeof(GamepadHardwareBuffer); base::SystemMonitor* monitor = base::SystemMonitor::Get(); @@ -55,13 +53,23 @@ GamepadProvider::GamepadProvider(GamepadDataFetcher* fetcher) gamepad_shared_memory_.CreateAndMapAnonymous(data_size); GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); memset(hwbuf, 0, sizeof(GamepadHardwareBuffer)); + + polling_thread_.reset(new base::Thread("Gamepad polling thread")); + polling_thread_->Start(); + + MessageLoop* polling_loop = polling_thread_->message_loop(); + polling_loop->PostTask( + FROM_HERE, + base::Bind(&GamepadProvider::DoInitializePollingThread, this)); } GamepadProvider::~GamepadProvider() { base::SystemMonitor* monitor = base::SystemMonitor::Get(); if (monitor) monitor->RemoveDevicesChangedObserver(this); - Stop(); + + polling_thread_.reset(); + data_fetcher_.reset(); } base::SharedMemoryHandle GamepadProvider::GetRendererSharedMemoryHandle( @@ -71,34 +79,28 @@ base::SharedMemoryHandle GamepadProvider::GetRendererSharedMemoryHandle( return renderer_handle; } -void GamepadProvider::OnDevicesChanged() { - devices_changed_ = true; +void GamepadProvider::Pause() { + base::AutoLock lock(is_paused_lock_); + is_paused_ = true; } -void GamepadProvider::Start() { - DCHECK(MessageLoop::current()->message_loop_proxy() == creator_loop_); - - if (polling_thread_.get()) - return; - - polling_thread_.reset(new base::Thread("Gamepad polling thread")); - if (!polling_thread_->Start()) { - LOG(ERROR) << "Failed to start gamepad polling thread"; - polling_thread_.reset(); - return; +void GamepadProvider::Resume() { + { + base::AutoLock lock(is_paused_lock_); + if (!is_paused_) + return; + is_paused_ = false; } MessageLoop* polling_loop = polling_thread_->message_loop(); polling_loop->PostTask( FROM_HERE, - base::Bind(&GamepadProvider::DoInitializePollingThread, this)); + base::Bind(&GamepadProvider::ScheduleDoPoll, this)); } -void GamepadProvider::Stop() { - DCHECK(MessageLoop::current()->message_loop_proxy() == creator_loop_); - - polling_thread_.reset(); - data_fetcher_.reset(); +void GamepadProvider::OnDevicesChanged() { + base::AutoLock lock(devices_changed_lock_); + devices_changed_ = true; } void GamepadProvider::DoInitializePollingThread() { @@ -116,6 +118,7 @@ void GamepadProvider::DoInitializePollingThread() { void GamepadProvider::DoPoll() { DCHECK(MessageLoop::current() == polling_thread_->message_loop()); + bool changed; GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); ANNOTATE_BENIGN_RACE_SIZED( @@ -123,12 +126,18 @@ void GamepadProvider::DoPoll() { sizeof(WebKit::WebGamepads), "Racey reads are discarded"); + { + base::AutoLock lock(devices_changed_lock_); + changed = devices_changed_; + devices_changed_ = false; + } + // Acquire the SeqLock. There is only ever one writer to this data. // See gamepad_hardware_buffer.h. hwbuf->sequence.WriteBegin(); - data_fetcher_->GetGamepadData(&hwbuf->buffer, devices_changed_); + data_fetcher_->GetGamepadData(&hwbuf->buffer, changed); hwbuf->sequence.WriteEnd(); - devices_changed_ = false; + // Schedule our next interval of polling. ScheduleDoPoll(); } @@ -136,6 +145,12 @@ void GamepadProvider::DoPoll() { void GamepadProvider::ScheduleDoPoll() { DCHECK(MessageLoop::current() == polling_thread_->message_loop()); + { + base::AutoLock lock(is_paused_lock_); + if (is_paused_) + return; + } + MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&GamepadProvider::DoPoll, weak_factory_.GetWeakPtr()), diff --git a/content/browser/gamepad/gamepad_provider.h b/content/browser/gamepad/gamepad_provider.h index a40047d..963e768 100644 --- a/content/browser/gamepad/gamepad_provider.h +++ b/content/browser/gamepad/gamepad_provider.h @@ -9,6 +9,7 @@ #include "base/memory/weak_ptr.h" #include "base/message_loop_proxy.h" #include "base/shared_memory.h" +#include "base/synchronization/lock.h" #include "base/system_monitor/system_monitor.h" #include "base/task.h" #include "content/browser/gamepad/data_fetcher.h" @@ -29,12 +30,14 @@ class CONTENT_EXPORT GamepadProvider : public: explicit GamepadProvider(GamepadDataFetcher* fetcher); - // Starts or Stops the provider. Called from creator_loop_. - void Start(); - void Stop(); base::SharedMemoryHandle GetRendererSharedMemoryHandle( base::ProcessHandle renderer_process); + // Pause and resume the background polling thread. Can be called from any + // thread. + void Pause(); + void Resume(); + private: friend class base::RefCountedThreadSafe<GamepadProvider>; @@ -53,16 +56,27 @@ class CONTENT_EXPORT GamepadProvider : enum { kDesiredSamplingIntervalMs = 16 }; + // Keeps track of when the background thread is paused. Access to is_paused_ + // must be guarded by is_paused_lock_. + base::Lock is_paused_lock_; + bool is_paused_; + + // Updated based on notification from SystemMonitor when the system devices + // have been updated, and this notification is passed on to the data fetcher + // to enable it to avoid redundant (and possibly expensive) is-connected + // tests. Access to devices_changed_ must be guarded by + // devices_changed_lock_. + base::Lock devices_changed_lock_; + bool devices_changed_; + // The Message Loop on which this object was created. // Typically the I/O loop, but may be something else during testing. - scoped_refptr<base::MessageLoopProxy> creator_loop_; scoped_ptr<GamepadDataFetcher> provided_fetcher_; // When polling_thread_ is running, members below are only to be used // from that thread. scoped_ptr<GamepadDataFetcher> data_fetcher_; base::SharedMemory gamepad_shared_memory_; - bool devices_changed_; // Polling is done on this background thread. scoped_ptr<base::Thread> polling_thread_; diff --git a/content/browser/gamepad/gamepad_provider_unittest.cc b/content/browser/gamepad/gamepad_provider_unittest.cc index b016405..cdc6374 100644 --- a/content/browser/gamepad/gamepad_provider_unittest.cc +++ b/content/browser/gamepad/gamepad_provider_unittest.cc @@ -56,15 +56,6 @@ class GamepadProviderTest : public testing::Test { scoped_refptr<GamepadProvider> provider_; }; -TEST_F(GamepadProviderTest, BasicStartStop) { - WebGamepads test_data; - memset(&test_data, 0, sizeof(test_data)); - GamepadProvider* provider = CreateProvider(test_data); - provider->Start(); - provider->Stop(); - // Just ensure that there's no asserts on startup, shutdown, or destroy. -} - TEST_F(GamepadProviderTest, PollingAccess) { WebGamepads test_data; test_data.length = 1; @@ -77,7 +68,6 @@ TEST_F(GamepadProviderTest, PollingAccess) { test_data.items[0].axes[1] = .5f; GamepadProvider* provider = CreateProvider(test_data); - provider->Start(); main_message_loop_.RunAllPending(); @@ -107,8 +97,6 @@ TEST_F(GamepadProviderTest, PollingAccess) { EXPECT_EQ(2u, output.items[0].axesLength); EXPECT_EQ(-1.f, output.items[0].axes[0]); EXPECT_EQ(0.5f, output.items[0].axes[1]); - - provider->Stop(); } } // namespace diff --git a/content/browser/gamepad/gamepad_service.cc b/content/browser/gamepad/gamepad_service.cc new file mode 100644 index 0000000..a6e90d3 --- /dev/null +++ b/content/browser/gamepad/gamepad_service.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/gamepad/gamepad_service.h" + +#include "base/bind.h" +#include "base/memory/singleton.h" +#include "content/browser/gamepad/data_fetcher.h" +#include "content/browser/gamepad/gamepad_provider.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_source.h" +#include "content/public/browser/notification_types.h" +#include "content/public/browser/render_process_host.h" + +namespace content { + +GamepadService::GamepadService() : num_readers_(0) { +} + +GamepadService::~GamepadService() { +} + +GamepadService* GamepadService::GetInstance() { + return Singleton<GamepadService>::get(); +} + +void GamepadService::Start( + GamepadDataFetcher* data_fetcher, + content::RenderProcessHost* associated_rph) { + num_readers_++; + if (!provider_) + provider_ = new GamepadProvider(data_fetcher); + DCHECK(num_readers_ > 0); + provider_->Resume(); + + content::BrowserThread::PostTask( + content::BrowserThread::UI, + FROM_HERE, + base::Bind(&GamepadService::RegisterForCloseNotification, + base::Unretained(this), + associated_rph)); +} + +void GamepadService::RegisterForCloseNotification( + content::RenderProcessHost* rph) { + registrar_.Add(this, + content::NOTIFICATION_RENDERER_PROCESS_CLOSED, + content::Source<content::RenderProcessHost>(rph)); +} + +base::SharedMemoryHandle GamepadService::GetSharedMemoryHandle( + base::ProcessHandle handle) { + return provider_->GetRendererSharedMemoryHandle(handle); +} + +void GamepadService::Stop() { + --num_readers_; + DCHECK(num_readers_ >= 0); + + if (num_readers_ == 0) + provider_->Pause(); +} + +void GamepadService::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + DCHECK(type == content::NOTIFICATION_RENDERER_PROCESS_CLOSED); + content::BrowserThread::PostTask( + content::BrowserThread::IO, + FROM_HERE, + base::Bind(&GamepadService::Stop, base::Unretained(this))); +} + +} // namespace content diff --git a/content/browser/gamepad/gamepad_service.h b/content/browser/gamepad/gamepad_service.h new file mode 100644 index 0000000..7a00ee6 --- /dev/null +++ b/content/browser/gamepad/gamepad_service.h @@ -0,0 +1,70 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Owns the GamepadProvider (the background polling thread) and keeps track of +// the number of renderers currently using the data (and pausing the provider +// when not in use). + +#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_SERVICE_H +#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_SERVICE_H + +#include "base/basictypes.h" +#include "base/memory/singleton.h" +#include "base/shared_memory.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" + +namespace content { + +class GamepadDataFetcher; +class GamepadProvider; +class RenderProcessHost; + +class GamepadService : public NotificationObserver { + public: + // Returns the GamepadService singleton. + static GamepadService* GetInstance(); + + // Called on IO thread from a renderer host. Increments the number of users + // of the provider. The Provider is running when there's > 0 users, and is + // paused when the count drops to 0. There is no stop, the gamepad service + // registers with the RPH to be notified when the associated renderer closes + // (or crashes). + void Start(GamepadDataFetcher* fetcher, + RenderProcessHost* associated_rph); + + base::SharedMemoryHandle GetSharedMemoryHandle(base::ProcessHandle handle); + + private: + friend struct DefaultSingletonTraits<GamepadService>; + friend class base::RefCountedThreadSafe<GamepadService>; + GamepadService(); + virtual ~GamepadService(); + + // Called when a renderer that Start'd us is closed/crashes. + void Stop(); + + // Run on UI thread to receive notifications of renderer closes. + void RegisterForCloseNotification(RenderProcessHost* rph); + + // NotificationObserver overrides: + virtual void Observe(int type, + const NotificationSource& source, + const NotificationDetails& details) OVERRIDE; + + // A registrar for listening notifications. Used to listen for when an + // associated renderer has gone away (possibly crashed). We don't trust + // the renderers to send a stop message because of the possibility of + // crashing. + NotificationRegistrar registrar_; + + int num_readers_; + scoped_refptr<GamepadProvider> provider_; + + DISALLOW_COPY_AND_ASSIGN(GamepadService); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_SERVICE_H |