1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/device_sensors/data_fetcher_shared_memory_base.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/stl_util.h"
10#include "base/threading/thread.h"
11#include "base/timer/timer.h"
12#include "content/common/device_sensors/device_light_hardware_buffer.h"
13#include "content/common/device_sensors/device_motion_hardware_buffer.h"
14#include "content/common/device_sensors/device_orientation_hardware_buffer.h"
15
16namespace content {
17
18namespace {
19
20static size_t GetConsumerSharedMemoryBufferSize(ConsumerType consumer_type) {
21  switch (consumer_type) {
22    case CONSUMER_TYPE_MOTION:
23      return sizeof(DeviceMotionHardwareBuffer);
24    case CONSUMER_TYPE_ORIENTATION:
25      return sizeof(DeviceOrientationHardwareBuffer);
26    case CONSUMER_TYPE_LIGHT:
27      return sizeof(DeviceLightHardwareBuffer);
28    default:
29      NOTREACHED();
30  }
31  return 0;
32}
33
34}  // namespace
35
36class DataFetcherSharedMemoryBase::PollingThread : public base::Thread {
37 public:
38  PollingThread(const char* name, DataFetcherSharedMemoryBase* fetcher);
39  virtual ~PollingThread();
40
41  void AddConsumer(ConsumerType consumer_type, void* buffer);
42  void RemoveConsumer(ConsumerType consumer_type);
43
44  unsigned GetConsumersBitmask() const { return consumers_bitmask_; }
45  bool IsTimerRunning() const { return timer_ ? timer_->IsRunning() : false; }
46
47 private:
48  void DoPoll();
49
50  unsigned consumers_bitmask_;
51  DataFetcherSharedMemoryBase* fetcher_;
52  scoped_ptr<base::RepeatingTimer<PollingThread> > timer_;
53
54  DISALLOW_COPY_AND_ASSIGN(PollingThread);
55};
56
57// --- PollingThread methods
58
59DataFetcherSharedMemoryBase::PollingThread::PollingThread(
60    const char* name, DataFetcherSharedMemoryBase* fetcher)
61    : base::Thread(name),
62      consumers_bitmask_(0),
63      fetcher_(fetcher) {
64}
65
66DataFetcherSharedMemoryBase::PollingThread::~PollingThread() {
67}
68
69void DataFetcherSharedMemoryBase::PollingThread::AddConsumer(
70    ConsumerType consumer_type, void* buffer) {
71  DCHECK(fetcher_);
72  if (!fetcher_->Start(consumer_type, buffer))
73    return;
74
75  consumers_bitmask_ |= consumer_type;
76
77  if (!timer_ && fetcher_->GetType() == FETCHER_TYPE_POLLING_CALLBACK) {
78    timer_.reset(new base::RepeatingTimer<PollingThread>());
79    timer_->Start(FROM_HERE,
80                  fetcher_->GetInterval(),
81                  this, &PollingThread::DoPoll);
82  }
83}
84
85void DataFetcherSharedMemoryBase::PollingThread::RemoveConsumer(
86    ConsumerType consumer_type) {
87  DCHECK(fetcher_);
88  if (!fetcher_->Stop(consumer_type))
89    return;
90
91  consumers_bitmask_ ^= consumer_type;
92
93  if (!consumers_bitmask_)
94    timer_.reset();  // will also stop the timer.
95}
96
97void DataFetcherSharedMemoryBase::PollingThread::DoPoll() {
98  DCHECK(fetcher_);
99  DCHECK(consumers_bitmask_);
100  fetcher_->Fetch(consumers_bitmask_);
101}
102
103// --- end of PollingThread methods
104
105DataFetcherSharedMemoryBase::DataFetcherSharedMemoryBase()
106    : started_consumers_(0) {
107}
108
109DataFetcherSharedMemoryBase::~DataFetcherSharedMemoryBase() {
110  DCHECK_EQ(0u, started_consumers_);
111
112  // make sure polling thread stops asap.
113  if (polling_thread_)
114    polling_thread_->Stop();
115
116  STLDeleteContainerPairSecondPointers(shared_memory_map_.begin(),
117      shared_memory_map_.end());
118}
119
120bool DataFetcherSharedMemoryBase::StartFetchingDeviceData(
121    ConsumerType consumer_type) {
122  if (started_consumers_ & consumer_type)
123    return true;
124
125  void* buffer = GetSharedMemoryBuffer(consumer_type);
126  if (!buffer)
127    return false;
128
129  if (GetType() != FETCHER_TYPE_DEFAULT) {
130    if (!InitAndStartPollingThreadIfNecessary())
131      return false;
132    polling_thread_->message_loop()->PostTask(
133        FROM_HERE,
134        base::Bind(&PollingThread::AddConsumer,
135                   base::Unretained(polling_thread_.get()),
136                   consumer_type, buffer));
137  } else {
138    if (!Start(consumer_type, buffer))
139      return false;
140  }
141
142  started_consumers_ |= consumer_type;
143
144  return true;
145}
146
147bool DataFetcherSharedMemoryBase::StopFetchingDeviceData(
148    ConsumerType consumer_type) {
149  if (!(started_consumers_ & consumer_type))
150    return true;
151
152  if (GetType() != FETCHER_TYPE_DEFAULT) {
153    polling_thread_->message_loop()->PostTask(
154        FROM_HERE,
155        base::Bind(&PollingThread::RemoveConsumer,
156                   base::Unretained(polling_thread_.get()),
157                   consumer_type));
158  } else {
159    if (!Stop(consumer_type))
160      return false;
161  }
162
163  started_consumers_ ^= consumer_type;
164
165  return true;
166}
167
168void DataFetcherSharedMemoryBase::StopFetchingAllDeviceData() {
169  StopFetchingDeviceData(CONSUMER_TYPE_MOTION);
170  StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION);
171  StopFetchingDeviceData(CONSUMER_TYPE_LIGHT);
172}
173
174base::SharedMemoryHandle
175DataFetcherSharedMemoryBase::GetSharedMemoryHandleForProcess(
176    ConsumerType consumer_type, base::ProcessHandle process) {
177  SharedMemoryMap::const_iterator it = shared_memory_map_.find(consumer_type);
178  if (it == shared_memory_map_.end())
179    return base::SharedMemory::NULLHandle();
180
181  base::SharedMemoryHandle renderer_handle;
182  it->second->ShareToProcess(process, &renderer_handle);
183  return renderer_handle;
184}
185
186bool DataFetcherSharedMemoryBase::InitAndStartPollingThreadIfNecessary() {
187  if (polling_thread_)
188    return true;
189
190  polling_thread_.reset(
191      new PollingThread("Inertial Device Sensor poller", this));
192
193  if (!polling_thread_->Start()) {
194      LOG(ERROR) << "Failed to start inertial sensor data polling thread";
195      return false;
196  }
197  return true;
198}
199
200void DataFetcherSharedMemoryBase::Fetch(unsigned consumer_bitmask) {
201  NOTIMPLEMENTED();
202}
203
204DataFetcherSharedMemoryBase::FetcherType
205DataFetcherSharedMemoryBase::GetType() const {
206  return FETCHER_TYPE_DEFAULT;
207}
208
209base::TimeDelta DataFetcherSharedMemoryBase::GetInterval() const {
210  return base::TimeDelta::FromMilliseconds(kInertialSensorIntervalMillis);
211}
212
213base::SharedMemory* DataFetcherSharedMemoryBase::GetSharedMemory(
214    ConsumerType consumer_type) {
215  SharedMemoryMap::const_iterator it = shared_memory_map_.find(consumer_type);
216  if (it != shared_memory_map_.end())
217    return it->second;
218
219  size_t buffer_size = GetConsumerSharedMemoryBufferSize(consumer_type);
220  if (buffer_size == 0)
221    return NULL;
222
223  scoped_ptr<base::SharedMemory> new_shared_mem(new base::SharedMemory);
224  if (new_shared_mem->CreateAndMapAnonymous(buffer_size)) {
225    if (void* mem = new_shared_mem->memory()) {
226      memset(mem, 0, buffer_size);
227      base::SharedMemory* shared_mem = new_shared_mem.release();
228      shared_memory_map_[consumer_type] = shared_mem;
229      return shared_mem;
230    }
231  }
232  LOG(ERROR) << "Failed to initialize shared memory";
233  return NULL;
234}
235
236void* DataFetcherSharedMemoryBase::GetSharedMemoryBuffer(
237    ConsumerType consumer_type) {
238  if (base::SharedMemory* shared_memory = GetSharedMemory(consumer_type))
239    return shared_memory->memory();
240  return NULL;
241}
242
243base::MessageLoop* DataFetcherSharedMemoryBase::GetPollingMessageLoop() const {
244  return polling_thread_ ? polling_thread_->message_loop() : NULL;
245}
246
247bool DataFetcherSharedMemoryBase::IsPollingTimerRunningForTesting() const {
248  return polling_thread_ ? polling_thread_->IsTimerRunning() : false;
249}
250
251}  // namespace content
252