blob: 6cc63212cc404f9c571ac7c6666ccf7ebb2f26aa [file] [log] [blame]
// Copyright (c) 2012 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/device_orientation/provider_impl.h"
#include <set>
#include <vector>
#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/threading/thread.h"
#include "base/threading/worker_pool.h"
namespace {
void DeleteThread(base::Thread* thread) {
delete thread;
}
}
namespace content {
class ProviderImpl::PollingThread : public base::Thread {
public:
PollingThread(const char* name,
base::WeakPtr<ProviderImpl> provider,
base::MessageLoop* creator_loop);
virtual ~PollingThread();
// Method for creating a DataFetcher and starting the polling, if the fetcher
// can provide this type of data.
void Initialize(DataFetcherFactory factory, DeviceData::Type type);
// Method for adding a type of data to poll for.
void DoAddPollingDataType(DeviceData::Type type);
private:
// Method for polling a DataFetcher.
void DoPoll();
void ScheduleDoPoll();
// Schedule a notification to the |provider_| which lives on a different
// thread (|creator_loop_| is its message loop).
void ScheduleDoNotify(const scoped_refptr<const DeviceData>& device_data,
DeviceData::Type device_data_type);
enum { kDesiredSamplingIntervalMs = 100 };
base::TimeDelta SamplingInterval() const;
// The Message Loop on which this object was created.
// Typically the I/O loop, but may be something else during testing.
base::MessageLoop* creator_loop_;
scoped_ptr<DataFetcher> data_fetcher_;
std::map<DeviceData::Type, scoped_refptr<const DeviceData> >
last_device_data_map_;
std::set<DeviceData::Type> polling_data_types_;
base::WeakPtr<ProviderImpl> provider_;
};
ProviderImpl::PollingThread::PollingThread(const char* name,
base::WeakPtr<ProviderImpl> provider,
base::MessageLoop* creator_loop)
: base::Thread(name), creator_loop_(creator_loop), provider_(provider) {}
ProviderImpl::PollingThread::~PollingThread() {
Stop();
}
void ProviderImpl::PollingThread::DoAddPollingDataType(DeviceData::Type type) {
DCHECK(base::MessageLoop::current() == message_loop());
polling_data_types_.insert(type);
}
void ProviderImpl::PollingThread::Initialize(DataFetcherFactory factory,
DeviceData::Type type) {
DCHECK(base::MessageLoop::current() == message_loop());
if (factory != NULL) {
// Try to use factory to create a fetcher that can provide this type of
// data. If factory creates a fetcher that provides this type of data,
// start polling.
scoped_ptr<DataFetcher> fetcher(factory());
if (fetcher) {
scoped_refptr<const DeviceData> device_data(fetcher->GetDeviceData(type));
if (device_data.get() != NULL) {
// Pass ownership of fetcher to provider_.
data_fetcher_.swap(fetcher);
last_device_data_map_[type] = device_data;
// Notify observers.
ScheduleDoNotify(device_data, type);
// Start polling.
ScheduleDoPoll();
return;
}
}
}
// When no device data can be provided.
ScheduleDoNotify(NULL, type);
}
void ProviderImpl::PollingThread::ScheduleDoNotify(
const scoped_refptr<const DeviceData>& device_data,
DeviceData::Type device_data_type) {
DCHECK(base::MessageLoop::current() == message_loop());
creator_loop_->PostTask(FROM_HERE,
base::Bind(&ProviderImpl::DoNotify, provider_,
device_data, device_data_type));
}
void ProviderImpl::PollingThread::DoPoll() {
DCHECK(base::MessageLoop::current() == message_loop());
// Poll the fetcher for each type of data.
typedef std::set<DeviceData::Type>::const_iterator SetIterator;
for (SetIterator i = polling_data_types_.begin();
i != polling_data_types_.end(); ++i) {
DeviceData::Type device_data_type = *i;
scoped_refptr<const DeviceData> device_data(data_fetcher_->GetDeviceData(
device_data_type));
if (device_data.get() == NULL) {
LOG(ERROR) << "Failed to poll device data fetcher.";
ScheduleDoNotify(NULL, device_data_type);
continue;
}
const DeviceData* old_data = last_device_data_map_[device_data_type].get();
if (old_data != NULL && !device_data->ShouldFireEvent(old_data))
continue;
// Update the last device data of this type and notify observers.
last_device_data_map_[device_data_type] = device_data;
ScheduleDoNotify(device_data, device_data_type);
}
ScheduleDoPoll();
}
void ProviderImpl::PollingThread::ScheduleDoPoll() {
DCHECK(base::MessageLoop::current() == message_loop());
message_loop()->PostDelayedTask(
FROM_HERE,
base::Bind(&PollingThread::DoPoll, base::Unretained(this)),
SamplingInterval());
}
base::TimeDelta ProviderImpl::PollingThread::SamplingInterval() const {
DCHECK(base::MessageLoop::current() == message_loop());
DCHECK(data_fetcher_.get());
// TODO(erg): There used to be unused code here, that called a default
// implementation on the DataFetcherInterface that was never defined. I'm
// removing unused methods from headers.
return base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs);
}
ProviderImpl::ProviderImpl(DataFetcherFactory factory)
: creator_loop_(base::MessageLoop::current()),
factory_(factory),
weak_factory_(this),
polling_thread_(NULL) {
}
ProviderImpl::~ProviderImpl() {
Stop();
}
void ProviderImpl::ScheduleDoAddPollingDataType(DeviceData::Type type) {
DCHECK(base::MessageLoop::current() == creator_loop_);
base::MessageLoop* polling_loop = polling_thread_->message_loop();
polling_loop->PostTask(FROM_HERE,
base::Bind(&PollingThread::DoAddPollingDataType,
base::Unretained(polling_thread_),
type));
}
void ProviderImpl::AddObserver(Observer* observer) {
DCHECK(base::MessageLoop::current() == creator_loop_);
DeviceData::Type type = observer->device_data_type();
observers_.insert(observer);
if (observers_.size() == 1)
Start(type);
else {
// Notify observer of most recent notification if one exists.
const DeviceData* last_notification = last_notifications_map_[type].get();
if (last_notification != NULL)
observer->OnDeviceDataUpdate(last_notification, type);
}
ScheduleDoAddPollingDataType(type);
}
void ProviderImpl::RemoveObserver(Observer* observer) {
DCHECK(base::MessageLoop::current() == creator_loop_);
observers_.erase(observer);
if (observers_.empty())
Stop();
}
void ProviderImpl::Start(DeviceData::Type type) {
DCHECK(base::MessageLoop::current() == creator_loop_);
DCHECK(!polling_thread_);
polling_thread_ = new PollingThread("Device data polling thread",
weak_factory_.GetWeakPtr(),
creator_loop_);
#if defined(OS_WIN)
polling_thread_->init_com_with_mta(true);
#endif
if (!polling_thread_->Start()) {
LOG(ERROR) << "Failed to start device data polling thread";
delete polling_thread_;
polling_thread_ = NULL;
return;
}
ScheduleInitializePollingThread(type);
}
void ProviderImpl::Stop() {
DCHECK(base::MessageLoop::current() == creator_loop_);
weak_factory_.InvalidateWeakPtrs();
if (polling_thread_) {
polling_thread_->StopSoon();
bool posted = base::WorkerPool::PostTask(
FROM_HERE,
base::Bind(&DeleteThread, base::Unretained(polling_thread_)),
true /* task is slow */);
DCHECK(posted);
polling_thread_ = NULL;
}
}
void ProviderImpl::ScheduleInitializePollingThread(
DeviceData::Type device_data_type) {
DCHECK(base::MessageLoop::current() == creator_loop_);
base::MessageLoop* polling_loop = polling_thread_->message_loop();
polling_loop->PostTask(FROM_HERE,
base::Bind(&PollingThread::Initialize,
base::Unretained(polling_thread_),
factory_,
device_data_type));
}
void ProviderImpl::DoNotify(const scoped_refptr<const DeviceData>& data,
DeviceData::Type device_data_type) {
DCHECK(base::MessageLoop::current() == creator_loop_);
// Update last notification of this type.
last_notifications_map_[device_data_type] = data;
// Notify observers of this type of the new data.
typedef std::set<Observer*>::const_iterator ConstIterator;
for (ConstIterator i = observers_.begin(); i != observers_.end(); ++i) {
if ((*i)->device_data_type() == device_data_type)
(*i)->OnDeviceDataUpdate(data.get(), device_data_type);
}
if (data.get() == NULL) {
// Notify observers exactly once about failure to provide data.
typedef std::set<Observer*>::iterator Iterator;
Iterator i = observers_.begin();
while (i != observers_.end()) {
Iterator current = i++;
if ((*current)->device_data_type() == device_data_type)
observers_.erase(current);
}
if (observers_.empty())
Stop();
}
}
} // namespace content