Commit 1ced7256 authored by scottmg@chromium.org's avatar scottmg@chromium.org

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
parent 02fea642
...@@ -23,8 +23,6 @@ ...@@ -23,8 +23,6 @@
namespace content { namespace content {
GamepadProvider* GamepadProvider::instance_ = NULL;
// Define the default data fetcher that GamepadProvider will use if none is // Define the default data fetcher that GamepadProvider will use if none is
// supplied. (GamepadPlatformDataFetcher). // supplied. (GamepadPlatformDataFetcher).
#if defined(OS_WIN) #if defined(OS_WIN)
...@@ -44,9 +42,9 @@ typedef GamepadEmptyDataFetcher GamepadPlatformDataFetcher; ...@@ -44,9 +42,9 @@ typedef GamepadEmptyDataFetcher GamepadPlatformDataFetcher;
#endif #endif
GamepadProvider::GamepadProvider(GamepadDataFetcher* fetcher) GamepadProvider::GamepadProvider(GamepadDataFetcher* fetcher)
: creator_loop_(MessageLoop::current()->message_loop_proxy()), : is_paused_(false),
provided_fetcher_(fetcher),
devices_changed_(true), devices_changed_(true),
provided_fetcher_(fetcher),
ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
size_t data_size = sizeof(GamepadHardwareBuffer); size_t data_size = sizeof(GamepadHardwareBuffer);
base::SystemMonitor* monitor = base::SystemMonitor::Get(); base::SystemMonitor* monitor = base::SystemMonitor::Get();
...@@ -55,13 +53,23 @@ GamepadProvider::GamepadProvider(GamepadDataFetcher* fetcher) ...@@ -55,13 +53,23 @@ GamepadProvider::GamepadProvider(GamepadDataFetcher* fetcher)
gamepad_shared_memory_.CreateAndMapAnonymous(data_size); gamepad_shared_memory_.CreateAndMapAnonymous(data_size);
GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
memset(hwbuf, 0, sizeof(GamepadHardwareBuffer)); 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() { GamepadProvider::~GamepadProvider() {
base::SystemMonitor* monitor = base::SystemMonitor::Get(); base::SystemMonitor* monitor = base::SystemMonitor::Get();
if (monitor) if (monitor)
monitor->RemoveDevicesChangedObserver(this); monitor->RemoveDevicesChangedObserver(this);
Stop();
polling_thread_.reset();
data_fetcher_.reset();
} }
base::SharedMemoryHandle GamepadProvider::GetRendererSharedMemoryHandle( base::SharedMemoryHandle GamepadProvider::GetRendererSharedMemoryHandle(
...@@ -71,34 +79,28 @@ base::SharedMemoryHandle GamepadProvider::GetRendererSharedMemoryHandle( ...@@ -71,34 +79,28 @@ base::SharedMemoryHandle GamepadProvider::GetRendererSharedMemoryHandle(
return renderer_handle; return renderer_handle;
} }
void GamepadProvider::OnDevicesChanged() { void GamepadProvider::Pause() {
devices_changed_ = true; base::AutoLock lock(is_paused_lock_);
is_paused_ = true;
} }
void GamepadProvider::Start() { void GamepadProvider::Resume() {
DCHECK(MessageLoop::current()->message_loop_proxy() == creator_loop_); {
base::AutoLock lock(is_paused_lock_);
if (polling_thread_.get()) if (!is_paused_)
return; return;
is_paused_ = false;
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;
} }
MessageLoop* polling_loop = polling_thread_->message_loop(); MessageLoop* polling_loop = polling_thread_->message_loop();
polling_loop->PostTask( polling_loop->PostTask(
FROM_HERE, FROM_HERE,
base::Bind(&GamepadProvider::DoInitializePollingThread, this)); base::Bind(&GamepadProvider::ScheduleDoPoll, this));
} }
void GamepadProvider::Stop() { void GamepadProvider::OnDevicesChanged() {
DCHECK(MessageLoop::current()->message_loop_proxy() == creator_loop_); base::AutoLock lock(devices_changed_lock_);
devices_changed_ = true;
polling_thread_.reset();
data_fetcher_.reset();
} }
void GamepadProvider::DoInitializePollingThread() { void GamepadProvider::DoInitializePollingThread() {
...@@ -116,6 +118,7 @@ void GamepadProvider::DoInitializePollingThread() { ...@@ -116,6 +118,7 @@ void GamepadProvider::DoInitializePollingThread() {
void GamepadProvider::DoPoll() { void GamepadProvider::DoPoll() {
DCHECK(MessageLoop::current() == polling_thread_->message_loop()); DCHECK(MessageLoop::current() == polling_thread_->message_loop());
bool changed;
GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
ANNOTATE_BENIGN_RACE_SIZED( ANNOTATE_BENIGN_RACE_SIZED(
...@@ -123,12 +126,18 @@ void GamepadProvider::DoPoll() { ...@@ -123,12 +126,18 @@ void GamepadProvider::DoPoll() {
sizeof(WebKit::WebGamepads), sizeof(WebKit::WebGamepads),
"Racey reads are discarded"); "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. // Acquire the SeqLock. There is only ever one writer to this data.
// See gamepad_hardware_buffer.h. // See gamepad_hardware_buffer.h.
hwbuf->sequence.WriteBegin(); hwbuf->sequence.WriteBegin();
data_fetcher_->GetGamepadData(&hwbuf->buffer, devices_changed_); data_fetcher_->GetGamepadData(&hwbuf->buffer, changed);
hwbuf->sequence.WriteEnd(); hwbuf->sequence.WriteEnd();
devices_changed_ = false;
// Schedule our next interval of polling. // Schedule our next interval of polling.
ScheduleDoPoll(); ScheduleDoPoll();
} }
...@@ -136,6 +145,12 @@ void GamepadProvider::DoPoll() { ...@@ -136,6 +145,12 @@ void GamepadProvider::DoPoll() {
void GamepadProvider::ScheduleDoPoll() { void GamepadProvider::ScheduleDoPoll() {
DCHECK(MessageLoop::current() == polling_thread_->message_loop()); DCHECK(MessageLoop::current() == polling_thread_->message_loop());
{
base::AutoLock lock(is_paused_lock_);
if (is_paused_)
return;
}
MessageLoop::current()->PostDelayedTask( MessageLoop::current()->PostDelayedTask(
FROM_HERE, FROM_HERE,
base::Bind(&GamepadProvider::DoPoll, weak_factory_.GetWeakPtr()), base::Bind(&GamepadProvider::DoPoll, weak_factory_.GetWeakPtr()),
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/message_loop_proxy.h" #include "base/message_loop_proxy.h"
#include "base/shared_memory.h" #include "base/shared_memory.h"
#include "base/synchronization/lock.h"
#include "base/system_monitor/system_monitor.h" #include "base/system_monitor/system_monitor.h"
#include "base/task.h" #include "base/task.h"
#include "content/browser/gamepad/data_fetcher.h" #include "content/browser/gamepad/data_fetcher.h"
...@@ -29,12 +30,14 @@ class CONTENT_EXPORT GamepadProvider : ...@@ -29,12 +30,14 @@ class CONTENT_EXPORT GamepadProvider :
public: public:
explicit GamepadProvider(GamepadDataFetcher* fetcher); explicit GamepadProvider(GamepadDataFetcher* fetcher);
// Starts or Stops the provider. Called from creator_loop_.
void Start();
void Stop();
base::SharedMemoryHandle GetRendererSharedMemoryHandle( base::SharedMemoryHandle GetRendererSharedMemoryHandle(
base::ProcessHandle renderer_process); base::ProcessHandle renderer_process);
// Pause and resume the background polling thread. Can be called from any
// thread.
void Pause();
void Resume();
private: private:
friend class base::RefCountedThreadSafe<GamepadProvider>; friend class base::RefCountedThreadSafe<GamepadProvider>;
...@@ -53,16 +56,27 @@ class CONTENT_EXPORT GamepadProvider : ...@@ -53,16 +56,27 @@ class CONTENT_EXPORT GamepadProvider :
enum { kDesiredSamplingIntervalMs = 16 }; 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. // The Message Loop on which this object was created.
// Typically the I/O loop, but may be something else during testing. // Typically the I/O loop, but may be something else during testing.
scoped_refptr<base::MessageLoopProxy> creator_loop_;
scoped_ptr<GamepadDataFetcher> provided_fetcher_; scoped_ptr<GamepadDataFetcher> provided_fetcher_;
// When polling_thread_ is running, members below are only to be used // When polling_thread_ is running, members below are only to be used
// from that thread. // from that thread.
scoped_ptr<GamepadDataFetcher> data_fetcher_; scoped_ptr<GamepadDataFetcher> data_fetcher_;
base::SharedMemory gamepad_shared_memory_; base::SharedMemory gamepad_shared_memory_;
bool devices_changed_;
// Polling is done on this background thread. // Polling is done on this background thread.
scoped_ptr<base::Thread> polling_thread_; scoped_ptr<base::Thread> polling_thread_;
......
...@@ -56,15 +56,6 @@ class GamepadProviderTest : public testing::Test { ...@@ -56,15 +56,6 @@ class GamepadProviderTest : public testing::Test {
scoped_refptr<GamepadProvider> provider_; 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) { TEST_F(GamepadProviderTest, PollingAccess) {
WebGamepads test_data; WebGamepads test_data;
test_data.length = 1; test_data.length = 1;
...@@ -77,7 +68,6 @@ TEST_F(GamepadProviderTest, PollingAccess) { ...@@ -77,7 +68,6 @@ TEST_F(GamepadProviderTest, PollingAccess) {
test_data.items[0].axes[1] = .5f; test_data.items[0].axes[1] = .5f;
GamepadProvider* provider = CreateProvider(test_data); GamepadProvider* provider = CreateProvider(test_data);
provider->Start();
main_message_loop_.RunAllPending(); main_message_loop_.RunAllPending();
...@@ -107,8 +97,6 @@ TEST_F(GamepadProviderTest, PollingAccess) { ...@@ -107,8 +97,6 @@ TEST_F(GamepadProviderTest, PollingAccess) {
EXPECT_EQ(2u, output.items[0].axesLength); EXPECT_EQ(2u, output.items[0].axesLength);
EXPECT_EQ(-1.f, output.items[0].axes[0]); EXPECT_EQ(-1.f, output.items[0].axes[0]);
EXPECT_EQ(0.5f, output.items[0].axes[1]); EXPECT_EQ(0.5f, output.items[0].axes[1]);
provider->Stop();
} }
} // namespace } // namespace
......
// 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
// 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
...@@ -4,9 +4,14 @@ ...@@ -4,9 +4,14 @@
#include "content/browser/renderer_host/gamepad_browser_message_filter.h" #include "content/browser/renderer_host/gamepad_browser_message_filter.h"
#include "content/browser/gamepad/gamepad_service.h"
#include "content/common/gamepad_messages.h" #include "content/common/gamepad_messages.h"
GamepadBrowserMessageFilter::GamepadBrowserMessageFilter() { namespace content {
GamepadBrowserMessageFilter::GamepadBrowserMessageFilter(
content::RenderProcessHost* render_process_host)
: render_process_host_(render_process_host) {
} }
GamepadBrowserMessageFilter::~GamepadBrowserMessageFilter() { GamepadBrowserMessageFilter::~GamepadBrowserMessageFilter() {
...@@ -28,14 +33,14 @@ bool GamepadBrowserMessageFilter::OnMessageReceived( ...@@ -28,14 +33,14 @@ bool GamepadBrowserMessageFilter::OnMessageReceived(
void GamepadBrowserMessageFilter::OnGamepadStartPolling( void GamepadBrowserMessageFilter::OnGamepadStartPolling(
base::SharedMemoryHandle* renderer_handle) { base::SharedMemoryHandle* renderer_handle) {
if (!provider_) { GamepadService* service = GamepadService::GetInstance();
provider_ = new content::GamepadProvider(NULL); service->Start(NULL, render_process_host_);
provider_->Start(); *renderer_handle = service->GetSharedMemoryHandle(peer_handle());
}
*renderer_handle = provider_->GetRendererSharedMemoryHandle(peer_handle());
} }
void GamepadBrowserMessageFilter::OnGamepadStopPolling() { void GamepadBrowserMessageFilter::OnGamepadStopPolling() {
// TODO(scottmg) Remove this message entirely? // TODO(scottmg): Probably get rid of this message. We can't trust it will
// Stop is currently handled by the refcount on provider_. // arrive anyway if the renderer crashes, etc.
} }
} // namespace content
...@@ -7,11 +7,18 @@ ...@@ -7,11 +7,18 @@
#pragma once #pragma once
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/shared_memory.h"
#include "content/browser/browser_message_filter.h" #include "content/browser/browser_message_filter.h"
#include "content/browser/gamepad/gamepad_provider.h"
namespace content {
class GamepadService;
class RenderProcessHost;
class GamepadBrowserMessageFilter : public BrowserMessageFilter { class GamepadBrowserMessageFilter : public BrowserMessageFilter {
public: public:
explicit GamepadBrowserMessageFilter(RenderProcessHost* rph);
// BrowserMessageFilter implementation. // BrowserMessageFilter implementation.
virtual bool OnMessageReceived(const IPC::Message& message, virtual bool OnMessageReceived(const IPC::Message& message,
bool* message_was_ok) OVERRIDE; bool* message_was_ok) OVERRIDE;
...@@ -23,9 +30,11 @@ class GamepadBrowserMessageFilter : public BrowserMessageFilter { ...@@ -23,9 +30,11 @@ class GamepadBrowserMessageFilter : public BrowserMessageFilter {
void OnGamepadStartPolling(base::SharedMemoryHandle* renderer_handle); void OnGamepadStartPolling(base::SharedMemoryHandle* renderer_handle);
void OnGamepadStopPolling(); void OnGamepadStopPolling();
scoped_refptr<content::GamepadProvider> provider_; RenderProcessHost* render_process_host_;
DISALLOW_COPY_AND_ASSIGN(GamepadBrowserMessageFilter); DISALLOW_COPY_AND_ASSIGN(GamepadBrowserMessageFilter);
}; };
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_GAMEPAD_BROWSER_MESSAGE_FILTER_H_ #endif // CONTENT_BROWSER_RENDERER_HOST_GAMEPAD_BROWSER_MESSAGE_FILTER_H_
...@@ -538,7 +538,7 @@ void RenderProcessHostImpl::CreateMessageFilters() { ...@@ -538,7 +538,7 @@ void RenderProcessHostImpl::CreateMessageFilters() {
channel_->AddFilter(new QuotaDispatcherHost( channel_->AddFilter(new QuotaDispatcherHost(
GetID(), GetBrowserContext()->GetQuotaManager(), GetID(), GetBrowserContext()->GetQuotaManager(),
content::GetContentClient()->browser()->CreateQuotaPermissionContext())); content::GetContentClient()->browser()->CreateQuotaPermissionContext()));
channel_->AddFilter(new GamepadBrowserMessageFilter); channel_->AddFilter(new content::GamepadBrowserMessageFilter(this));
channel_->AddFilter(new ProfilerMessageFilter()); channel_->AddFilter(new ProfilerMessageFilter());
} }
......
...@@ -211,6 +211,8 @@ ...@@ -211,6 +211,8 @@
'browser/gamepad/data_fetcher_win.h', 'browser/gamepad/data_fetcher_win.h',
'browser/gamepad/gamepad_provider.cc', 'browser/gamepad/gamepad_provider.cc',
'browser/gamepad/gamepad_provider.h', 'browser/gamepad/gamepad_provider.h',
'browser/gamepad/gamepad_service.cc',
'browser/gamepad/gamepad_service.h',
'browser/geolocation/access_token_store.cc', 'browser/geolocation/access_token_store.cc',
'browser/geolocation/access_token_store.h', 'browser/geolocation/access_token_store.h',
'browser/geolocation/arbitrator_dependency_factory.cc', 'browser/geolocation/arbitrator_dependency_factory.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment