| // 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() {} |
| |
| 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(); |
| } |
| |
| 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 |