blob: ff473d4c8635424c5a6b384a011e140f3ea81c50 [file] [log] [blame]
// Copyright 2014 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 "device/sensors/data_fetcher_shared_memory_base.h"
#include <stddef.h>
#include <string.h>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/threading/thread.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "device/sensors/public/cpp/device_motion_hardware_buffer.h"
#include "device/sensors/public/cpp/device_orientation_hardware_buffer.h"
namespace device {
namespace {
size_t GetConsumerSharedMemoryBufferSize(ConsumerType consumer_type) {
switch (consumer_type) {
case CONSUMER_TYPE_MOTION:
return sizeof(DeviceMotionHardwareBuffer);
case CONSUMER_TYPE_ORIENTATION:
case CONSUMER_TYPE_ORIENTATION_ABSOLUTE:
return sizeof(DeviceOrientationHardwareBuffer);
default:
NOTREACHED();
}
return 0;
}
} // namespace
class DataFetcherSharedMemoryBase::PollingThread : public base::Thread {
public:
PollingThread(const char* name, DataFetcherSharedMemoryBase* fetcher);
~PollingThread() override;
void AddConsumer(ConsumerType consumer_type, void* buffer);
void RemoveConsumer(ConsumerType consumer_type);
unsigned GetConsumersBitmask() const { return consumers_bitmask_; }
bool IsTimerRunning() const { return timer_ ? timer_->IsRunning() : false; }
private:
void DoPoll();
unsigned consumers_bitmask_;
DataFetcherSharedMemoryBase* fetcher_;
std::unique_ptr<base::RepeatingTimer> timer_;
DISALLOW_COPY_AND_ASSIGN(PollingThread);
};
// --- PollingThread methods
DataFetcherSharedMemoryBase::PollingThread::PollingThread(
const char* name,
DataFetcherSharedMemoryBase* fetcher)
: base::Thread(name), consumers_bitmask_(0), fetcher_(fetcher) {}
DataFetcherSharedMemoryBase::PollingThread::~PollingThread() = default;
void DataFetcherSharedMemoryBase::PollingThread::AddConsumer(
ConsumerType consumer_type,
void* buffer) {
DCHECK(fetcher_);
if (!fetcher_->Start(consumer_type, buffer))
return;
consumers_bitmask_ |= consumer_type;
if (!timer_ && fetcher_->GetType() == FETCHER_TYPE_POLLING_CALLBACK) {
timer_.reset(new base::RepeatingTimer());
timer_->Start(FROM_HERE, fetcher_->GetInterval(), this,
&PollingThread::DoPoll);
}
}
void DataFetcherSharedMemoryBase::PollingThread::RemoveConsumer(
ConsumerType consumer_type) {
DCHECK(fetcher_);
if (!fetcher_->Stop(consumer_type))
return;
consumers_bitmask_ &= ~consumer_type;
if (!consumers_bitmask_)
timer_.reset(); // will also stop the timer.
}
void DataFetcherSharedMemoryBase::PollingThread::DoPoll() {
DCHECK(fetcher_);
DCHECK(consumers_bitmask_);
fetcher_->Fetch(consumers_bitmask_);
}
// --- end of PollingThread methods
DataFetcherSharedMemoryBase::DataFetcherSharedMemoryBase()
: started_consumers_(0) {}
DataFetcherSharedMemoryBase::~DataFetcherSharedMemoryBase() {
DCHECK_EQ(0u, started_consumers_);
// By this point the polling thread should have already been stopped (it's not
// safe for it to be running in this class's destructor as tasks are posted to
// it that call virtual methods of this class).
DCHECK(!polling_thread_ || !polling_thread_->IsRunning());
}
bool DataFetcherSharedMemoryBase::StartFetchingDeviceData(
ConsumerType consumer_type) {
if (started_consumers_ & consumer_type)
return true;
void* buffer = GetSharedMemoryBuffer(consumer_type);
if (!buffer)
return false;
size_t buffer_size = GetConsumerSharedMemoryBufferSize(consumer_type);
// buffer size should be strictly positive because buffer is non-zero.
DCHECK(buffer_size > 0);
// make sure to clear any potentially stale values in the memory buffer.
memset(buffer, 0, buffer_size);
if (GetType() != FETCHER_TYPE_DEFAULT) {
if (!InitAndStartPollingThreadIfNecessary())
return false;
polling_thread_->task_runner()->PostTask(
FROM_HERE, base::Bind(&PollingThread::AddConsumer,
base::Unretained(polling_thread_.get()),
consumer_type, buffer));
} else {
if (!Start(consumer_type, buffer))
return false;
}
started_consumers_ |= consumer_type;
return true;
}
bool DataFetcherSharedMemoryBase::StopFetchingDeviceData(
ConsumerType consumer_type) {
if (!(started_consumers_ & consumer_type))
return true;
if (GetType() != FETCHER_TYPE_DEFAULT) {
polling_thread_->task_runner()->PostTask(
FROM_HERE,
base::Bind(&PollingThread::RemoveConsumer,
base::Unretained(polling_thread_.get()), consumer_type));
} else {
if (!Stop(consumer_type))
return false;
}
started_consumers_ ^= consumer_type;
return true;
}
void DataFetcherSharedMemoryBase::Shutdown() {
StopFetchingDeviceData(CONSUMER_TYPE_MOTION);
StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION);
StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION_ABSOLUTE);
// Ensure that the polling thread stops before entering the destructor of the
// subclass, as the stopping of the polling thread causes tasks to execute
// that call virtual methods of this class, which can cause crashes if they
// execute while (or after) the subclass is being torn down.
if (polling_thread_)
polling_thread_->Stop();
}
mojo::ScopedSharedBufferHandle
DataFetcherSharedMemoryBase::GetSharedMemoryHandle(ConsumerType consumer_type) {
auto it = shared_memory_map_.find(consumer_type);
DCHECK(it != shared_memory_map_.end());
return it->second.first->Clone(
mojo::SharedBufferHandle::AccessMode::READ_ONLY);
}
bool DataFetcherSharedMemoryBase::InitAndStartPollingThreadIfNecessary() {
if (polling_thread_)
return true;
polling_thread_.reset(new PollingThread("Device Sensor poller", this));
#if defined(OS_WIN)
polling_thread_->init_com_with_mta(true);
#endif
if (!polling_thread_->Start()) {
LOG(ERROR) << "Failed to start sensor data polling thread";
return false;
}
return true;
}
void DataFetcherSharedMemoryBase::Fetch(unsigned consumer_bitmask) {
NOTIMPLEMENTED();
}
DataFetcherSharedMemoryBase::FetcherType DataFetcherSharedMemoryBase::GetType()
const {
return FETCHER_TYPE_DEFAULT;
}
base::TimeDelta DataFetcherSharedMemoryBase::GetInterval() const {
return base::TimeDelta::FromMicroseconds(kDeviceSensorIntervalMicroseconds);
}
void* DataFetcherSharedMemoryBase::GetSharedMemoryBuffer(
ConsumerType consumer_type) {
auto it = shared_memory_map_.find(consumer_type);
if (it != shared_memory_map_.end())
return it->second.second.get();
size_t buffer_size = GetConsumerSharedMemoryBufferSize(consumer_type);
if (buffer_size == 0)
return nullptr;
mojo::ScopedSharedBufferHandle buffer =
mojo::SharedBufferHandle::Create(buffer_size);
mojo::ScopedSharedBufferMapping mapping = buffer->Map(buffer_size);
if (!mapping)
return nullptr;
void* mem = mapping.get();
memset(mem, 0, buffer_size);
shared_memory_map_[consumer_type] =
std::make_pair(std::move(buffer), std::move(mapping));
return mem;
}
base::MessageLoop* DataFetcherSharedMemoryBase::GetPollingMessageLoop() const {
return polling_thread_ ? polling_thread_->message_loop() : nullptr;
}
bool DataFetcherSharedMemoryBase::IsPollingTimerRunningForTesting() const {
return polling_thread_ ? polling_thread_->IsTimerRunning() : false;
}
} // namespace device