Index: ppapi/proxy/gamepad_resource.cc |
diff --git a/ppapi/proxy/gamepad_resource.cc b/ppapi/proxy/gamepad_resource.cc |
index 21ee02d9de9fa1cce6f3dc6b4ce99885691e4a7d..446c11c9f79a372d0e7ed8d878c7e2fa32224ab5 100644 |
--- a/ppapi/proxy/gamepad_resource.cc |
+++ b/ppapi/proxy/gamepad_resource.cc |
@@ -6,16 +6,47 @@ |
#include <string.h> |
-#include "ppapi/c/ppb_gamepad.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_)); |
+ |
SendCreateToBrowser(PpapiHostMsg_Gamepad_Create()); |
CallBrowser(PpapiHostMsg_Gamepad_RequestMemory()); |
} |
@@ -28,30 +59,56 @@ void GamepadResource::Sample(PP_GamepadsSampleData* data) { |
// Browser hasn't sent back our shared memory, give the plugin gamepad |
// data corresponding to "not connected". |
memset(data, 0, sizeof(PP_GamepadsSampleData)); |
- } else { |
- memcpy(data, buffer_, 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::OnReplyReceived(const ResourceMessageReplyParams& params, |
const IPC::Message& msg) { |
IPC_BEGIN_MESSAGE_MAP(GamepadResource, msg) |
- PPAPI_DISPATCH_RESOURCE_REPLY(PpapiPluginMsg_Gamepad_SendMemory, |
- OnPluginMsgSendMemory) |
+ PPAPI_DISPATCH_RESOURCE_REPLY_0(PpapiPluginMsg_Gamepad_SendMemory, |
+ OnPluginMsgSendMemory) |
IPC_END_MESSAGE_MAP() |
} |
void GamepadResource::OnPluginMsgSendMemory( |
- const ResourceMessageReplyParams& params, |
- base::SharedMemoryHandle shared_memory_handle) { |
- /* TODO(brettw) implement this when we have shared gamepad code. It would be |
- something like this: |
- shared_memory_.reset( |
- new base::SharedMemory(shared_memory_handle, true)); |
- CHECK(shared_memory_->Map(sizeof(GamepadHardwareBuffer))); |
- void *memory = shared_memory_->memory(); |
- // Use the memory... |
- */ |
+ const ResourceMessageReplyParams& params) { |
+ // On failure, the handle will be null and the CHECK below will be tripped. |
+ base::SharedMemoryHandle handle; |
+ params.GetSharedMemoryHandleAtIndex(0, &handle); |
+ |
+ shared_memory_.reset(new base::SharedMemory(handle, true)); |
+ CHECK(shared_memory_->Map(sizeof(ContentGamepadHardwareBuffer))); |
+ buffer_ = static_cast<const ContentGamepadHardwareBuffer*>( |
+ shared_memory_->memory()); |
} |
} // namespace proxy |