diff options
author | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-27 23:55:44 +0000 |
---|---|---|
committer | thakis@chromium.org <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-27 23:55:44 +0000 |
commit | f4195b9441438b98f232167e50e0086474d7bc50 (patch) | |
tree | b3f2cda1241f6dfb8f854ca119768b494ccd151b /content/browser/device_orientation | |
parent | b7520dd23365bdc2ab52e6730d9b6e6b647e2e8f (diff) | |
download | chromium_src-f4195b9441438b98f232167e50e0086474d7bc50.zip chromium_src-f4195b9441438b98f232167e50e0086474d7bc50.tar.gz chromium_src-f4195b9441438b98f232167e50e0086474d7bc50.tar.bz2 |
Mac: implement orientation/motion shared memory data fetcher.
Add Device Motion/Orientation data fetcher for the mac platform. The fetcher uses the mac's sudden motion sensor to obtain acceleration and orientation information. It uses a polling thread to update shared memory at regular intervals. This patch also contains some minor changes to the base polling thread implementation to make the fetcher thread safe.
also fixes a leak in a test, see crbug.com/280129.
BUG=261165,280129
NOTRY=true
R=bulach@chromium.org, piman@chromium.org
Review URL: https://codereview.chromium.org/22926029
Patch from Tim Volodine <timvolodine@chromium.org>.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@219861 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser/device_orientation')
5 files changed, 257 insertions, 65 deletions
diff --git a/content/browser/device_orientation/data_fetcher_shared_memory.h b/content/browser/device_orientation/data_fetcher_shared_memory.h index 9d378eb..cbc274a 100644 --- a/content/browser/device_orientation/data_fetcher_shared_memory.h +++ b/content/browser/device_orientation/data_fetcher_shared_memory.h @@ -7,6 +7,13 @@ #include "content/browser/device_orientation/data_fetcher_shared_memory_base.h" +#if defined(OS_MACOSX) +#include "content/common/device_motion_hardware_buffer.h" +#include "content/common/device_orientation/device_orientation_hardware_buffer.h" + +class SuddenMotionSensor; +#endif + namespace content { class CONTENT_EXPORT DataFetcherSharedMemory @@ -21,6 +28,15 @@ class CONTENT_EXPORT DataFetcherSharedMemory virtual bool Start(ConsumerType consumer_type) OVERRIDE; virtual bool Stop(ConsumerType consumer_type) OVERRIDE; +#if defined(OS_MACOSX) + virtual void Fetch(unsigned consumer_bitmask) OVERRIDE; + virtual bool IsPolling() const OVERRIDE; + + DeviceMotionHardwareBuffer* motion_buffer_; + DeviceOrientationHardwareBuffer* orientation_buffer_; + scoped_ptr<SuddenMotionSensor> sudden_motion_sensor_; +#endif + DISALLOW_COPY_AND_ASSIGN(DataFetcherSharedMemory); }; diff --git a/content/browser/device_orientation/data_fetcher_shared_memory_base.cc b/content/browser/device_orientation/data_fetcher_shared_memory_base.cc index 167b787..5b60f1f 100644 --- a/content/browser/device_orientation/data_fetcher_shared_memory_base.cc +++ b/content/browser/device_orientation/data_fetcher_shared_memory_base.cc @@ -21,7 +21,9 @@ class DataFetcherSharedMemoryBase::PollingThread : public base::Thread { PollingThread(const char* name, DataFetcherSharedMemoryBase* fetcher); virtual ~PollingThread(); - void SetConsumers(int consumers_bitmask); + void AddConsumer(ConsumerType consumer_type); + void RemoveConsumer(ConsumerType consumer_type); + unsigned GetConsumersBitmask() const { return consumers_bitmask_; } private: @@ -45,13 +47,13 @@ DataFetcherSharedMemoryBase::PollingThread::PollingThread( DataFetcherSharedMemoryBase::PollingThread::~PollingThread() { } -void DataFetcherSharedMemoryBase::PollingThread::SetConsumers( - int consumers_bitmask) { - consumers_bitmask_ = consumers_bitmask; - if (!consumers_bitmask_) { - timer_.reset(); // will also stop the timer. +void DataFetcherSharedMemoryBase::PollingThread::AddConsumer( + ConsumerType consumer_type) { + DCHECK(fetcher_); + if (!fetcher_->Start(consumer_type)) return; - } + + consumers_bitmask_ |= consumer_type; if (!timer_) timer_.reset(new base::RepeatingTimer<PollingThread>()); @@ -61,6 +63,18 @@ void DataFetcherSharedMemoryBase::PollingThread::SetConsumers( 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_); @@ -90,23 +104,21 @@ bool DataFetcherSharedMemoryBase::StartFetchingDeviceData( if (started_consumers_ & consumer_type) return true; - if (!Start(consumer_type)) - return false; - - started_consumers_ |= consumer_type; - if (IsPolling()) { - if (!InitAndStartPollingThreadIfNecessary()) { - Stop(consumer_type); - started_consumers_ ^= consumer_type; + if (!InitAndStartPollingThreadIfNecessary()) return false; - } polling_thread_->message_loop()->PostTask( FROM_HERE, - base::Bind(&PollingThread::SetConsumers, + base::Bind(&PollingThread::AddConsumer, base::Unretained(polling_thread_.get()), - started_consumers_)); + consumer_type)); + } else { + if (!Start(consumer_type)) + return false; } + + started_consumers_ |= consumer_type; + return true; } @@ -115,18 +127,19 @@ bool DataFetcherSharedMemoryBase::StopFetchingDeviceData( if (!(started_consumers_ & consumer_type)) return true; - if (!Stop(consumer_type)) - return false; - - started_consumers_ ^= consumer_type; - if (IsPolling()) { polling_thread_->message_loop()->PostTask( FROM_HERE, - base::Bind(&PollingThread::SetConsumers, + base::Bind(&PollingThread::RemoveConsumer, base::Unretained(polling_thread_.get()), - started_consumers_)); + consumer_type)); + } else { + if (!Stop(consumer_type)) + return false; } + + started_consumers_ ^= consumer_type; + return true; } @@ -170,12 +183,13 @@ base::SharedMemory* DataFetcherSharedMemoryBase::InitSharedMemory( if (it != shared_memory_map_.end()) return it->second; - base::SharedMemory* new_shared_mem = new base::SharedMemory; + scoped_ptr<base::SharedMemory> new_shared_mem(new base::SharedMemory); if (new_shared_mem->CreateAndMapAnonymous(buffer_size)) { if (void* mem = new_shared_mem->memory()) { memset(mem, 0, buffer_size); - shared_memory_map_[consumer_type] = new_shared_mem; - return new_shared_mem; + base::SharedMemory* shared_mem = new_shared_mem.release(); + shared_memory_map_[consumer_type] = shared_mem; + return shared_mem; } } LOG(ERROR) << "Failed to initialize shared memory"; diff --git a/content/browser/device_orientation/data_fetcher_shared_memory_base.h b/content/browser/device_orientation/data_fetcher_shared_memory_base.h index a449596..3bd4103 100644 --- a/content/browser/device_orientation/data_fetcher_shared_memory_base.h +++ b/content/browser/device_orientation/data_fetcher_shared_memory_base.h @@ -59,8 +59,9 @@ class CONTENT_EXPORT DataFetcherSharedMemoryBase { // fetch the sensor data. virtual bool IsPolling() const; - // Start() method should call InitSharedMemoryBuffer() and cache the obtained - // pointer for efficienty and thread-safety. + // Start() method should call InitSharedMemoryBuffer() to get the shared + // memory pointer. If IsPolling() is true both Start() and Stop() methods + // are called from the |polling_thread_|. virtual bool Start(ConsumerType consumer_type) = 0; virtual bool Stop(ConsumerType consumer_type) = 0; diff --git a/content/browser/device_orientation/data_fetcher_shared_memory_base_unittest.cc b/content/browser/device_orientation/data_fetcher_shared_memory_base_unittest.cc index b580872..d9b8835 100644 --- a/content/browser/device_orientation/data_fetcher_shared_memory_base_unittest.cc +++ b/content/browser/device_orientation/data_fetcher_shared_memory_base_unittest.cc @@ -147,18 +147,20 @@ class FakePollingDataFetcher : public FakeDataFetcher { virtual ~FakePollingDataFetcher() { } virtual bool Start(ConsumerType consumer_type) OVERRIDE { + EXPECT_TRUE(base::MessageLoop::current() == GetPollingMessageLoop()); Init(consumer_type); start_.Signal(); return true; } virtual bool Stop(ConsumerType consumer_type) OVERRIDE { + EXPECT_TRUE(base::MessageLoop::current() == GetPollingMessageLoop()); stop_.Signal(); return true; } virtual void Fetch(unsigned consumer_bitmask) OVERRIDE { - DCHECK(base::MessageLoop::current() == GetPollingMessageLoop()); + EXPECT_TRUE(base::MessageLoop::current() == GetPollingMessageLoop()); if (consumer_bitmask & CONSUMER_TYPE_ORIENTATION) UpdateOrientation(); @@ -179,65 +181,61 @@ class FakePollingDataFetcher : public FakeDataFetcher { TEST(DataFetcherSharedMemoryBaseTest, DoesStartMotion) { - FakeNonPollingDataFetcher* mock_data_fetcher = - new FakeNonPollingDataFetcher(); - EXPECT_FALSE(mock_data_fetcher->IsPolling()); + FakeNonPollingDataFetcher mock_data_fetcher; + EXPECT_FALSE(mock_data_fetcher.IsPolling()); - EXPECT_TRUE(mock_data_fetcher->StartFetchingDeviceData(CONSUMER_TYPE_MOTION)); - mock_data_fetcher->WaitForStart(); + EXPECT_TRUE(mock_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_MOTION)); + mock_data_fetcher.WaitForStart(); EXPECT_EQ(kPeriodInMilliseconds, - mock_data_fetcher->GetMotionBuffer()->data.interval); + mock_data_fetcher.GetMotionBuffer()->data.interval); - mock_data_fetcher->StopFetchingDeviceData(CONSUMER_TYPE_MOTION); - mock_data_fetcher->WaitForStop(); + mock_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_MOTION); + mock_data_fetcher.WaitForStop(); } TEST(DataFetcherSharedMemoryBaseTest, DoesStartOrientation) { - FakeNonPollingDataFetcher* mock_data_fetcher = - new FakeNonPollingDataFetcher(); - EXPECT_FALSE(mock_data_fetcher->IsPolling()); + FakeNonPollingDataFetcher mock_data_fetcher; + EXPECT_FALSE(mock_data_fetcher.IsPolling()); - EXPECT_TRUE(mock_data_fetcher->StartFetchingDeviceData( + EXPECT_TRUE(mock_data_fetcher.StartFetchingDeviceData( CONSUMER_TYPE_ORIENTATION)); - mock_data_fetcher->WaitForStart(); + mock_data_fetcher.WaitForStart(); - EXPECT_EQ(1, mock_data_fetcher->GetOrientationBuffer()->data.alpha); + EXPECT_EQ(1, mock_data_fetcher.GetOrientationBuffer()->data.alpha); - mock_data_fetcher->StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION); - mock_data_fetcher->WaitForStop(); + mock_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION); + mock_data_fetcher.WaitForStop(); } TEST(DataFetcherSharedMemoryBaseTest, DoesPollMotion) { - FakePollingDataFetcher* mock_data_fetcher = - new FakePollingDataFetcher(); - EXPECT_TRUE(mock_data_fetcher->IsPolling()); + FakePollingDataFetcher mock_data_fetcher; + EXPECT_TRUE(mock_data_fetcher.IsPolling()); - EXPECT_TRUE(mock_data_fetcher->StartFetchingDeviceData(CONSUMER_TYPE_MOTION)); - mock_data_fetcher->WaitForStart(); - mock_data_fetcher->WaitForUpdateMotion(); + EXPECT_TRUE(mock_data_fetcher.StartFetchingDeviceData(CONSUMER_TYPE_MOTION)); + mock_data_fetcher.WaitForStart(); + mock_data_fetcher.WaitForUpdateMotion(); EXPECT_EQ(kPeriodInMilliseconds, - mock_data_fetcher->GetMotionBuffer()->data.interval); + mock_data_fetcher.GetMotionBuffer()->data.interval); - mock_data_fetcher->StopFetchingDeviceData(CONSUMER_TYPE_MOTION); - mock_data_fetcher->WaitForStop(); + mock_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_MOTION); + mock_data_fetcher.WaitForStop(); } TEST(DataFetcherSharedMemoryBaseTest, DoesPollOrientation) { - FakePollingDataFetcher* mock_data_fetcher = - new FakePollingDataFetcher(); - EXPECT_TRUE(mock_data_fetcher->IsPolling()); + FakePollingDataFetcher mock_data_fetcher; + EXPECT_TRUE(mock_data_fetcher.IsPolling()); - EXPECT_TRUE(mock_data_fetcher->StartFetchingDeviceData( + EXPECT_TRUE(mock_data_fetcher.StartFetchingDeviceData( CONSUMER_TYPE_ORIENTATION)); - mock_data_fetcher->WaitForStart(); - mock_data_fetcher->WaitForUpdateOrientation(); + mock_data_fetcher.WaitForStart(); + mock_data_fetcher.WaitForUpdateOrientation(); - EXPECT_EQ(1, mock_data_fetcher->GetOrientationBuffer()->data.alpha); + EXPECT_EQ(1, mock_data_fetcher.GetOrientationBuffer()->data.alpha); - mock_data_fetcher->StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION); - mock_data_fetcher->WaitForStop(); + mock_data_fetcher.StopFetchingDeviceData(CONSUMER_TYPE_ORIENTATION); + mock_data_fetcher.WaitForStop(); } } // namespace diff --git a/content/browser/device_orientation/data_fetcher_shared_memory_mac.cc b/content/browser/device_orientation/data_fetcher_shared_memory_mac.cc new file mode 100644 index 0000000..1d658f0 --- /dev/null +++ b/content/browser/device_orientation/data_fetcher_shared_memory_mac.cc @@ -0,0 +1,163 @@ +// Copyright 2013 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 "data_fetcher_shared_memory.h" + +#include "base/logging.h" +#include "third_party/sudden_motion_sensor/sudden_motion_sensor_mac.h" + +namespace { + +void FetchMotion(SuddenMotionSensor* sensor, + content::DeviceMotionHardwareBuffer* buffer) { + DCHECK(buffer); + + float axis_value[3]; + if (!sensor->ReadSensorValues(axis_value)) + return; + + buffer->seqlock.WriteBegin(); + buffer->data.accelerationIncludingGravityX = axis_value[0]; + buffer->data.hasAccelerationIncludingGravityX = true; + buffer->data.accelerationIncludingGravityY = axis_value[1]; + buffer->data.hasAccelerationIncludingGravityY = true; + buffer->data.accelerationIncludingGravityZ = axis_value[2]; + buffer->data.hasAccelerationIncludingGravityZ = true; + buffer->data.allAvailableSensorsAreActive = true; + buffer->seqlock.WriteEnd(); +} + +void FetchOrientation(SuddenMotionSensor* sensor, + content::DeviceOrientationHardwareBuffer* buffer) { + DCHECK(buffer); + + // Retrieve per-axis calibrated values. + float axis_value[3]; + if (!sensor->ReadSensorValues(axis_value)) + return; + + // Transform the accelerometer values to W3C draft angles. + // + // Accelerometer values are just dot products of the sensor axes + // by the gravity vector 'g' with the result for the z axis inverted. + // + // To understand this transformation calculate the 3rd row of the z-x-y + // Euler angles rotation matrix (because of the 'g' vector, only 3rd row + // affects to the result). Note that z-x-y matrix means R = Ry * Rx * Rz. + // Then, assume alpha = 0 and you get this: + // + // x_acc = sin(gamma) + // y_acc = - cos(gamma) * sin(beta) + // z_acc = cos(beta) * cos(gamma) + // + // After that the rest is just a bit of trigonometry. + // + // Also note that alpha can't be provided but it's assumed to be always zero. + // This is necessary in order to provide enough information to solve + // the equations. + // + const double kRad2deg = 180.0 / M_PI; + double beta = kRad2deg * atan2(-axis_value[1], axis_value[2]); + double gamma = kRad2deg * asin(axis_value[0]); + + // TODO(aousterh): should absolute_ be set to false here? + // See crbug.com/136010. + + // Make sure that the interval boundaries comply with the specification. At + // this point, beta is [-180, 180] and gamma is [-90, 90], but the spec has + // the upper bound open on both. + if (beta == 180.0) + beta = -180; // -180 == 180 (upside-down) + if (gamma == 90.0) + gamma = nextafter(90, 0); + + // At this point, DCHECKing is paranoia. Never hurts. + DCHECK_GE(beta, -180.0); + DCHECK_LT(beta, 180.0); + DCHECK_GE(gamma, -90.0); + DCHECK_LT(gamma, 90.0); + + buffer->seqlock.WriteBegin(); + buffer->data.beta = beta; + buffer->data.hasBeta = true; + buffer->data.gamma = gamma; + buffer->data.hasGamma = true; + buffer->data.allAvailableSensorsAreActive = true; + buffer->seqlock.WriteEnd(); +} + +} // namespace + +namespace content { + +DataFetcherSharedMemory::DataFetcherSharedMemory() { +} + +DataFetcherSharedMemory::~DataFetcherSharedMemory() { +} + +void DataFetcherSharedMemory::Fetch(unsigned consumer_bitmask) { + DCHECK(base::MessageLoop::current() == GetPollingMessageLoop()); + DCHECK(sudden_motion_sensor_); + + if (consumer_bitmask & CONSUMER_TYPE_ORIENTATION) + FetchOrientation(sudden_motion_sensor_.get(), orientation_buffer_); + else if (consumer_bitmask & CONSUMER_TYPE_MOTION) + FetchMotion(sudden_motion_sensor_.get(), motion_buffer_); + + NOTREACHED(); +} + +bool DataFetcherSharedMemory::IsPolling() const { + return true; +} + +bool DataFetcherSharedMemory::Start(ConsumerType consumer_type) { + DCHECK(base::MessageLoop::current() == GetPollingMessageLoop()); + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + if (void* buffer = InitSharedMemoryBuffer(consumer_type, + sizeof(DeviceMotionHardwareBuffer))) { + motion_buffer_ = static_cast<DeviceMotionHardwareBuffer*>(buffer); + if (!sudden_motion_sensor_) + sudden_motion_sensor_.reset(SuddenMotionSensor::Create()); + return true; + } + case CONSUMER_TYPE_ORIENTATION: + if (void* buffer = InitSharedMemoryBuffer(consumer_type, + sizeof(DeviceOrientationHardwareBuffer))) { + orientation_buffer_ = + static_cast<DeviceOrientationHardwareBuffer*>(buffer); + if (!sudden_motion_sensor_) + sudden_motion_sensor_.reset(SuddenMotionSensor::Create()); + return true; + } + default: + NOTREACHED(); + } + return false; +} + +bool DataFetcherSharedMemory::Stop(ConsumerType consumer_type) { + DCHECK(base::MessageLoop::current() == GetPollingMessageLoop()); + switch (consumer_type) { + case CONSUMER_TYPE_MOTION: + motion_buffer_->seqlock.WriteBegin(); + motion_buffer_->data.allAvailableSensorsAreActive = false; + motion_buffer_->seqlock.WriteEnd(); + motion_buffer_ = NULL; + return true; + case CONSUMER_TYPE_ORIENTATION: + orientation_buffer_->seqlock.WriteBegin(); + orientation_buffer_->data.allAvailableSensorsAreActive = false; + orientation_buffer_->seqlock.WriteEnd(); + orientation_buffer_ = NULL; + return true; + default: + NOTREACHED(); + } + return false; +} + +} // namespace content |