diff options
author | teravest@chromium.org <teravest@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-19 20:42:33 +0000 |
---|---|---|
committer | teravest@chromium.org <teravest@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-19 20:42:33 +0000 |
commit | 30e1cb75b8fa46b67cfc7faa146eacdca5e90241 (patch) | |
tree | 2b6b293673d938f49843aa6b76143ca7d5950315 | |
parent | ae5df4b22be0cfee27c09c6a1e4721eb0935eab2 (diff) | |
download | chromium_src-30e1cb75b8fa46b67cfc7faa146eacdca5e90241.zip chromium_src-30e1cb75b8fa46b67cfc7faa146eacdca5e90241.tar.gz chromium_src-30e1cb75b8fa46b67cfc7faa146eacdca5e90241.tar.bz2 |
Pepper: Use shared memory for ArrayBufferVar.
This change improves performance for sending ArrayBufferVars between the
plugin and the host. It copies the data from the var into a shared
memory region and sends the shared memory handle over IPC.
This required defining a new message for plugins to request the host to
create shared memory.
BUG=
Review URL: https://chromiumcodereview.appspot.com/11827059
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@189089 0039d316-1c4b-4281-b951-d872f2087c98
37 files changed, 858 insertions, 36 deletions
diff --git a/base/shared_memory_nacl.cc b/base/shared_memory_nacl.cc index 63e56e5..291527e 100644 --- a/base/shared_memory_nacl.cc +++ b/base/shared_memory_nacl.cc @@ -123,7 +123,6 @@ SharedMemoryHandle SharedMemory::handle() const { void SharedMemory::Close() { Unmap(); - if (mapped_file_ > 0) { if (close(mapped_file_) < 0) DPLOG(ERROR) << "close"; diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index 549985b..42b162f 100644 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -216,6 +216,8 @@ 'renderer/pepper/pepper_flash_renderer_host.h', 'renderer/pepper/pepper_helper.cc', 'renderer/pepper/pepper_helper.h', + 'renderer/pepper/pepper_shared_memory_message_filter.cc', + 'renderer/pepper/pepper_shared_memory_message_filter.h', 'renderer/pepper/ppb_nacl_private_impl.cc', 'renderer/pepper/ppb_nacl_private_impl.h', 'renderer/pepper/ppb_pdf_impl.cc', diff --git a/chrome/nacl/nacl_ipc_adapter.cc b/chrome/nacl/nacl_ipc_adapter.cc index a37756c..c615a05 100644 --- a/chrome/nacl/nacl_ipc_adapter.cc +++ b/chrome/nacl/nacl_ipc_adapter.cc @@ -15,9 +15,11 @@ #include "build/build_config.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_platform_file.h" +#include "native_client/src/trusted/desc/nacl_desc_base.h" #include "native_client/src/trusted/desc/nacl_desc_custom.h" #include "native_client/src/trusted/desc/nacl_desc_imc_shm.h" #include "native_client/src/trusted/desc/nacl_desc_sync_socket.h" +#include "native_client/src/trusted/desc/nacl_desc_wrapper.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/serialized_handle.h" @@ -460,6 +462,7 @@ int NaClIPCAdapter::LockedReceive(NaClImcTypedMsgHdr* msg) { bool NaClIPCAdapter::SendCompleteMessage(const char* buffer, size_t buffer_len) { + lock_.AssertAcquired(); // The message will have already been validated, so we know it's large enough // for our header. const NaClMessageHeader* header = diff --git a/chrome/nacl/nacl_ipc_adapter.h b/chrome/nacl/nacl_ipc_adapter.h index 032d98d..515b0fc 100644 --- a/chrome/nacl/nacl_ipc_adapter.h +++ b/chrome/nacl/nacl_ipc_adapter.h @@ -8,6 +8,7 @@ #include <map> #include <queue> #include <string> +#include <vector> #include "base/basictypes.h" #include "base/memory/ref_counted.h" diff --git a/chrome/renderer/pepper/pepper_helper.cc b/chrome/renderer/pepper/pepper_helper.cc index c15f893..c36bac0 100644 --- a/chrome/renderer/pepper/pepper_helper.cc +++ b/chrome/renderer/pepper/pepper_helper.cc @@ -5,6 +5,7 @@ #include "chrome/renderer/pepper/pepper_helper.h" #include "chrome/renderer/pepper/chrome_renderer_pepper_host_factory.h" +#include "chrome/renderer/pepper/pepper_shared_memory_message_filter.h" #include "content/public/renderer/renderer_ppapi_host.h" #include "ppapi/host/ppapi_host.h" @@ -23,6 +24,9 @@ void PepperHelper::DidCreatePepperPlugin(content::RendererPpapiHost* host) { host->GetPpapiHost()->AddHostFactoryFilter( scoped_ptr<ppapi::host::HostFactory>( new ChromeRendererPepperHostFactory(host))); + host->GetPpapiHost()->AddInstanceMessageFilter( + scoped_ptr<ppapi::host::InstanceMessageFilter>( + new PepperSharedMemoryMessageFilter(host))); } } // namespace chrome diff --git a/chrome/renderer/pepper/pepper_shared_memory_message_filter.cc b/chrome/renderer/pepper/pepper_shared_memory_message_filter.cc new file mode 100644 index 0000000..c568218 --- /dev/null +++ b/chrome/renderer/pepper/pepper_shared_memory_message_filter.cc @@ -0,0 +1,75 @@ +// Copyright (c) 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 "chrome/renderer/pepper/pepper_shared_memory_message_filter.h" + +#include "base/memory/scoped_ptr.h" +#include "base/process_util.h" +#include "base/shared_memory.h" +#include "content/public/common/content_client.h" +#include "content/public/renderer/render_thread.h" +#include "content/public/renderer/renderer_ppapi_host.h" +#include "ppapi/host/ppapi_host.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "webkit/plugins/ppapi/host_globals.h" + +namespace chrome { + +PepperSharedMemoryMessageFilter::PepperSharedMemoryMessageFilter( + content::RendererPpapiHost* host) + : InstanceMessageFilter(host->GetPpapiHost()), + host_(host) { +} + +PepperSharedMemoryMessageFilter::~PepperSharedMemoryMessageFilter() { +} + +bool PepperSharedMemoryMessageFilter::OnInstanceMessageReceived( + const IPC::Message& msg) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(PepperSharedMemoryMessageFilter, msg) + IPC_MESSAGE_HANDLER(PpapiHostMsg_SharedMemory_CreateSharedMemory, + OnHostMsgCreateSharedMemory) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +bool PepperSharedMemoryMessageFilter::Send(IPC::Message* msg) { + return host_->GetPpapiHost()->Send(msg); +} + +void PepperSharedMemoryMessageFilter::OnHostMsgCreateSharedMemory( + PP_Instance instance, + uint32_t size, + int* host_handle_id, + ppapi::proxy::SerializedHandle* plugin_handle) { + plugin_handle->set_null_shmem(); + *host_handle_id = -1; + scoped_ptr<base::SharedMemory> shm(content::RenderThread::Get()-> + HostAllocateSharedMemoryBuffer(size).Pass()); + if (!shm.get()) + return; + + base::SharedMemoryHandle host_shm_handle; + shm->ShareToProcess(base::GetCurrentProcessHandle(), &host_shm_handle); + *host_handle_id = content::GetHostGlobals()->GetVarTracker()-> + TrackSharedMemoryHandle(instance, host_shm_handle, size); + + base::PlatformFile host_handle = +#if defined(OS_WIN) + host_shm_handle; +#elif defined(OS_POSIX) + host_shm_handle.fd; +#else + #error Not implemented. +#endif + // We set auto_close to false since we need our file descriptor to + // actually be duplicated on linux. The shared memory destructor will + // close the original handle for us. + plugin_handle->set_shmem( + host_->ShareHandleWithRemote(host_handle, false), size); +} + +} // namespace chrome diff --git a/chrome/renderer/pepper/pepper_shared_memory_message_filter.h b/chrome/renderer/pepper/pepper_shared_memory_message_filter.h new file mode 100644 index 0000000..5ffcf0b --- /dev/null +++ b/chrome/renderer/pepper/pepper_shared_memory_message_filter.h @@ -0,0 +1,53 @@ +// Copyright (c) 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. + +#ifndef CHROME_RENDERER_PEPPER_PEPPER_SHARED_MEMORY_MESSAGE_FILTER_H_ +#define CHROME_RENDERER_PEPPER_PEPPER_SHARED_MEMORY_MESSAGE_FILTER_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/platform_file.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/host/instance_message_filter.h" + +namespace content { +class RendererPpapiHost; +} + +namespace ppapi { +namespace proxy { +class SerializedHandle; +} +} + +namespace chrome { + +// Implements the backend for shared memory messages from a plugin process. +class PepperSharedMemoryMessageFilter + : public ppapi::host::InstanceMessageFilter { + public: + explicit PepperSharedMemoryMessageFilter(content::RendererPpapiHost* host); + virtual ~PepperSharedMemoryMessageFilter(); + + // InstanceMessageFilter: + virtual bool OnInstanceMessageReceived(const IPC::Message& msg) OVERRIDE; + + bool Send(IPC::Message* msg); + + private: + // Message handlers. + void OnHostMsgCreateSharedMemory( + PP_Instance instance, + uint32_t size, + int* host_shm_handle_id, + ppapi::proxy::SerializedHandle* plugin_shm_handle); + + content::RendererPpapiHost* host_; + + DISALLOW_COPY_AND_ASSIGN(PepperSharedMemoryMessageFilter); +}; + +} // namespace chrome + +#endif // CHROME_RENDERER_PEPPER_PEPPER_SHARED_MEMORY_MESSAGE_FILTER_H_ diff --git a/content/renderer/pepper/pepper_plugin_delegate_impl.cc b/content/renderer/pepper/pepper_plugin_delegate_impl.cc index cb5497a..fdb5c9c 100644 --- a/content/renderer/pepper/pepper_plugin_delegate_impl.cc +++ b/content/renderer/pepper/pepper_plugin_delegate_impl.cc @@ -33,6 +33,7 @@ #include "content/public/common/context_menu_params.h" #include "content/public/common/media_stream_request.h" #include "content/public/common/referrer.h" +#include "content/public/common/sandbox_init.h" #include "content/public/renderer/content_renderer_client.h" #include "content/public/renderer/renderer_restrict_dispatch_group.h" #include "content/renderer/gamepad_shared_memory_reader.h" @@ -1608,4 +1609,14 @@ MouseLockDispatcher* PepperPluginDelegateImpl::GetMouseLockDispatcher( } } +IPC::PlatformFileForTransit PepperPluginDelegateImpl::ShareHandleWithRemote( + base::PlatformFile handle, + base::ProcessId target_process_id, + bool should_close_source) const { + return content::BrokerGetFileHandleForProcess( + handle, + target_process_id, + should_close_source); +} + } // namespace content diff --git a/content/renderer/pepper/pepper_plugin_delegate_impl.h b/content/renderer/pepper/pepper_plugin_delegate_impl.h index 5a66214..d8b2e17 100644 --- a/content/renderer/pepper/pepper_plugin_delegate_impl.h +++ b/content/renderer/pepper/pepper_plugin_delegate_impl.h @@ -387,6 +387,12 @@ class PepperPluginDelegateImpl MouseLockDispatcher* GetMouseLockDispatcher( webkit::ppapi::PluginInstance* instance); + // Share a given handle with the target process. + virtual IPC::PlatformFileForTransit ShareHandleWithRemote( + base::PlatformFile handle, + base::ProcessId target_process_id, + bool should_close_source) const OVERRIDE; + // Pointer to the RenderView that owns us. RenderViewImpl* render_view_; diff --git a/ppapi/host/instance_message_filter.cc b/ppapi/host/instance_message_filter.cc new file mode 100644 index 0000000..229235f --- /dev/null +++ b/ppapi/host/instance_message_filter.cc @@ -0,0 +1,19 @@ +// 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/host/instance_message_filter.h" + +#include "ppapi/host/ppapi_host.h" + +namespace ppapi { +namespace host { + +InstanceMessageFilter::InstanceMessageFilter(PpapiHost* host) : host_(host) { +} + +InstanceMessageFilter::~InstanceMessageFilter() { +} + +} // namespace host +} // namespace ppapi diff --git a/ppapi/host/instance_message_filter.h b/ppapi/host/instance_message_filter.h new file mode 100644 index 0000000..2b072e9 --- /dev/null +++ b/ppapi/host/instance_message_filter.h @@ -0,0 +1,41 @@ +// 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. + +#ifndef PPAPI_HOST_INSTANCE_MESSAGE_FILTER_H_ +#define PPAPI_HOST_INSTANCE_MESSAGE_FILTER_H_ + +#include "base/basictypes.h" +#include "ppapi/host/ppapi_host_export.h" + +namespace IPC { +class Message; +} + +namespace ppapi { +namespace host { + +class PpapiHost; + +class PPAPI_HOST_EXPORT InstanceMessageFilter { + public: + explicit InstanceMessageFilter(PpapiHost* host); + virtual ~InstanceMessageFilter(); + + // Processes an instance message from the plugin process. Returns true if the + // message was handled. On false, the PpapiHost will forward the message to + // the next filter. + virtual bool OnInstanceMessageReceived(const IPC::Message& msg) = 0; + + PpapiHost* host() { return host_; } + + private: + PpapiHost* host_; + + DISALLOW_COPY_AND_ASSIGN(InstanceMessageFilter); +}; + +} // namespace host +} // namespace ppapi + +#endif // PPAPI_HOST_INSTANCE_MESSAGE_FILTER_H_ diff --git a/ppapi/host/ppapi_host.cc b/ppapi/host/ppapi_host.cc index 6423f1e..87dfb11 100644 --- a/ppapi/host/ppapi_host.cc +++ b/ppapi/host/ppapi_host.cc @@ -8,6 +8,7 @@ #include "ppapi/c/pp_errors.h" #include "ppapi/host/host_factory.h" #include "ppapi/host/host_message_context.h" +#include "ppapi/host/instance_message_filter.h" #include "ppapi/host/resource_host.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/resource_message_params.h" @@ -33,8 +34,11 @@ PpapiHost::PpapiHost(IPC::Sender* sender, PpapiHost::~PpapiHost() { // Delete these explicitly before destruction since then the host is still - // technically alive in case one of the hosts accesses us from the + // technically alive in case one of the filters accesses us from the // destructor. + instance_message_filters_.clear(); + + // The resources may also want to use us in their destructors. resources_.clear(); pending_resource_hosts_.clear(); } @@ -59,6 +63,15 @@ bool PpapiHost::OnMessageReceived(const IPC::Message& msg) { IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() + if (!handled) { + for (size_t i = 0; i < instance_message_filters_.size(); i++) { + if (instance_message_filters_[i]->OnInstanceMessageReceived(msg)) { + handled = true; + break; + } + } + } + return handled; } @@ -94,6 +107,11 @@ void PpapiHost::AddHostFactoryFilter(scoped_ptr<HostFactory> filter) { host_factory_filters_.push_back(filter.release()); } +void PpapiHost::AddInstanceMessageFilter( + scoped_ptr<InstanceMessageFilter> filter) { + instance_message_filters_.push_back(filter.release()); +} + void PpapiHost::OnHostMsgResourceCall( const proxy::ResourceMessageCallParams& params, const IPC::Message& nested_msg) { diff --git a/ppapi/host/ppapi_host.h b/ppapi/host/ppapi_host.h index 5e44d1a..c661a9b 100644 --- a/ppapi/host/ppapi_host.h +++ b/ppapi/host/ppapi_host.h @@ -30,6 +30,7 @@ namespace host { class HostFactory; struct HostMessageContext; +class InstanceMessageFilter; struct ReplyMessageContext; class ResourceHost; @@ -69,6 +70,10 @@ class PPAPI_HOST_EXPORT PpapiHost : public IPC::Sender, public IPC::Listener { // ownership of the pointer. void AddHostFactoryFilter(scoped_ptr<HostFactory> filter); + // Adds the given message filter to the host. The PpapiHost will take + // ownership of the pointer. + void AddInstanceMessageFilter(scoped_ptr<InstanceMessageFilter> filter); + // Returns null if the resource doesn't exist. host::ResourceHost* GetResourceHost(PP_Resource resource) const; @@ -104,6 +109,12 @@ class PPAPI_HOST_EXPORT PpapiHost : public IPC::Sender, public IPC::Listener { // an ObserverList. ScopedVector<HostFactory> host_factory_filters_; + // Filters for instance messages. Note that since we don't support deleting + // these dynamically we don't need to worry about modifications during + // iteration. If we add that capability, this should be replaced with an + // ObserverList. + ScopedVector<InstanceMessageFilter> instance_message_filters_; + typedef std::map<PP_Resource, linked_ptr<ResourceHost> > ResourceMap; ResourceMap resources_; diff --git a/ppapi/ppapi_host.gypi b/ppapi/ppapi_host.gypi index a2afd58..4031c77 100644 --- a/ppapi/ppapi_host.gypi +++ b/ppapi/ppapi_host.gypi @@ -25,6 +25,8 @@ 'host/host_factory.h', 'host/host_message_context.cc', 'host/host_message_context.h', + 'host/instance_message_filter.cc', + 'host/instance_message_filter.h', 'host/message_filter_host.cc', 'host/message_filter_host.h', 'host/ppapi_host.cc', diff --git a/ppapi/ppapi_proxy.gypi b/ppapi/ppapi_proxy.gypi index 9978a42..838bbc6 100644 --- a/ppapi/ppapi_proxy.gypi +++ b/ppapi/ppapi_proxy.gypi @@ -88,6 +88,7 @@ 'proxy/plugin_var_tracker.h', 'proxy/ppapi_command_buffer_proxy.h', 'proxy/ppapi_command_buffer_proxy.cc', + 'proxy/ppapi_messages.h', 'proxy/ppapi_message_utils.h', 'proxy/ppb_audio_proxy.cc', 'proxy/ppb_audio_proxy.h', diff --git a/ppapi/proxy/handle_converter.cc b/ppapi/proxy/handle_converter.cc index a669062..ef4102c 100644 --- a/ppapi/proxy/handle_converter.cc +++ b/ppapi/proxy/handle_converter.cc @@ -4,11 +4,15 @@ #include "ppapi/proxy/handle_converter.h" +#include <vector> #include "ipc/ipc_message.h" #include "ipc/ipc_message_macros.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/resource_message_params.h" #include "ppapi/proxy/serialized_handle.h" +#include "ppapi/proxy/serialized_var.h" + +class NaClDescImcShm; namespace IPC { class Message; @@ -28,9 +32,9 @@ void WriteHandle(int handle_index, typedef std::vector<ppapi::proxy::SerializedHandle> Handles; -// We define overload for catching SerializedHandles, so that we can share -// them correctly to the untrusted side, and another for handling all other -// parameters. See ConvertHandlesImpl for how these get used. +// We define overloads for catching SerializedHandles so that we can share +// them correctly to the untrusted side. +// See ConvertHandlesImpl for how these get used. void ConvertHandlesInParam(const ppapi::proxy::SerializedHandle& handle, Handles* handles, IPC::Message* msg, @@ -40,6 +44,20 @@ void ConvertHandlesInParam(const ppapi::proxy::SerializedHandle& handle, WriteHandle((*handle_index)++, handle, msg); } +void ConvertHandlesInParam(const ppapi::proxy::SerializedVar& var, + Handles* handles, + IPC::Message* msg, + int* handle_index) { + ppapi::proxy::SerializedHandle *handle = var.GetPluginShmemHandle(); + if (handle) { + handles->push_back(*handle); + if (msg) { + var.WriteRawVarHeader(msg); + WriteHandle((*handle_index)++, *handle, msg); + } + } +} + // For PpapiMsg_ResourceReply and the reply to PpapiHostMsg_ResourceSyncCall, // the handles are carried inside the ResourceMessageReplyParams. // NOTE: We only translate handles from host->NaCl. The only kind of @@ -218,6 +236,7 @@ bool HandleConverter::ConvertNativeHandlesToPosix( switch (msg.type()) { CASE_FOR_MESSAGE(PpapiMsg_CreateNaClChannel) CASE_FOR_MESSAGE(PpapiMsg_PPBAudio_NotifyAudioStreamCreated) + CASE_FOR_MESSAGE(PpapiMsg_PPPMessaging_HandleMessage) CASE_FOR_MESSAGE(PpapiPluginMsg_ResourceReply) case IPC_REPLY_ID: { int id = IPC::SyncMessage::GetMessageId(msg); @@ -232,6 +251,7 @@ bool HandleConverter::ConvertNativeHandlesToPosix( CASE_FOR_REPLY(PpapiHostMsg_PPBGraphics3D_GetTransferBuffer) CASE_FOR_REPLY(PpapiHostMsg_PPBImageData_CreateNaCl) CASE_FOR_REPLY(PpapiHostMsg_ResourceSyncCall) + CASE_FOR_REPLY(PpapiHostMsg_SharedMemory_CreateSharedMemory) default: // Do nothing for messages we don't know. break; diff --git a/ppapi/proxy/plugin_array_buffer_var.cc b/ppapi/proxy/plugin_array_buffer_var.cc index c9d302f..e990ed8 100644 --- a/ppapi/proxy/plugin_array_buffer_var.cc +++ b/ppapi/proxy/plugin_array_buffer_var.cc @@ -8,28 +8,104 @@ #include <limits> +#include "base/process_util.h" +#include "base/shared_memory.h" +#include "ppapi/c/dev/ppb_buffer_dev.h" +#include "ppapi/proxy/plugin_dispatcher.h" +#include "ppapi/proxy/plugin_globals.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/proxy/serialized_structs.h" +#include "ppapi/shared_impl/host_resource.h" +#include "ppapi/shared_impl/resource.h" +#include "ppapi/thunk/enter.h" +#include "ppapi/thunk/ppb_buffer_api.h" + +using base::SharedMemory; +using base::SharedMemoryHandle; +using ppapi::proxy::PluginGlobals; +using ppapi::proxy::PluginResourceTracker; + namespace ppapi { PluginArrayBufferVar::PluginArrayBufferVar(uint32 size_in_bytes) - : buffer_(size_in_bytes) { + : buffer_(size_in_bytes), + plugin_handle_(base::SharedMemory::NULLHandle()), + size_in_bytes_(size_in_bytes) { +} + +PluginArrayBufferVar::PluginArrayBufferVar(uint32 size_in_bytes, + SharedMemoryHandle plugin_handle) + : plugin_handle_(plugin_handle), + size_in_bytes_(size_in_bytes) { } PluginArrayBufferVar::~PluginArrayBufferVar() { + Unmap(); + + if (shmem_.get() == NULL) { + // The SharedMemory destuctor can't close the handle for us. + if (SharedMemory::IsHandleValid(plugin_handle_)) + SharedMemory::CloseHandle(plugin_handle_); + } else { + // Delete SharedMemory, if we have one. + shmem_.reset(); + } } void* PluginArrayBufferVar::Map() { + if (shmem_.get()) + return shmem_->memory(); + if (SharedMemory::IsHandleValid(plugin_handle_)) { + shmem_.reset(new SharedMemory(plugin_handle_, false)); + if (!shmem_->Map(size_in_bytes_)) { + shmem_.reset(); + return NULL; + } + return shmem_->memory(); + } if (buffer_.empty()) return NULL; return &(buffer_[0]); } void PluginArrayBufferVar::Unmap() { - // We don't actually use shared memory yet, so do nothing. + if (shmem_.get()) + shmem_->Unmap(); } uint32 PluginArrayBufferVar::ByteLength() { - return static_cast<uint32>(buffer_.size()); + return size_in_bytes_; } -} // namespace ppapi +bool PluginArrayBufferVar::CopyToNewShmem( + PP_Instance instance, + int* host_handle_id, + SharedMemoryHandle* plugin_out_handle) { + ppapi::proxy::PluginDispatcher* dispatcher = + ppapi::proxy::PluginDispatcher::GetForInstance(instance); + if (!dispatcher) + return false; + ppapi::proxy::SerializedHandle plugin_handle; + dispatcher->Send(new PpapiHostMsg_SharedMemory_CreateSharedMemory( + instance, ByteLength(), host_handle_id, &plugin_handle)); + if (!plugin_handle.IsHandleValid() || !plugin_handle.is_shmem() || + *host_handle_id == -1) + return false; + + base::SharedMemoryHandle tmp_handle = plugin_handle.shmem(); + SharedMemory s(tmp_handle, false); + if (!s.Map(ByteLength())) + return false; + memcpy(s.memory(), Map(), ByteLength()); + s.Unmap(); + + // We don't need to keep the shared memory around on the plugin side; + // we've already copied all our data into it. We'll make it invalid + // just to be safe. + *plugin_out_handle = base::SharedMemory::NULLHandle(); + + return true; +} + +} // namespace ppapi diff --git a/ppapi/proxy/plugin_array_buffer_var.h b/ppapi/proxy/plugin_array_buffer_var.h index 2eed216..bcafe8c 100644 --- a/ppapi/proxy/plugin_array_buffer_var.h +++ b/ppapi/proxy/plugin_array_buffer_var.h @@ -8,6 +8,9 @@ #include <vector> #include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "base/shared_memory.h" +#include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_stdint.h" #include "ppapi/shared_impl/var.h" @@ -18,17 +21,28 @@ namespace ppapi { class PluginArrayBufferVar : public ArrayBufferVar { public: explicit PluginArrayBufferVar(uint32 size_in_bytes); + PluginArrayBufferVar(uint32 size_in_bytes, + base::SharedMemoryHandle plugin_handle); virtual ~PluginArrayBufferVar(); // ArrayBufferVar implementation. virtual void* Map() OVERRIDE; virtual void Unmap() OVERRIDE; virtual uint32 ByteLength() OVERRIDE; + virtual bool CopyToNewShmem( + PP_Instance instance, + int* host_handle, + base::SharedMemoryHandle* plugin_handle) OVERRIDE; private: - // TODO(dmichael): Use shared memory for this. + // Non-shared memory std::vector<uint8> buffer_; + // Shared memory + base::SharedMemoryHandle plugin_handle_; + scoped_ptr<base::SharedMemory> shmem_; + uint32 size_in_bytes_; + DISALLOW_COPY_AND_ASSIGN(PluginArrayBufferVar); }; diff --git a/ppapi/proxy/plugin_var_tracker.cc b/ppapi/proxy/plugin_var_tracker.cc index 384eb3ec..cff8fa1 100644 --- a/ppapi/proxy/plugin_var_tracker.cc +++ b/ppapi/proxy/plugin_var_tracker.cc @@ -194,6 +194,12 @@ ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) { return new PluginArrayBufferVar(size_in_bytes); } +ArrayBufferVar* PluginVarTracker::CreateShmArrayBuffer( + uint32 size_in_bytes, + base::SharedMemoryHandle handle) { + return new PluginArrayBufferVar(size_in_bytes, handle); +} + void PluginVarTracker::PluginImplementedObjectCreated( PP_Instance instance, const PP_Var& created_var, @@ -369,5 +375,21 @@ scoped_refptr<ProxyObjectVar> PluginVarTracker::FindOrMakePluginVarFromHostVar( return scoped_refptr<ProxyObjectVar>(ret->second.var->AsProxyObjectVar()); } +int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance, + base::SharedMemoryHandle handle, + uint32 size_in_bytes) { + NOTREACHED(); + return -1; +} + +bool PluginVarTracker::StopTrackingSharedMemoryHandle( + int id, + PP_Instance instance, + base::SharedMemoryHandle* handle, + uint32* size_in_bytes) { + NOTREACHED(); + return false; +} + } // namesace proxy } // namespace ppapi diff --git a/ppapi/proxy/plugin_var_tracker.h b/ppapi/proxy/plugin_var_tracker.h index 3e287be..a8b229397 100644 --- a/ppapi/proxy/plugin_var_tracker.h +++ b/ppapi/proxy/plugin_var_tracker.h @@ -11,6 +11,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" +#include "base/shared_memory.h" #include "ppapi/c/pp_stdint.h" #include "ppapi/c/pp_var.h" #include "ppapi/proxy/ppapi_proxy_export.h" @@ -59,6 +60,13 @@ class PPAPI_PROXY_EXPORT PluginVarTracker : public VarTracker { // VarTracker public overrides. void DidDeleteInstance(PP_Instance instance) OVERRIDE; + virtual int TrackSharedMemoryHandle(PP_Instance instance, + base::SharedMemoryHandle file, + uint32 size_in_bytes) OVERRIDE; + virtual bool StopTrackingSharedMemoryHandle(int id, + PP_Instance instance, + base::SharedMemoryHandle* handle, + uint32* size_in_bytes) OVERRIDE; // Notification that a plugin-implemented object (PPP_Class) was created by // the plugin or deallocated by WebKit over IPC. @@ -86,6 +94,9 @@ class PPAPI_PROXY_EXPORT PluginVarTracker : public VarTracker { virtual void ObjectGettingZeroRef(VarMap::iterator iter) OVERRIDE; virtual bool DeleteObjectInfoIfNecessary(VarMap::iterator iter) OVERRIDE; virtual ArrayBufferVar* CreateArrayBuffer(uint32 size_in_bytes) OVERRIDE; + virtual ArrayBufferVar* CreateShmArrayBuffer( + uint32 size_in_bytes, + base::SharedMemoryHandle handle) OVERRIDE; private: friend struct DefaultSingletonTraits<PluginVarTracker>; diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h index 9ccd930..6ae94ab 100644 --- a/ppapi/proxy/ppapi_messages.h +++ b/ppapi/proxy/ppapi_messages.h @@ -1455,6 +1455,17 @@ IPC_MESSAGE_CONTROL0(PpapiHostMsg_Printing_GetDefaultPrintSettings) IPC_MESSAGE_CONTROL1(PpapiPluginMsg_Printing_GetDefaultPrintSettingsReply, PP_PrintSettings_Dev /* print_settings */) +// Shared memory --------------------------------------------------------------- +// Creates shared memory on the host side, returning a handle to the shared +// memory on the plugin and keeping the memory mapped in on the host. +// We return a "host handle_id" that can be mapped back to the +// handle on the host side by PpapiGlobals::UntrackSharedMemoryHandle(). +IPC_SYNC_MESSAGE_CONTROL2_2(PpapiHostMsg_SharedMemory_CreateSharedMemory, + PP_Instance /* instance */, + uint32_t /* size */, + int /* host_handle_id */, + ppapi::proxy::SerializedHandle /* plugin_handle */) + // WebSocket ------------------------------------------------------------------ IPC_MESSAGE_CONTROL0(PpapiHostMsg_WebSocket_Create) diff --git a/ppapi/proxy/ppb_instance_proxy.cc b/ppapi/proxy/ppb_instance_proxy.cc index 58b1593..bc51675 100644 --- a/ppapi/proxy/ppb_instance_proxy.cc +++ b/ppapi/proxy/ppb_instance_proxy.cc @@ -680,7 +680,8 @@ void PPB_Instance_Proxy::PostMessage(PP_Instance instance, PP_Var message) { dispatcher()->Send(new PpapiHostMsg_PPBInstance_PostMessage( API_ID_PPB_INSTANCE, - instance, SerializedVarSendInput(dispatcher(), message))); + instance, SerializedVarSendInputShmem(dispatcher(), message, + instance))); } PP_Bool PPB_Instance_Proxy::SetCursor(PP_Instance instance, @@ -912,7 +913,9 @@ void PPB_Instance_Proxy::OnHostMsgPostMessage( SerializedVarReceiveInput message) { EnterInstanceNoLock enter(instance); if (enter.succeeded()) - enter.functions()->PostMessage(instance, message.Get(dispatcher())); + enter.functions()->PostMessage(instance, + message.GetForInstance(dispatcher(), + instance)); } void PPB_Instance_Proxy::OnHostMsgLockMouse(PP_Instance instance) { diff --git a/ppapi/proxy/ppp_messaging_proxy.cc b/ppapi/proxy/ppp_messaging_proxy.cc index 690e874..5a12419 100644 --- a/ppapi/proxy/ppp_messaging_proxy.cc +++ b/ppapi/proxy/ppp_messaging_proxy.cc @@ -34,7 +34,7 @@ void HandleMessage(PP_Instance instance, PP_Var message_data) { dispatcher->Send(new PpapiMsg_PPPMessaging_HandleMessage( API_ID_PPP_MESSAGING, instance, - SerializedVarSendInput(dispatcher, message_data))); + SerializedVarSendInputShmem(dispatcher, message_data, instance))); } static const PPP_Messaging messaging_interface = { @@ -90,7 +90,7 @@ bool PPP_Messaging_Proxy::OnMessageReceived(const IPC::Message& msg) { void PPP_Messaging_Proxy::OnMsgHandleMessage( PP_Instance instance, SerializedVarReceiveInput message_data) { - PP_Var received_var(message_data.Get(dispatcher())); + PP_Var received_var(message_data.GetForInstance(dispatcher(), instance)); // SerializedVarReceiveInput will decrement the reference count, but we want // to give the recipient a reference. PpapiGlobals::Get()->GetVarTracker()->AddRefVar(received_var); diff --git a/ppapi/proxy/serialized_var.cc b/ppapi/proxy/serialized_var.cc index 5352e8f..698d920 100644 --- a/ppapi/proxy/serialized_var.cc +++ b/ppapi/proxy/serialized_var.cc @@ -6,20 +6,29 @@ #include "base/logging.h" #include "ipc/ipc_message_utils.h" +#include "ppapi/c/pp_instance.h" #include "ppapi/proxy/dispatcher.h" #include "ppapi/proxy/interface_proxy.h" #include "ppapi/proxy/ppapi_param_traits.h" +#include "ppapi/proxy/ppb_buffer_proxy.h" #include "ppapi/shared_impl/ppapi_globals.h" #include "ppapi/shared_impl/var.h" +#include "ppapi/thunk/enter.h" namespace ppapi { namespace proxy { +// When sending array buffers, if the size is over 256K, we use shared +// memory instead of sending the data over IPC. Light testing suggests +// shared memory is much faster for 256K and larger messages. +static const uint32 kMinimumArrayBufferSizeForShmem = 256 * 1024; + // SerializedVar::Inner -------------------------------------------------------- SerializedVar::Inner::Inner() : serialization_rules_(NULL), var_(PP_MakeUndefined()), + instance_(0), cleanup_mode_(CLEANUP_NONE) { #ifndef NDEBUG has_been_serialized_ = false; @@ -30,6 +39,7 @@ SerializedVar::Inner::Inner() SerializedVar::Inner::Inner(VarSerializationRules* serialization_rules) : serialization_rules_(serialization_rules), var_(PP_MakeUndefined()), + instance_(0), cleanup_mode_(CLEANUP_NONE) { #ifndef NDEBUG has_been_serialized_ = false; @@ -65,11 +75,30 @@ void SerializedVar::Inner::SetVar(PP_Var var) { raw_var_data_.reset(NULL); } +void SerializedVar::Inner::SetInstance(PP_Instance instance) { + instance_ = instance; +} + void SerializedVar::Inner::ForceSetVarValueForTest(PP_Var value) { var_ = value; raw_var_data_.reset(NULL); } +void SerializedVar::Inner::WriteRawVarHeader(IPC::Message* m) const { + // Write raw_var_data_ when we're called from + // chrome/nacl/nacl_ipc_adapter.cc. + DCHECK(raw_var_data_.get()); + DCHECK_EQ(PP_VARTYPE_ARRAY_BUFFER, raw_var_data_->type); + DCHECK(raw_var_data_->shmem_size != 0); + + // The serialization for this message MUST MATCH the implementation at + // SerializedVar::Inner::WriteToMessage for ARRAY_BUFFER_SHMEM_PLUGIN. + m->WriteInt(static_cast<int>(raw_var_data_->type)); + m->WriteInt(ARRAY_BUFFER_SHMEM_PLUGIN); + m->WriteInt(raw_var_data_->shmem_size); + // NaClIPCAdapter will write the handles for us. +} + void SerializedVar::Inner::WriteToMessage(IPC::Message* m) const { // When writing to the IPC messages, a serialization rules handler should // always have been set. @@ -124,14 +153,45 @@ void SerializedVar::Inner::WriteToMessage(IPC::Message* m) const { // handle the invalid string as if it was in process rather than seeing // what looks like a valid empty ArraryBuffer. ArrayBufferVar* buffer_var = ArrayBufferVar::FromPPVar(var_); - if (buffer_var) { - // TODO(dmichael): If it wasn't already Mapped, Unmap it. (Though once - // we use shared memory, this will probably be - // completely different anyway). - m->WriteData(static_cast<const char*>(buffer_var->Map()), - buffer_var->ByteLength()); - } else { - m->WriteData(NULL, 0); + bool using_shmem = false; + if (buffer_var && + buffer_var->ByteLength() >= kMinimumArrayBufferSizeForShmem && + instance_ != 0) { + int host_shm_handle_id; + base::SharedMemoryHandle plugin_shm_handle; + using_shmem = buffer_var->CopyToNewShmem(instance_, + &host_shm_handle_id, + &plugin_shm_handle); + if (using_shmem) { + // The serialization for this message MUST MATCH the implementation + // at SerializedVar::Inner::WriteRawVarHeader for + // ARRAY_BUFFER_SHMEM_PLUGIN. + if (host_shm_handle_id != -1) { + DCHECK(!base::SharedMemory::IsHandleValid(plugin_shm_handle)); + DCHECK(PpapiGlobals::Get()->IsPluginGlobals()); + m->WriteInt(ARRAY_BUFFER_SHMEM_HOST); + m->WriteInt(host_shm_handle_id); + } else { + DCHECK(base::SharedMemory::IsHandleValid(plugin_shm_handle)); + DCHECK(PpapiGlobals::Get()->IsHostGlobals()); + m->WriteInt(ARRAY_BUFFER_SHMEM_PLUGIN); + m->WriteInt(buffer_var->ByteLength()); + SerializedHandle handle(plugin_shm_handle, + buffer_var->ByteLength()); + IPC::ParamTraits<SerializedHandle>::Write(m, handle); + } + } + } + if (!using_shmem) { + if (buffer_var) { + m->WriteInt(ARRAY_BUFFER_NO_SHMEM); + m->WriteData(static_cast<const char*>(buffer_var->Map()), + buffer_var->ByteLength()); + } else { + // TODO(teravest): Introduce an ARRAY_BUFFER_EMPTY message type. + m->WriteBool(ARRAY_BUFFER_NO_SHMEM); + m->WriteData(NULL, 0); + } } break; } @@ -159,7 +219,6 @@ bool SerializedVar::Inner::ReadFromMessage(const IPC::Message* m, DCHECK(!has_been_deserialized_); has_been_deserialized_ = true; #endif - // When reading, the dispatcher should be set when we get a Deserialize // call (which will supply a dispatcher). int type; @@ -197,11 +256,40 @@ bool SerializedVar::Inner::ReadFromMessage(const IPC::Message* m, case PP_VARTYPE_ARRAY_BUFFER: { int length = 0; const char* message_bytes = NULL; - success = m->ReadData(iter, &message_bytes, &length); + int shmem_type; + success = m->ReadInt(iter, &shmem_type); if (success) { - raw_var_data_.reset(new RawVarData); - raw_var_data_->type = PP_VARTYPE_ARRAY_BUFFER; - raw_var_data_->data.assign(message_bytes, length); + if (shmem_type == ARRAY_BUFFER_NO_SHMEM) { + success = m->ReadData(iter, &message_bytes, &length); + if (success) { + raw_var_data_.reset(new RawVarData); + raw_var_data_->type = PP_VARTYPE_ARRAY_BUFFER; + raw_var_data_->shmem_type = static_cast<ShmemType>(shmem_type); + raw_var_data_->shmem_size = 0; + raw_var_data_->data.assign(message_bytes, length); + } + } else if (shmem_type == ARRAY_BUFFER_SHMEM_HOST) { + int host_handle_id; + success = m->ReadInt(iter, &host_handle_id); + if (success) { + raw_var_data_.reset(new RawVarData); + raw_var_data_->type = PP_VARTYPE_ARRAY_BUFFER; + raw_var_data_->shmem_type = static_cast<ShmemType>(shmem_type); + raw_var_data_->host_handle_id = host_handle_id; + } + } else if (shmem_type == ARRAY_BUFFER_SHMEM_PLUGIN) { + SerializedHandle plugin_handle; + success = m->ReadInt(iter, &length); + success &= IPC::ParamTraits<SerializedHandle>::Read( + m, iter, &plugin_handle); + if (success) { + raw_var_data_.reset(new RawVarData); + raw_var_data_->type = PP_VARTYPE_ARRAY_BUFFER; + raw_var_data_->shmem_type = static_cast<ShmemType>(shmem_type); + raw_var_data_->shmem_size = length; + raw_var_data_->plugin_handle = plugin_handle; + } + } } break; } @@ -237,6 +325,9 @@ void SerializedVar::Inner::SetCleanupModeToEndReceiveCallerOwned() { } void SerializedVar::Inner::ConvertRawVarData() { +#if defined(NACL_WIN64) + NOTREACHED(); +#else if (!raw_var_data_.get()) return; @@ -248,15 +339,49 @@ void SerializedVar::Inner::ConvertRawVarData() { break; } case PP_VARTYPE_ARRAY_BUFFER: { - var_ = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( - static_cast<uint32>(raw_var_data_->data.size()), - raw_var_data_->data.data()); + if (raw_var_data_->shmem_type == ARRAY_BUFFER_SHMEM_HOST) { + base::SharedMemoryHandle host_handle; + uint32 size_in_bytes; + bool ok = + PpapiGlobals::Get()->GetVarTracker()-> + StopTrackingSharedMemoryHandle(raw_var_data_->host_handle_id, + instance_, + &host_handle, + &size_in_bytes); + if (ok) { + var_ = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( + size_in_bytes, host_handle); + } else { + LOG(ERROR) << "Couldn't find array buffer id: " + << raw_var_data_->host_handle_id; + var_ = PP_MakeUndefined(); + } + } else if (raw_var_data_->shmem_type == ARRAY_BUFFER_SHMEM_PLUGIN) { + var_ = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( + raw_var_data_->shmem_size, + raw_var_data_->plugin_handle.shmem()); + } else { + var_ = PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( + static_cast<uint32>(raw_var_data_->data.size()), + raw_var_data_->data.data()); + } break; } default: NOTREACHED(); } raw_var_data_.reset(NULL); +#endif +} + +SerializedHandle* SerializedVar::Inner::GetPluginShmemHandle() const { + if (raw_var_data_.get()) { + if (raw_var_data_->type == PP_VARTYPE_ARRAY_BUFFER) { + if (raw_var_data_->shmem_size != 0) + return &raw_var_data_->plugin_handle; + } + } + return NULL; } // SerializedVar --------------------------------------------------------------- @@ -289,6 +414,17 @@ void SerializedVarSendInput::ConvertVector(Dispatcher* dispatcher, output->push_back(SerializedVarSendInput(dispatcher, input[i])); } +// SerializedVarSendInputShmem ------------------------------------------------- + +SerializedVarSendInputShmem::SerializedVarSendInputShmem( + Dispatcher* dispatcher, + const PP_Var& var, + const PP_Instance& instance) + : SerializedVar(dispatcher->serialization_rules()) { + inner_->SetVar(dispatcher->serialization_rules()->SendCallerOwned(var)); + inner_->SetInstance(instance); +} + // ReceiveSerializedVarReturnValue --------------------------------------------- ReceiveSerializedVarReturnValue::ReceiveSerializedVarReturnValue() { @@ -394,6 +530,13 @@ PP_Var SerializedVarReceiveInput::Get(Dispatcher* dispatcher) { return serialized_.inner_->GetVar(); } + +PP_Var SerializedVarReceiveInput::GetForInstance(Dispatcher* dispatcher, + PP_Instance instance) { + serialized_.inner_->SetInstance(instance); + return Get(dispatcher); +} + // SerializedVarVectorReceiveInput --------------------------------------------- SerializedVarVectorReceiveInput::SerializedVarVectorReceiveInput( diff --git a/ppapi/proxy/serialized_var.h b/ppapi/proxy/serialized_var.h index 3b74a3e..f283590 100644 --- a/ppapi/proxy/serialized_var.h +++ b/ppapi/proxy/serialized_var.h @@ -11,8 +11,12 @@ #include "base/basictypes.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/shared_memory.h" +#include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_var.h" #include "ppapi/proxy/ppapi_proxy_export.h" +#include "ppapi/proxy/serialized_handle.h" +#include "ppapi/proxy/serialized_structs.h" #include "ppapi/proxy/var_serialization_rules.h" class PickleIterator; @@ -79,11 +83,20 @@ class PPAPI_PROXY_EXPORT SerializedVar { return inner_->ReadFromMessage(m, iter); } + // Used by chrome/nacl/nacl_ipc_adapter.cc + SerializedHandle* GetPluginShmemHandle() const { + return inner_->GetPluginShmemHandle(); + } + void WriteRawVarHeader(IPC::Message* m) const { + inner_->WriteRawVarHeader(m); + } + protected: friend class SerializedVarReceiveInput; friend class SerializedVarReturnValue; friend class SerializedVarOutParam; friend class SerializedVarSendInput; + friend class SerializedVarSendInputShmem; friend class SerializedVarTestConstructor; friend class SerializedVarVectorReceiveInput; @@ -103,6 +116,7 @@ class PPAPI_PROXY_EXPORT SerializedVar { // See outer class's declarations above. PP_Var GetVar(); void SetVar(PP_Var var); + void SetInstance(PP_Instance instance); // For the SerializedVarTestConstructor, this writes the Var value as if // it was just received off the wire, without any serialization rules. @@ -115,6 +129,12 @@ class PPAPI_PROXY_EXPORT SerializedVar { void SetCleanupModeToEndSendPassRef(); void SetCleanupModeToEndReceiveCallerOwned(); + // Returns a handle in the underlying data, if it exists. + SerializedHandle* GetPluginShmemHandle() const; + + // Writes raw var data, excluding handles. + void WriteRawVarHeader(IPC::Message* m) const; + private: enum CleanupMode { // The serialized var won't do anything special in the destructor @@ -128,6 +148,13 @@ class PPAPI_PROXY_EXPORT SerializedVar { END_RECEIVE_CALLER_OWNED }; + // Enum for array buffer message types. + enum ShmemType { + ARRAY_BUFFER_NO_SHMEM, + ARRAY_BUFFER_SHMEM_HOST, + ARRAY_BUFFER_SHMEM_PLUGIN, + }; + // ReadFromMessage() may be called on the I/O thread, e.g., when reading the // reply to a sync message. We cannot use the var tracker on the I/O thread, // which means we cannot create PP_Var for PP_VARTYPE_STRING and @@ -136,7 +163,11 @@ class PPAPI_PROXY_EXPORT SerializedVar { // the main thread. struct RawVarData { PP_VarType type; + ShmemType shmem_type; std::string data; + uint32 shmem_size; + int host_handle_id; + SerializedHandle plugin_handle; }; // Converts |raw_var_data_| to |var_|. It is a no-op if |raw_var_data_| is @@ -158,6 +189,8 @@ class PPAPI_PROXY_EXPORT SerializedVar { // a string ID. Before this, the as_id will be 0 for VARTYPE_STRING. PP_Var var_; + PP_Instance instance_; + CleanupMode cleanup_mode_; #ifndef NDEBUG @@ -208,6 +241,19 @@ class PPAPI_PROXY_EXPORT SerializedVarSendInput : public SerializedVar { SerializedVarSendInput(); }; +// Specialization for optionally sending over shared memory. +class PPAPI_PROXY_EXPORT SerializedVarSendInputShmem : public SerializedVar { + public: + SerializedVarSendInputShmem(Dispatcher* dispatcher, const PP_Var& var, + const PP_Instance& instance); + + private: + // Disallow the empty constructor, but keep the default copy constructor + // which is required to send the object to the IPC system. + SerializedVarSendInputShmem(); +}; + + // For the calling side of a function returning a var. The sending side uses // SerializedVarReturnValue. // @@ -324,6 +370,7 @@ class PPAPI_PROXY_EXPORT SerializedVarReceiveInput { ~SerializedVarReceiveInput(); PP_Var Get(Dispatcher* dispatcher); + PP_Var GetForInstance(Dispatcher* dispatcher, PP_Instance instance); private: const SerializedVar& serialized_; diff --git a/ppapi/shared_impl/test_globals.h b/ppapi/shared_impl/test_globals.h index 68a997a..5579b09 100644 --- a/ppapi/shared_impl/test_globals.h +++ b/ppapi/shared_impl/test_globals.h @@ -6,6 +6,7 @@ #define PPAPI_SHARED_IMPL_TEST_GLOBALS_H_ #include "base/compiler_specific.h" +#include "base/shared_memory.h" #include "ppapi/shared_impl/callback_tracker.h" #include "ppapi/shared_impl/ppapi_globals.h" #include "ppapi/shared_impl/resource_tracker.h" @@ -20,8 +21,24 @@ class TestVarTracker : public VarTracker { virtual ArrayBufferVar* CreateArrayBuffer(uint32 size_in_bytes) OVERRIDE { return NULL; } + virtual ArrayBufferVar* CreateShmArrayBuffer( + uint32 size_in_bytes, + base::SharedMemoryHandle handle) OVERRIDE { + return NULL; + } virtual void DidDeleteInstance(PP_Instance instance) OVERRIDE { } + virtual int TrackSharedMemoryHandle(PP_Instance instance, + base::SharedMemoryHandle handle, + uint32 size_in_bytes) OVERRIDE { + return -1; + } + virtual bool StopTrackingSharedMemoryHandle(int id, + PP_Instance instance, + base::SharedMemoryHandle* handle, + uint32* size_in_bytes) OVERRIDE { + return false; + } }; // Implementation of PpapiGlobals for tests that don't need either the host- or diff --git a/ppapi/shared_impl/var.h b/ppapi/shared_impl/var.h index d5e33d5..50c13b9 100644 --- a/ppapi/shared_impl/var.h +++ b/ppapi/shared_impl/var.h @@ -9,7 +9,10 @@ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" +#include "base/platform_file.h" +#include "base/shared_memory.h" #include "ppapi/c/pp_var.h" +#include "ppapi/shared_impl/host_resource.h" #include "ppapi/shared_impl/ppapi_shared_export.h" namespace ppapi { @@ -160,6 +163,18 @@ class PPAPI_SHARED_EXPORT ArrayBufferVar : public Var { virtual void Unmap() = 0; virtual uint32 ByteLength() = 0; + // Creates a new shared memory region, and copies the data in the + // ArrayBufferVar into it. On the plugin side, host_shm_handle_id will be set + // to some value that is not -1. On the host side, plugin_shm_handle will be + // set to a valid SharedMemoryHandle. + // + // Returns true if creating the shared memory (and copying) is successful, + // false otherwise. + virtual bool CopyToNewShmem( + PP_Instance instance, + int *host_shm_handle_id, + base::SharedMemoryHandle *plugin_shm_handle) = 0; + // Var override. virtual ArrayBufferVar* AsArrayBufferVar() OVERRIDE; virtual PP_VarType GetType() const OVERRIDE; diff --git a/ppapi/shared_impl/var_tracker.cc b/ppapi/shared_impl/var_tracker.cc index a704c3a..434121e 100644 --- a/ppapi/shared_impl/var_tracker.cc +++ b/ppapi/shared_impl/var_tracker.cc @@ -9,6 +9,8 @@ #include <limits> #include "base/logging.h" +#include "base/shared_memory.h" +#include "ppapi/shared_impl/host_resource.h" #include "ppapi/shared_impl/id_assignment.h" #include "ppapi/shared_impl/proxy_lock.h" #include "ppapi/shared_impl/var.h" @@ -216,6 +218,17 @@ ArrayBufferVar* VarTracker::MakeArrayBufferVar(uint32 size_in_bytes, return array_buffer; } +PP_Var VarTracker::MakeArrayBufferPPVar(uint32 size_in_bytes, + base::SharedMemoryHandle handle) { + DCHECK(CalledOnValidThread()); + + scoped_refptr<ArrayBufferVar> array_buffer( + CreateShmArrayBuffer(size_in_bytes, handle)); + if (!array_buffer) + return PP_MakeNull(); + return array_buffer->GetPPVar(); +} + std::vector<PP_Var> VarTracker::GetLiveVars() { DCHECK(CalledOnValidThread()); ProxyLock::AssertAcquired(); diff --git a/ppapi/shared_impl/var_tracker.h b/ppapi/shared_impl/var_tracker.h index 1d0c0d6..32acdeb 100644 --- a/ppapi/shared_impl/var_tracker.h +++ b/ppapi/shared_impl/var_tracker.h @@ -10,10 +10,13 @@ #include "base/basictypes.h" #include "base/hash_tables.h" #include "base/memory/ref_counted.h" +#include "base/shared_memory.h" #include "base/threading/non_thread_safe.h" #include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_module.h" +#include "ppapi/c/pp_resource.h" #include "ppapi/c/pp_var.h" +#include "ppapi/shared_impl/host_resource.h" #include "ppapi/shared_impl/ppapi_shared_export.h" namespace ppapi { @@ -72,6 +75,10 @@ class PPAPI_SHARED_EXPORT VarTracker PP_Var MakeArrayBufferPPVar(uint32 size_in_bytes); // Same as above, but copy the contents of |data| in to the new array buffer. PP_Var MakeArrayBufferPPVar(uint32 size_in_bytes, const void* data); + // Same as above, but copy the contents of the shared memory in |h| + // into the new array buffer. + PP_Var MakeArrayBufferPPVar(uint32 size_in_bytes, + base::SharedMemoryHandle h); // Create an ArrayBuffer and copy the contents of |data| in to it. The // returned object has 0 reference count in the tracker, and like all @@ -94,6 +101,22 @@ class PPAPI_SHARED_EXPORT VarTracker // Called after an instance is deleted to do var cleanup. virtual void DidDeleteInstance(PP_Instance instance) = 0; + // Returns an "id" for a shared memory handle that can be safely sent between + // the host and plugin, and resolved back into the original handle on the + // host. Not implemented on the plugin side. + virtual int TrackSharedMemoryHandle(PP_Instance instance, + base::SharedMemoryHandle handle, + uint32 size_in_bytes) = 0; + + // Resolves an "id" generated by TrackSharedMemoryHandle back into + // a SharedMemory handle and its size on the host. + // Not implemented on the plugin side. + virtual bool StopTrackingSharedMemoryHandle( + int id, + PP_Instance instance, + base::SharedMemoryHandle *handle, + uint32* size_in_bytes) = 0; + protected: struct VarInfo { VarInfo(); @@ -171,6 +194,9 @@ class PPAPI_SHARED_EXPORT VarTracker // implemented by the Host and Plugin tracker separately, so that it can be // a real WebKit ArrayBuffer on the host side. virtual ArrayBufferVar* CreateArrayBuffer(uint32 size_in_bytes) = 0; + virtual ArrayBufferVar* CreateShmArrayBuffer( + uint32 size_in_bytes, + base::SharedMemoryHandle handle) = 0; DISALLOW_COPY_AND_ASSIGN(VarTracker); }; diff --git a/ppapi/tests/test_post_message.cc b/ppapi/tests/test_post_message.cc index 68fe633..8b16620 100644 --- a/ppapi/tests/test_post_message.cc +++ b/ppapi/tests/test_post_message.cc @@ -261,7 +261,11 @@ std::string TestPostMessage::TestSendingArrayBuffer() { // TODO(sehr,dmichael): Add testing of longer array buffers when // crbug.com/110086 is fixed. - uint32_t sizes[] = { 0, 100, 1000, 10000, 100000 }; +#if defined(OS_LINUX) + uint32_t sizes[] = { 0, 100, 1000, 10000, 100000, 1000000 }; +#else + uint32_t sizes[] = { 0, 100, 1000, 10000, 100000 }; +#endif for (size_t i = 0; i < sizeof(sizes)/sizeof(sizes[i]); ++i) { std::ostringstream size_stream; size_stream << sizes[i]; diff --git a/webkit/plugins/ppapi/host_array_buffer_var.cc b/webkit/plugins/ppapi/host_array_buffer_var.cc index c8266df..e3d06a5 100644 --- a/webkit/plugins/ppapi/host_array_buffer_var.cc +++ b/webkit/plugins/ppapi/host_array_buffer_var.cc @@ -4,6 +4,17 @@ #include "webkit/plugins/ppapi/host_array_buffer_var.h" +#include <stdio.h> +#include <string.h> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/process_util.h" +#include "ppapi/c/pp_instance.h" +#include "webkit/plugins/ppapi/host_globals.h" +#include "webkit/plugins/ppapi/plugin_module.h" +#include "webkit/plugins/ppapi/ppapi_plugin_instance.h" + using ppapi::ArrayBufferVar; using WebKit::WebArrayBuffer; @@ -11,17 +22,32 @@ namespace webkit { namespace ppapi { HostArrayBufferVar::HostArrayBufferVar(uint32 size_in_bytes) - : buffer_(WebArrayBuffer::create(size_in_bytes, 1 /* element_size */)) { + : buffer_(WebArrayBuffer::create(size_in_bytes, 1 /* element_size */)), + valid_(true) { } HostArrayBufferVar::HostArrayBufferVar(const WebArrayBuffer& buffer) - : buffer_(buffer) { + : buffer_(buffer), + valid_(true) { +} + +HostArrayBufferVar::HostArrayBufferVar(uint32 size_in_bytes, + base::SharedMemoryHandle handle) + : buffer_(WebArrayBuffer::create(size_in_bytes, 1 /* element_size */)) { + base::SharedMemory s(handle, true); + valid_ = s.Map(size_in_bytes); + if (valid_) { + memcpy(buffer_.data(), s.memory(), size_in_bytes); + s.Unmap(); + } } HostArrayBufferVar::~HostArrayBufferVar() { } void* HostArrayBufferVar::Map() { + if (!valid_) + return NULL; return buffer_.data(); } @@ -33,6 +59,45 @@ uint32 HostArrayBufferVar::ByteLength() { return buffer_.byteLength(); } +bool HostArrayBufferVar::CopyToNewShmem( + PP_Instance instance, + int* host_shm_handle_id, + base::SharedMemoryHandle* plugin_shm_handle) { + webkit::ppapi::PluginInstance* i = + webkit::ppapi::HostGlobals::Get()->GetInstance(instance); + scoped_ptr<base::SharedMemory> shm(i->delegate()->CreateAnonymousSharedMemory( + ByteLength())); + if (!shm.get()) + return false; + + shm->Map(ByteLength()); + memcpy(shm->memory(), Map(), ByteLength()); + shm->Unmap(); + + // Duplicate the handle here; the SharedMemory destructor closes + // its handle on us. + HostGlobals* hg = HostGlobals::Get(); + PluginModule* pm = hg->GetModule(hg->GetModuleForInstance(instance)); + base::ProcessId p = pm->GetPeerProcessId(); + if (p == base::kNullProcessId) { + // In-process, clone for ourselves. + p = base::GetCurrentProcId(); + } + + base::PlatformFile platform_file = +#if defined(OS_WIN) + shm->handle(); +#elif defined(OS_POSIX) + shm->handle().fd; +#else +#error Not implemented. +#endif + + *plugin_shm_handle = + i->delegate()->ShareHandleWithRemote(platform_file, p, false); + *host_shm_handle_id = -1; + return true; +} + } // namespace ppapi } // namespace webkit - diff --git a/webkit/plugins/ppapi/host_array_buffer_var.h b/webkit/plugins/ppapi/host_array_buffer_var.h index 803069a..646adf8 100644 --- a/webkit/plugins/ppapi/host_array_buffer_var.h +++ b/webkit/plugins/ppapi/host_array_buffer_var.h @@ -5,6 +5,9 @@ #ifndef PPAPI_WEBKIT_PLUGINS_PPAPI_HOST_ARRAY_BUFFER_VAR_H_ #define PPAPI_WEBKIT_PLUGINS_PPAPI_HOST_ARRAY_BUFFER_VAR_H_ +#include "base/shared_memory.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/shared_impl/host_resource.h" #include "ppapi/shared_impl/var.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebArrayBuffer.h" @@ -16,17 +19,25 @@ class HostArrayBufferVar : public ::ppapi::ArrayBufferVar { public: explicit HostArrayBufferVar(uint32 size_in_bytes); explicit HostArrayBufferVar(const WebKit::WebArrayBuffer& buffer); + explicit HostArrayBufferVar(uint32 size_in_bytes, + base::SharedMemoryHandle handle); virtual ~HostArrayBufferVar(); // ArrayBufferVar implementation. virtual void* Map() OVERRIDE; virtual void Unmap() OVERRIDE; virtual uint32 ByteLength() OVERRIDE; + virtual bool CopyToNewShmem( + PP_Instance instance, + int* host_shm_handle_id, + base::SharedMemoryHandle* plugin_shm_handle) OVERRIDE; WebKit::WebArrayBuffer& webkit_buffer() { return buffer_; } private: WebKit::WebArrayBuffer buffer_; + // Tracks whether the data in the buffer is valid. + bool valid_; DISALLOW_COPY_AND_ASSIGN(HostArrayBufferVar); }; diff --git a/webkit/plugins/ppapi/host_var_tracker.cc b/webkit/plugins/ppapi/host_var_tracker.cc index adeeaa1..5d53499 100644 --- a/webkit/plugins/ppapi/host_var_tracker.cc +++ b/webkit/plugins/ppapi/host_var_tracker.cc @@ -16,7 +16,7 @@ using ppapi::NPObjectVar; namespace webkit { namespace ppapi { -HostVarTracker::HostVarTracker() { +HostVarTracker::HostVarTracker() : last_shared_memory_map_id_(0) { } HostVarTracker::~HostVarTracker() { @@ -26,6 +26,12 @@ ArrayBufferVar* HostVarTracker::CreateArrayBuffer(uint32 size_in_bytes) { return new HostArrayBufferVar(size_in_bytes); } +ArrayBufferVar* HostVarTracker::CreateShmArrayBuffer( + uint32 size_in_bytes, + base::SharedMemoryHandle handle) { + return new HostArrayBufferVar(size_in_bytes, handle); +} + void HostVarTracker::AddNPObjectVar(NPObjectVar* object_var) { DCHECK(CalledOnValidThread()); @@ -127,5 +133,39 @@ void HostVarTracker::ForceReleaseNPObject(::ppapi::NPObjectVar* object_var) { DeleteObjectInfoIfNecessary(iter); } +int HostVarTracker::TrackSharedMemoryHandle(PP_Instance instance, + base::SharedMemoryHandle handle, + uint32 size_in_bytes) { + SharedMemoryMapEntry entry; + entry.instance = instance; + entry.handle = handle; + entry.size_in_bytes = size_in_bytes; + + // Find a free id for our map. + while (shared_memory_map_.find(last_shared_memory_map_id_) != + shared_memory_map_.end()) { + ++last_shared_memory_map_id_; + } + shared_memory_map_[last_shared_memory_map_id_] = entry; + return last_shared_memory_map_id_; +} + +bool HostVarTracker::StopTrackingSharedMemoryHandle( + int id, + PP_Instance instance, + base::SharedMemoryHandle* handle, + uint32* size_in_bytes) { + SharedMemoryMap::iterator it = shared_memory_map_.find(id); + if (it == shared_memory_map_.end()) + return false; + if (it->second.instance != instance) + return false; + + *handle = it->second.handle; + *size_in_bytes = it->second.size_in_bytes; + shared_memory_map_.erase(it); + return true; +} + } // namespace ppapi } // namespace webkit diff --git a/webkit/plugins/ppapi/host_var_tracker.h b/webkit/plugins/ppapi/host_var_tracker.h index 7629f9a..138920c 100644 --- a/webkit/plugins/ppapi/host_var_tracker.h +++ b/webkit/plugins/ppapi/host_var_tracker.h @@ -14,7 +14,7 @@ #include "base/memory/linked_ptr.h" #include "base/memory/ref_counted.h" #include "ppapi/c/pp_instance.h" -#include "ppapi/c/pp_resource.h" +#include "ppapi/shared_impl/host_resource.h" #include "ppapi/shared_impl/resource_tracker.h" #include "ppapi/shared_impl/var_tracker.h" #include "webkit/plugins/webkit_plugins_export.h" @@ -58,10 +58,20 @@ class HostVarTracker : public ::ppapi::VarTracker { // VarTracker public implementation. virtual void DidDeleteInstance(PP_Instance instance) OVERRIDE; + virtual int TrackSharedMemoryHandle(PP_Instance instance, + base::SharedMemoryHandle file, + uint32 size_in_bytes) OVERRIDE; + virtual bool StopTrackingSharedMemoryHandle(int id, + PP_Instance instance, + base::SharedMemoryHandle* handle, + uint32* size_in_bytes) OVERRIDE; + private: // VarTracker private implementation. virtual ::ppapi::ArrayBufferVar* CreateArrayBuffer( uint32 size_in_bytes) OVERRIDE; + virtual ::ppapi::ArrayBufferVar* CreateShmArrayBuffer( + uint32 size_in_bytes, base::SharedMemoryHandle handle) OVERRIDE; // Clear the reference count of the given object and remove it from // live_vars_. @@ -81,6 +91,16 @@ class HostVarTracker : public ::ppapi::VarTracker { InstanceMap; InstanceMap instance_map_; + // Tracks all shared memory handles used for transmitting array buffers. + struct SharedMemoryMapEntry { + PP_Instance instance; + base::SharedMemoryHandle handle; + uint32 size_in_bytes; + }; + typedef std::map<int, SharedMemoryMapEntry> SharedMemoryMap; + SharedMemoryMap shared_memory_map_; + uint32_t last_shared_memory_map_id_; + DISALLOW_COPY_AND_ASSIGN(HostVarTracker); }; diff --git a/webkit/plugins/ppapi/mock_plugin_delegate.cc b/webkit/plugins/ppapi/mock_plugin_delegate.cc index c465618..137c061 100644 --- a/webkit/plugins/ppapi/mock_plugin_delegate.cc +++ b/webkit/plugins/ppapi/mock_plugin_delegate.cc @@ -382,5 +382,12 @@ int MockPluginDelegate::EnumerateDevices( void MockPluginDelegate::StopEnumerateDevices(int request_id) { } +IPC::PlatformFileForTransit MockPluginDelegate::ShareHandleWithRemote( + base::PlatformFile handle, + base::ProcessId target_process_id, + bool should_close_source) const { + return IPC::InvalidPlatformFileForTransit(); +} + } // namespace ppapi } // namespace webkit diff --git a/webkit/plugins/ppapi/mock_plugin_delegate.h b/webkit/plugins/ppapi/mock_plugin_delegate.h index 5f47647..b26ad9f 100644 --- a/webkit/plugins/ppapi/mock_plugin_delegate.h +++ b/webkit/plugins/ppapi/mock_plugin_delegate.h @@ -167,6 +167,10 @@ class MockPluginDelegate : public PluginDelegate { virtual int EnumerateDevices(PP_DeviceType_Dev type, const EnumerateDevicesCallback& callback); virtual void StopEnumerateDevices(int request_id); + virtual IPC::PlatformFileForTransit ShareHandleWithRemote( + base::PlatformFile handle, + base::ProcessId target_process_id, + bool should_close_source) const; }; } // namespace ppapi diff --git a/webkit/plugins/ppapi/plugin_delegate.h b/webkit/plugins/ppapi/plugin_delegate.h index 164326b..1c861e8 100644 --- a/webkit/plugins/ppapi/plugin_delegate.h +++ b/webkit/plugins/ppapi/plugin_delegate.h @@ -17,6 +17,7 @@ #include "base/sync_socket.h" #include "base/time.h" #include "googleurl/src/gurl.h" +#include "ipc/ipc_platform_file.h" #include "media/video/capture/video_capture.h" #include "media/video/video_decode_accelerator.h" #include "ppapi/c/dev/pp_video_dev.h" @@ -650,6 +651,12 @@ class PluginDelegate { // Stop enumerating devices of the specified |request_id|. The |request_id| // is the return value of EnumerateDevicesCallback. virtual void StopEnumerateDevices(int request_id) = 0; + + // Share a given handle with the target process. + virtual IPC::PlatformFileForTransit ShareHandleWithRemote( + base::PlatformFile handle, + base::ProcessId target_process_id, + bool should_close_source) const = 0; }; } // namespace ppapi |