// 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 "ppapi/proxy/gamepad_resource.h" #include #include "base/bind.h" #include "base/threading/platform_thread.h" #include "ppapi/proxy/dispatch_reply_message.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/shared_impl/ppb_gamepad_shared.h" namespace ppapi { namespace proxy { namespace { // This is the read logic from content/common/gamepad_seqlock.h base::subtle::Atomic32 ReadBegin(const base::subtle::Atomic32* sequence) { base::subtle::Atomic32 version; for (;;) { version = base::subtle::NoBarrier_Load(sequence); // If the counter is even, then the associated data might be in a // consistent state, so we can try to read. if ((version & 1) == 0) break; // Otherwise, the writer is in the middle of an update. Retry the read. base::PlatformThread::YieldCurrentThread(); } return version; } bool ReadRetry(const base::subtle::Atomic32* sequence, base::subtle::Atomic32 version) { // If the sequence number was updated then a read should be re-attempted. // -- Load fence, read membarrier return base::subtle::Release_Load(sequence) != version; } } // namespace GamepadResource::GamepadResource(Connection connection, PP_Instance instance) : PluginResource(connection, instance), buffer_(NULL) { memset(&last_read_, 0, sizeof(last_read_)); SendCreate(BROWSER, PpapiHostMsg_Gamepad_Create()); Call( BROWSER, PpapiHostMsg_Gamepad_RequestMemory(), base::Bind(&GamepadResource::OnPluginMsgSendMemory, this)); } GamepadResource::~GamepadResource() { } thunk::PPB_Gamepad_API* GamepadResource::AsPPB_Gamepad_API() { return this; } void GamepadResource::Sample(PP_GamepadsSampleData* data) { if (!buffer_) { // Browser hasn't sent back our shared memory, give the plugin gamepad // data corresponding to "not connected". memset(data, 0, sizeof(PP_GamepadsSampleData)); return; } // ========== // DANGER // ========== // // This logic is duplicated in the renderer as well. If you change it, that // also needs to be in sync. See gamepad_shared_memory_reader.cc. // Only try to read this many times before failing to avoid waiting here // very long in case of contention with the writer. const int kMaximumContentionCount = 10; int contention_count = -1; base::subtle::Atomic32 version; WebKitGamepads read_into; do { version = ReadBegin(&buffer_->sequence); memcpy(&read_into, &buffer_->buffer, sizeof(read_into)); ++contention_count; if (contention_count == kMaximumContentionCount) break; } while (ReadRetry(&buffer_->sequence, version)); // In the event of a read failure, just leave the last read data as-is (the // hardware thread is taking unusally long). if (contention_count < kMaximumContentionCount) ConvertWebKitGamepadData(read_into, &last_read_); memcpy(data, &last_read_, sizeof(PP_GamepadsSampleData)); } void GamepadResource::OnPluginMsgSendMemory( const ResourceMessageReplyParams& params) { // On failure, the handle will be null and the CHECK below will be tripped. base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle(); params.TakeSharedMemoryHandleAtIndex(0, &handle); shared_memory_.reset(new base::SharedMemory(handle, true)); CHECK(shared_memory_->Map(sizeof(ContentGamepadHardwareBuffer))); buffer_ = static_cast( shared_memory_->memory()); } } // namespace proxy } // namespace ppapi