diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-15 22:06:21 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-15 22:06:21 +0000 |
commit | 1609d0d3a7d5b0f3777702308a440b22f1b42b59 (patch) | |
tree | 754be1a35ea8faa52a8dbb6a63264a2f686bea64 | |
parent | 54457f38c36e43d9f5d434e5bfc3cb83d9e56926 (diff) | |
download | chromium_src-1609d0d3a7d5b0f3777702308a440b22f1b42b59.zip chromium_src-1609d0d3a7d5b0f3777702308a440b22f1b42b59.tar.gz chromium_src-1609d0d3a7d5b0f3777702308a440b22f1b42b59.tar.bz2 |
Support PPB_Flash_ModileLocal blocking calls from background threads. This is
a temporary stopgap to allow these specific functions to be used until the
entire proxy can support threading.
There are two functions that set up and clean up the routing information in
the interface. These are necessary because the global dispatcher routing
information is not threadsafe.
When used from the main thread, the code works the same as before. When used
from a background thread, we create a sync message, send it to the I/O thread,
and block the sending thread until the reply is received. There is no handling
of other incoming blocking messages (which are impossible), and no other
Pepper functions are supported from the background thread.
TEST=manual
BUG=none
Review URL: http://codereview.chromium.org/6875009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@81814 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | ppapi/c/private/ppb_flash_file.h | 16 | ||||
-rw-r--r-- | ppapi/proxy/dispatcher.cc | 10 | ||||
-rw-r--r-- | ppapi/proxy/dispatcher.h | 13 | ||||
-rw-r--r-- | ppapi/proxy/ppb_flash_file_proxy.cc | 409 | ||||
-rw-r--r-- | webkit/plugins/ppapi/ppb_flash_file_impl.cc | 9 |
5 files changed, 422 insertions, 35 deletions
diff --git a/ppapi/c/private/ppb_flash_file.h b/ppapi/c/private/ppb_flash_file.h index 9b674fa..b545128a 100644 --- a/ppapi/c/private/ppb_flash_file.h +++ b/ppapi/c/private/ppb_flash_file.h @@ -36,12 +36,26 @@ struct PP_DirContents_Dev { // PPB_Flash_File_ModuleLocal -------------------------------------------------- -#define PPB_FLASH_FILE_MODULELOCAL_INTERFACE "PPB_Flash_File_ModuleLocal;1" +#define PPB_FLASH_FILE_MODULELOCAL_INTERFACE "PPB_Flash_File_ModuleLocal;2" // This interface provides (for Flash) synchronous access to module-local files. // Module-local file paths are '/'-separated UTF-8 strings, relative to a // module-specific root. struct PPB_Flash_File_ModuleLocal { + // Does initialization necessary for proxying this interface on background + // threads. You must always call this function before using any other + // function in this interface for a given instance ID. + // + // Returns true if multithreaded access is supported. In this case you can + // use the rest of the functions from background threads. You may not call + // GetInterface or do any other PPAPI operations on background threads at + // this time. + bool (*CreateThreadAdapterForInstance)(PP_Instance instance); + + // Call when an instance is destroyed when you've previously called + // CreateThreadAdapterForInstance. + void (*ClearThreadAdapterForInstance)(PP_Instance instance); + // Opens a file, returning a file descriptor (posix) or a HANDLE (win32) into // file. The return value is the ppapi error, PP_OK if success, one of the // PP_ERROR_* in case of failure. diff --git a/ppapi/proxy/dispatcher.cc b/ppapi/proxy/dispatcher.cc index ebdc718..a4d9798 100644 --- a/ppapi/proxy/dispatcher.cc +++ b/ppapi/proxy/dispatcher.cc @@ -192,6 +192,7 @@ Dispatcher::~Dispatcher() { bool Dispatcher::InitWithChannel(Delegate* delegate, const IPC::ChannelHandle& channel_handle, bool is_client) { + delegate_ = delegate; IPC::Channel::Mode mode = is_client ? IPC::Channel::MODE_CLIENT : IPC::Channel::MODE_SERVER; channel_.reset(new IPC::SyncChannel(channel_handle, mode, this, @@ -297,6 +298,15 @@ IPC::PlatformFileForTransit Dispatcher::ShareHandleWithRemote( return out_handle; } +MessageLoop* Dispatcher::GetIPCMessageLoop() { + return delegate_->GetIPCMessageLoop(); +} + +void Dispatcher::AddIOThreadMessageFilter( + IPC::ChannelProxy::MessageFilter* filter) { + channel_->AddFilter(filter); +} + bool Dispatcher::Send(IPC::Message* msg) { if (test_sink_) return test_sink_->Send(msg); diff --git a/ppapi/proxy/dispatcher.h b/ppapi/proxy/dispatcher.h index 326d97c..5e6ea28 100644 --- a/ppapi/proxy/dispatcher.h +++ b/ppapi/proxy/dispatcher.h @@ -15,6 +15,7 @@ #include "base/process.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_channel_handle.h" +#include "ipc/ipc_channel_proxy.h" #include "ipc/ipc_message.h" #include "ipc/ipc_platform_file.h" #include "ppapi/c/pp_instance.h" @@ -114,6 +115,18 @@ class Dispatcher : public IPC::Channel::Listener, base::PlatformFile handle, bool should_close_source); + // Returns the pointer to the IO thread for processing IPC messages. + // TODO(brettw) remove this. It's a hack to support the Flash + // ModuleLocalThreadAdapter. When the thread stuff is sorted out, this + // implementation detail should be hidden. + MessageLoop* GetIPCMessageLoop(); + + // Adds the given filter to the IO thread. Takes ownership of the pointer. + // TODO(brettw) remove this. It's a hack to support the Flash + // ModuleLocalThreadAdapter. When the thread stuff is sorted out, this + // implementation detail should be hidden. + void AddIOThreadMessageFilter(IPC::ChannelProxy::MessageFilter* filter); + // Called if the remote side is declaring to us which interfaces it supports // so we don't have to query for each one. We'll pre-create proxies for // each of the given interfaces. diff --git a/ppapi/proxy/ppb_flash_file_proxy.cc b/ppapi/proxy/ppb_flash_file_proxy.cc index da0f2b9..8b55b4a 100644 --- a/ppapi/proxy/ppb_flash_file_proxy.cc +++ b/ppapi/proxy/ppb_flash_file_proxy.cc @@ -4,8 +4,18 @@ #include "ppapi/proxy/ppb_flash_file_proxy.h" +#include <map> +#include <set> +#include <vector> + #include "base/logging.h" +#include "base/message_loop.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event.h" #include "build/build_config.h" +#include "ipc/ipc_channel_proxy.h" +#include "ipc/ipc_message.h" +#include "ipc/ipc_sync_message.h" #include "ppapi/c/dev/pp_file_info_dev.h" #include "ppapi/c/pp_errors.h" #include "ppapi/c/private/ppb_flash_file.h" @@ -44,23 +54,351 @@ void FreeDirContents(PP_Instance /* instance */, } // namespace +// ModuleLocalThreadAdapter ---------------------------------------------------- + +class ModuleLocalThreadAdapter + : public base::RefCountedThreadSafe<ModuleLocalThreadAdapter> { + class Filter; + public: + ModuleLocalThreadAdapter(); + + void AddInstanceRouting(PP_Instance instance, Dispatcher* dispatcher); + void ClearInstanceRouting(PP_Instance instance); + void ClearFilter(Dispatcher* dispatcher, Filter* filter); + + bool OnModuleLocalMessageReceived(const IPC::Message& msg); + + // Called on the I/O thread when the channel is being destroyed and the + // given message will never be issued a reply. + void OnModuleLocalMessageFailed(int message_id); + + bool Send(PP_Instance instance, IPC::Message* msg); + + private: + class Filter : public IPC::ChannelProxy::MessageFilter { + public: + Filter(Dispatcher* dispatcher); + ~Filter(); + + void Send(IPC::Message* msg); + + virtual void OnFilterAdded(IPC::Channel* channel); + virtual void OnFilterRemoved(); + virtual bool OnMessageReceived(const IPC::Message& message); + + private: + // DO NOT DEREFERENCE! This is used only for tracking. + Dispatcher* dispatcher_; + + IPC::Channel* channel_; + + // Holds the IPC messages that were sent before the channel was connected. + // These will be sent ASAP. + std::vector<IPC::Message*> pre_connect_pending_messages_; + + // Holds the IDs of the sync messages we're currently waiting on for this + // channel. This tracking allows us to cancel those requests if the + // remote process crashes and we're cleaning up this filter (without just + // deadlocking the waiting thread(s). + std::set<int> pending_requests_for_filter_; + }; + + virtual void SendFromIOThread(Dispatcher* dispatcher, IPC::Message* msg); + + // Internal version of OnModuleLocalMessageFailed which assumes the lock + // is already held. + void OnModuleLocalMessageFailedLocked(int message_id); + + base::Lock lock_; + + MessageLoop* main_thread_; + + // Will be NULL before an instance routing is added. + MessageLoop* io_thread_; + + typedef std::map<PP_Instance, Dispatcher*> InstanceToDispatcher; + InstanceToDispatcher instance_to_dispatcher_; + + // The filters are owned by the channel. + typedef std::map<Dispatcher*, Filter*> DispatcherToFilter; + DispatcherToFilter dispatcher_to_filter_; + + // Tracks all messages with currently waiting threads. This does not own + // the pointer, the pointer lifetime is managed by Send(). + typedef std::map<int, IPC::PendingSyncMsg*> SyncRequestMap; + SyncRequestMap pending_sync_requests_; +}; + +ModuleLocalThreadAdapter* g_module_local_thread_adapter = NULL; + +ModuleLocalThreadAdapter::Filter::Filter(Dispatcher* dispatcher) + : dispatcher_(dispatcher), channel_(NULL) { +} + +ModuleLocalThreadAdapter::Filter::~Filter() { +} + +void ModuleLocalThreadAdapter::Filter::Send(IPC::Message* msg) { + if (channel_) { + int message_id = IPC::SyncMessage::GetMessageId(*msg); + if (channel_->Send(msg)) + pending_requests_for_filter_.insert(message_id); + else // Message lost, notify adapter so it can unblock. + g_module_local_thread_adapter->OnModuleLocalMessageFailed(message_id); + } else { + // No channel, save this message for when it's connected. + pre_connect_pending_messages_.push_back(msg); + } +} + +void ModuleLocalThreadAdapter::Filter::OnFilterAdded(IPC::Channel* channel) { + DCHECK(!channel_); + channel_ = channel; + + // Now that we have a channel, process all pending messages. + for (size_t i = 0; i < pre_connect_pending_messages_.size(); i++) + Send(pre_connect_pending_messages_[i]); + pre_connect_pending_messages_.clear(); +} + +void ModuleLocalThreadAdapter::Filter::OnFilterRemoved() { + DCHECK(channel_); + channel_ = NULL; + g_module_local_thread_adapter->ClearFilter(dispatcher_, this); + + for (std::set<int>::iterator i = pending_requests_for_filter_.begin(); + i != pending_requests_for_filter_.end(); ++i) { + g_module_local_thread_adapter->OnModuleLocalMessageFailed(*i); + } +} + +bool ModuleLocalThreadAdapter::Filter::OnMessageReceived( + const IPC::Message& message) { + if (!message.is_reply() || + message.routing_id() != INTERFACE_ID_PPB_FLASH_FILE_MODULELOCAL) + return false; + + if (g_module_local_thread_adapter->OnModuleLocalMessageReceived(message)) { + // The message was consumed, this means we can remove the message ID from + // the list of messages this channel is waiting on. + pending_requests_for_filter_.erase(IPC::SyncMessage::GetMessageId(message)); + return true; + } + return false; +} + +ModuleLocalThreadAdapter::ModuleLocalThreadAdapter() + : main_thread_(MessageLoop::current()), + io_thread_(NULL) { +} + +void ModuleLocalThreadAdapter::AddInstanceRouting(PP_Instance instance, + Dispatcher* dispatcher) { + base::AutoLock lock(lock_); + + // Now that we've had contact with a dispatcher, we can set up the IO thread. + DCHECK(MessageLoop::current() == main_thread_); + if (!io_thread_) + io_thread_ = dispatcher->GetIPCMessageLoop(); + + // Set up the instance -> dispatcher routing. + DCHECK(instance_to_dispatcher_.find(instance) == + instance_to_dispatcher_.end()); + instance_to_dispatcher_[instance] = dispatcher; + + DispatcherToFilter::iterator found_filter = + dispatcher_to_filter_.find(dispatcher); + if (found_filter == dispatcher_to_filter_.end()) { + // Need to set up a filter for this dispatcher to intercept the messages. + Filter* filter = new Filter(dispatcher); + dispatcher_to_filter_[dispatcher] = filter; + dispatcher->AddIOThreadMessageFilter(filter); + } +} + +void ModuleLocalThreadAdapter::ClearInstanceRouting(PP_Instance instance) { + // The dispatcher->filter mapping is cleaned up by ClearFilter which is + // initiated by the channel. + instance_to_dispatcher_.erase(instance); +} + +void ModuleLocalThreadAdapter::ClearFilter(Dispatcher* dispatcher, + Filter* filter) { + // DANGER! Don't dereference the dispatcher, it's just used to identify + // which filter to remove. The dispatcher may not even exist any more. + // + // Since the dispatcher may be gone, there's a potential for ambiguity if + // another one is created on the main thread before this code runs on the + // I/O thread. So we check that the filter matches to avoid this rare case. + base::AutoLock lock(lock_); + if (dispatcher_to_filter_[dispatcher] == filter) + dispatcher_to_filter_.erase(dispatcher); +} + +bool ModuleLocalThreadAdapter::OnModuleLocalMessageReceived( + const IPC::Message& msg) { + base::AutoLock lock(lock_); + + int message_id = IPC::SyncMessage::GetMessageId(msg); + SyncRequestMap::iterator found = pending_sync_requests_.find(message_id); + if (found == pending_sync_requests_.end()) { + // Not waiting for this event. This will happen for sync messages to the + // main thread which use the "regular" sync channel code path. + return false; + } + + IPC::PendingSyncMsg& info = *found->second; + + if (!msg.is_reply_error()) + info.deserializer->SerializeOutputParameters(msg); + info.done_event->Signal(); + return true; +} + +void ModuleLocalThreadAdapter::OnModuleLocalMessageFailed(int message_id) { + base::AutoLock lock(lock_); + OnModuleLocalMessageFailedLocked(message_id); +} + +bool ModuleLocalThreadAdapter::Send(PP_Instance instance, IPC::Message* msg) { + // Compute the dispatcher corresponding to this message. + Dispatcher* dispatcher = NULL; + { + base::AutoLock lock(lock_); + InstanceToDispatcher::iterator found = + instance_to_dispatcher_.find(instance); + if (found == instance_to_dispatcher_.end()) { + NOTREACHED(); + delete msg; + return false; + } + dispatcher = found->second; + } + + if (MessageLoop::current() == main_thread_) { + // Easy case: We're on the same thread as the dispatcher, so we don't need + // a lock to access it, and we can just use the normal sync channel stuff + // to handle the message. Actually, we MUST use the normal sync channel + // stuff since there may be incoming sync messages that need processing. + // The code below doesn't handle any nested message loops. + return dispatcher->Send(msg); + } + + // Background thread case + // ---------------------- + // 1. Generate tracking info, stick in pending_sync_messages_map. + // 2. Kick off the request. This is done on the I/O thread. + // 3. Filter on the I/O thread notices reply, writes the reply data and + // signals the event. We block on the event while this is happening. + // 4. Remove tracking info. + + // Generate the tracking info. and copied + IPC::SyncMessage* sync_msg = static_cast<IPC::SyncMessage*>(msg); + int message_id = IPC::SyncMessage::GetMessageId(*sync_msg); + base::WaitableEvent event(true, false); + scoped_ptr<IPC::MessageReplyDeserializer> deserializer( + sync_msg->GetReplyDeserializer()); // We own this pointer once retrieved. + IPC::PendingSyncMsg info(message_id, deserializer.get(), &event); + + // Add the tracking information to our map. + { + base::AutoLock lock(lock_); + pending_sync_requests_[message_id] = &info; + } + + // This is a bit dangerous. We use the dispatcher pointer as the routing + // ID for this message. While we don't dereference it, there is an + // exceedingly remote possibility that while this is going to the background + // thread the connection will be shut down and a new one will be created with + // a dispatcher at the same address. It could potentially get sent to a + // random place, but it should actually still work (since the Flash file + // operations are global). + io_thread_->PostTask(FROM_HERE, + NewRunnableMethod(this, &ModuleLocalThreadAdapter::SendFromIOThread, + dispatcher, msg)); + + // Now we block the current thread waiting for the reply. + event.Wait(); + + { + // Clear our tracking info for this message now that we're done. + base::AutoLock lock(lock_); + DCHECK(pending_sync_requests_.find(message_id) != + pending_sync_requests_.end()); + pending_sync_requests_.erase(message_id); + } + + return true; +} + +void ModuleLocalThreadAdapter::SendFromIOThread(Dispatcher* dispatcher, + IPC::Message* msg) { + // DO NOT DEREFERENCE DISPATCHER. Used as a lookup only. + base::AutoLock lock(lock_); + DispatcherToFilter::iterator found = dispatcher_to_filter_.find(dispatcher); + + // The dispatcher could have been destroyed by the time we got here since + // we're on another thread. Need to unblock the caller. + if (found == dispatcher_to_filter_.end()) { + OnModuleLocalMessageFailedLocked(IPC::SyncMessage::GetMessageId(*msg)); + delete msg; + return; + } + + // Takes ownership of pointer. + found->second->Send(msg); +} + +void ModuleLocalThreadAdapter::OnModuleLocalMessageFailedLocked( + int message_id) { + lock_.AssertAcquired(); + + // Unblock the thread waiting for the message that will never come. + SyncRequestMap::iterator found = pending_sync_requests_.find(message_id); + if (found == pending_sync_requests_.end()) { + NOTREACHED(); + return; + } + found->second->done_event->Signal(); +} + // PPB_Flash_File_ModuleLocal -------------------------------------------------- namespace { +bool CreateThreadAdapterForInstance(PP_Instance instance) { + if (!g_module_local_thread_adapter) { + g_module_local_thread_adapter = new ModuleLocalThreadAdapter(); + g_module_local_thread_adapter->AddRef(); // Leaked, this object is global. + } + + PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); + if (!dispatcher) { + NOTREACHED(); + return false; + } + g_module_local_thread_adapter->AddInstanceRouting(instance, dispatcher); + return true; +} + +void ClearThreadAdapterForInstance(PP_Instance instance) { + if (g_module_local_thread_adapter) + g_module_local_thread_adapter->ClearInstanceRouting(instance); +} + int32_t OpenModuleLocalFile(PP_Instance instance, const char* path, int32_t mode, PP_FileHandle* file) { - PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); - if (!dispatcher) - return PP_ERROR_BADARGUMENT; + if (!g_module_local_thread_adapter) + return PP_ERROR_FAILED; int32_t result = PP_ERROR_FAILED; IPC::PlatformFileForTransit transit; - dispatcher->Send(new PpapiHostMsg_PPBFlashFile_ModuleLocal_OpenFile( - INTERFACE_ID_PPB_FLASH_FILE_MODULELOCAL, - instance, path, mode, &transit, &result)); + g_module_local_thread_adapter->Send(instance, + new PpapiHostMsg_PPBFlashFile_ModuleLocal_OpenFile( + INTERFACE_ID_PPB_FLASH_FILE_MODULELOCAL, + instance, path, mode, &transit, &result)); *file = IPC::PlatformFileForTransitToPlatformFile(transit); return result; } @@ -68,67 +406,68 @@ int32_t OpenModuleLocalFile(PP_Instance instance, int32_t RenameModuleLocalFile(PP_Instance instance, const char* from_path, const char* to_path) { - PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); - if (!dispatcher) - return PP_ERROR_BADARGUMENT; + if (!g_module_local_thread_adapter) + return PP_ERROR_FAILED; int32_t result = PP_ERROR_FAILED; - dispatcher->Send(new PpapiHostMsg_PPBFlashFile_ModuleLocal_RenameFile( - INTERFACE_ID_PPB_FLASH_FILE_MODULELOCAL, - instance, from_path, to_path, &result)); + g_module_local_thread_adapter->Send(instance, + new PpapiHostMsg_PPBFlashFile_ModuleLocal_RenameFile( + INTERFACE_ID_PPB_FLASH_FILE_MODULELOCAL, + instance, from_path, to_path, &result)); return result; } int32_t DeleteModuleLocalFileOrDir(PP_Instance instance, const char* path, PP_Bool recursive) { - PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); - if (!dispatcher) - return PP_ERROR_BADARGUMENT; + if (!g_module_local_thread_adapter) + return PP_ERROR_FAILED; int32_t result = PP_ERROR_FAILED; - dispatcher->Send(new PpapiHostMsg_PPBFlashFile_ModuleLocal_DeleteFileOrDir( - INTERFACE_ID_PPB_FLASH_FILE_MODULELOCAL, - instance, path, recursive, &result)); + g_module_local_thread_adapter->Send(instance, + new PpapiHostMsg_PPBFlashFile_ModuleLocal_DeleteFileOrDir( + INTERFACE_ID_PPB_FLASH_FILE_MODULELOCAL, + instance, path, recursive, &result)); return result; } int32_t CreateModuleLocalDir(PP_Instance instance, const char* path) { - PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); - if (!dispatcher) - return PP_ERROR_BADARGUMENT; + if (!g_module_local_thread_adapter) + return PP_ERROR_FAILED; int32_t result = PP_ERROR_FAILED; - dispatcher->Send(new PpapiHostMsg_PPBFlashFile_ModuleLocal_CreateDir( - INTERFACE_ID_PPB_FLASH_FILE_MODULELOCAL, instance, path, &result)); + g_module_local_thread_adapter->Send(instance, + new PpapiHostMsg_PPBFlashFile_ModuleLocal_CreateDir( + INTERFACE_ID_PPB_FLASH_FILE_MODULELOCAL, instance, path, &result)); return result; } int32_t QueryModuleLocalFile(PP_Instance instance, const char* path, PP_FileInfo_Dev* info) { - PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); - if (!dispatcher) - return PP_ERROR_BADARGUMENT; + if (!g_module_local_thread_adapter) + return PP_ERROR_FAILED; int32_t result = PP_ERROR_FAILED; - dispatcher->Send(new PpapiHostMsg_PPBFlashFile_ModuleLocal_QueryFile( - INTERFACE_ID_PPB_FLASH_FILE_MODULELOCAL, instance, path, info, &result)); + g_module_local_thread_adapter->Send(instance, + new PpapiHostMsg_PPBFlashFile_ModuleLocal_QueryFile( + INTERFACE_ID_PPB_FLASH_FILE_MODULELOCAL, instance, path, + info, &result)); return result; } int32_t GetModuleLocalDirContents(PP_Instance instance, const char* path, PP_DirContents_Dev** contents) { - PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); - if (!dispatcher) - return PP_ERROR_BADARGUMENT; + if (!g_module_local_thread_adapter) + return PP_ERROR_FAILED; int32_t result = PP_ERROR_FAILED; std::vector<SerializedDirEntry> entries; - dispatcher->Send(new PpapiHostMsg_PPBFlashFile_ModuleLocal_GetDirContents( - INTERFACE_ID_PPB_FLASH_FILE_MODULELOCAL, - instance, path, &entries, &result)); + g_module_local_thread_adapter->Send(instance, + new PpapiHostMsg_PPBFlashFile_ModuleLocal_GetDirContents( + INTERFACE_ID_PPB_FLASH_FILE_MODULELOCAL, + instance, path, &entries, &result)); if (result != PP_OK) return result; @@ -151,6 +490,8 @@ int32_t GetModuleLocalDirContents(PP_Instance instance, } const PPB_Flash_File_ModuleLocal flash_file_modulelocal_interface = { + &CreateThreadAdapterForInstance, + &ClearThreadAdapterForInstance, &OpenModuleLocalFile, &RenameModuleLocalFile, &DeleteModuleLocalFileOrDir, diff --git a/webkit/plugins/ppapi/ppb_flash_file_impl.cc b/webkit/plugins/ppapi/ppb_flash_file_impl.cc index 48e4ab0..cf657a9 100644 --- a/webkit/plugins/ppapi/ppb_flash_file_impl.cc +++ b/webkit/plugins/ppapi/ppb_flash_file_impl.cc @@ -72,6 +72,13 @@ void FreeDirContents(PP_Instance instance, PP_DirContents_Dev* contents) { namespace { +bool CreateThreadAdapterForInstance(PP_Instance instance) { + return false; // No multithreaded access allowed. +} + +void ClearThreadAdapterForInstance(PP_Instance instance) { +} + int32_t OpenModuleLocalFile(PP_Instance pp_instance, const char* path, int32_t mode, @@ -205,6 +212,8 @@ int32_t GetModuleLocalDirContents(PP_Instance pp_instance, } const PPB_Flash_File_ModuleLocal ppb_flash_file_modulelocal = { + &CreateThreadAdapterForInstance, + &ClearThreadAdapterForInstance, &OpenModuleLocalFile, &RenameModuleLocalFile, &DeleteModuleLocalFileOrDir, |