summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjcampan@google.com <jcampan@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-12 01:25:41 +0000
committerjcampan@google.com <jcampan@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-08-12 01:25:41 +0000
commitd65cab7ac310ea12c5d946ff40242a243ce911da (patch)
treefe3eaa4a4e6f14a7416c4b4da351e18cd34d34b2
parent1a48f315b0ca5c26c4446070edfb5842ed06c8c7 (diff)
downloadchromium_src-d65cab7ac310ea12c5d946ff40242a243ce911da.zip
chromium_src-d65cab7ac310ea12c5d946ff40242a243ce911da.tar.gz
chromium_src-d65cab7ac310ea12c5d946ff40242a243ce911da.tar.bz2
Enabling sync_channel in the browser to allow accessibility code making blocking calls. This replaces my previous CL that was somehow duplicating some of these functionalities.
BUG=None TEST=Run the unit tests. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@691 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/browser_process.h4
-rw-r--r--chrome/browser/browser_process_impl.cc5
-rw-r--r--chrome/browser/browser_process_impl.h6
-rw-r--r--chrome/browser/browser_shutdown.cc4
-rw-r--r--chrome/browser/render_process_host.cc12
-rw-r--r--chrome/browser/render_process_host.h7
-rw-r--r--chrome/common/ipc_channel_proxy.cc14
-rw-r--r--chrome/common/ipc_channel_proxy.h5
-rw-r--r--chrome/common/ipc_sync_channel.cc79
-rw-r--r--chrome/common/ipc_sync_channel.h24
-rw-r--r--chrome/common/ipc_sync_channel_unittest.cc105
-rw-r--r--chrome/common/ipc_sync_message.cc7
-rw-r--r--chrome/common/ipc_sync_message.h4
-rw-r--r--chrome/plugin/plugin_channel_base.cc6
-rw-r--r--chrome/plugin/plugin_thread.cc3
-rw-r--r--chrome/renderer/render_thread.cc3
-rw-r--r--chrome/test/testing_browser_process.h8
17 files changed, 254 insertions, 42 deletions
diff --git a/chrome/browser/browser_process.h b/chrome/browser/browser_process.h
index 1ca493c..c0eb6d8 100644
--- a/chrome/browser/browser_process.h
+++ b/chrome/browser/browser_process.h
@@ -53,6 +53,7 @@ class ResourceDispatcherHost;
class DebuggerWrapper;
class Thread;
class WebAppInstallerService;
+class SharedEvent;
class SuspendController;
namespace sandbox {
@@ -146,6 +147,9 @@ class BrowserProcess {
// TODO(beng): remove once XPFrame/VistaFrame are gone.
virtual bool IsUsingNewFrames() = 0;
+ // Returns an event that is signaled when the browser shutdown.
+ virtual HANDLE shutdown_event() = 0;
+
private:
DISALLOW_EVIL_CONSTRUCTORS(BrowserProcess);
};
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 20e0c37..ad12c305 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -143,6 +143,8 @@ BrowserProcessImpl::BrowserProcessImpl(CommandLine& command_line)
}
suspend_controller_ = new SuspendController();
+
+ shutdown_event_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
}
BrowserProcessImpl::~BrowserProcessImpl() {
@@ -224,6 +226,9 @@ struct RunnableMethodTraits<MessageLoop> {
};
void BrowserProcessImpl::EndSession() {
+ // Notify we are going away.
+ ::SetEvent(shutdown_event_);
+
// Mark all the profiles as clean.
ProfileManager* pm = profile_manager();
for (ProfileManager::const_iterator i = pm->begin(); i != pm->end(); ++i)
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index 3683277..7d12469 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -43,6 +43,7 @@
#include "base/non_thread_safe.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
+#include "base/shared_event.h"
#include "chrome/browser/automation/automation_provider_list.h"
#include "chrome/browser/browser_process.h"
#include "sandbox/src/sandbox.h"
@@ -207,6 +208,8 @@ class BrowserProcessImpl : public BrowserProcess, public NonThreadSafe {
return using_new_frames_;
}
+ virtual HANDLE shutdown_event() { return shutdown_event_; }
+
private:
void CreateResourceDispatcherHost();
void CreatePrefService();
@@ -281,6 +284,9 @@ class BrowserProcessImpl : public BrowserProcess, public NonThreadSafe {
bool checked_for_new_frames_;
bool using_new_frames_;
+ // An event that notifies when we are shutting-down.
+ HANDLE shutdown_event_;
+
DISALLOW_EVIL_CONSTRUCTORS(BrowserProcessImpl);
};
diff --git a/chrome/browser/browser_shutdown.cc b/chrome/browser/browser_shutdown.cc
index e405453..f710c33 100644
--- a/chrome/browser/browser_shutdown.cc
+++ b/chrome/browser/browser_shutdown.cc
@@ -32,6 +32,7 @@
#include "base/file_util.h"
#include "base/histogram.h"
#include "base/path_service.h"
+#include "base/shared_event.h"
#include "base/string_util.h"
#include "base/time.h"
#include "chrome/browser/browser_process.h"
@@ -106,6 +107,9 @@ void Shutdown() {
// consider putting it in BrowserProcessImpl::EndSession.
DCHECK(g_browser_process);
+ // Notifies we are going away.
+ ::SetEvent(g_browser_process->shutdown_event());
+
PluginService* plugin_service = PluginService::GetInstance();
if (plugin_service) {
plugin_service->Shutdown();
diff --git a/chrome/browser/render_process_host.cc b/chrome/browser/render_process_host.cc
index 4da6cad..be36149 100644
--- a/chrome/browser/render_process_host.cc
+++ b/chrome/browser/render_process_host.cc
@@ -44,6 +44,7 @@
#include "base/logging.h"
#include "base/path_service.h"
#include "base/process_util.h"
+#include "base/shared_event.h"
#include "base/shared_memory.h"
#include "base/string_util.h"
#include "base/thread.h"
@@ -241,9 +242,14 @@ bool RenderProcessHost::Init() {
// setup IPC channel
std::wstring channel_id = GenerateRandomChannelID(this);
channel_.reset(
- new IPC::ChannelProxy(channel_id, IPC::Channel::MODE_SERVER, this,
- resource_message_filter,
- io_thread->message_loop()));
+ new IPC::SyncChannel(channel_id, IPC::Channel::MODE_SERVER, this,
+ resource_message_filter,
+ io_thread->message_loop(), true,
+ g_browser_process->shutdown_event()));
+ // As a preventive mesure, we DCHECK if someone sends a synchronous message
+ // with no time-out, which in the context of the browser process we should not
+ // be doing.
+ channel_->set_sync_messages_with_no_timeout_allowed(false);
// build command line for renderer, we have to quote the executable name to
// deal with spaces
diff --git a/chrome/browser/render_process_host.h b/chrome/browser/render_process_host.h
index e64b8fe..d29ba07 100644
--- a/chrome/browser/render_process_host.h
+++ b/chrome/browser/render_process_host.h
@@ -38,8 +38,9 @@
#include "base/object_watcher.h"
#include "base/process.h"
#include "base/ref_counted.h"
+#include "base/scoped_handle.h"
#include "base/scoped_ptr.h"
-#include "chrome/common/ipc_channel_proxy.h"
+#include "chrome/common/ipc_sync_channel.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/rand_util.h"
#include "chrome/common/render_messages.h"
@@ -118,7 +119,7 @@ class RenderProcessHost : public IPC::Channel::Listener,
void ReportExpectingClose(int32 listener_id);
// getters, these may return NULL if there is no connection
- IPC::ChannelProxy* channel() {
+ IPC::SyncChannel* channel() {
return channel_.get();
}
HANDLE process() {
@@ -240,7 +241,7 @@ class RenderProcessHost : public IPC::Channel::Listener,
// A proxy for our IPC::Channel that lives on the IO thread (see
// browser_process.h)
- scoped_ptr<IPC::ChannelProxy> channel_;
+ scoped_ptr<IPC::SyncChannel> channel_;
// Our renderer process.
Process process_;
diff --git a/chrome/common/ipc_channel_proxy.cc b/chrome/common/ipc_channel_proxy.cc
index 9d6b473..2cddc8e 100644
--- a/chrome/common/ipc_channel_proxy.cc
+++ b/chrome/common/ipc_channel_proxy.cc
@@ -55,8 +55,7 @@ void ChannelProxy::Context::CreateChannel(const std::wstring& id,
channel_ = new Channel(id, mode, this);
}
-// Called on the IPC::Channel thread
-void ChannelProxy::Context::OnMessageReceived(const Message& message) {
+bool ChannelProxy::Context::TryFilters(const Message& message) {
#ifdef IPC_MESSAGE_LOG_ENABLED
Logging* logger = Logging::current();
if (logger->Enabled())
@@ -69,9 +68,17 @@ void ChannelProxy::Context::OnMessageReceived(const Message& message) {
if (logger->Enabled())
logger->OnPostDispatchMessage(message, channel_id_);
#endif
- return;
+ return true;
}
}
+ return false;
+}
+
+// Called on the IPC::Channel thread
+void ChannelProxy::Context::OnMessageReceived(const Message& message) {
+ // First give a chance to the filters to process this message.
+ if (TryFilters(message))
+ return;
// NOTE: This code relies on the listener's message loop not going away while
// this thread is active. That should be a reasonable assumption, but it
@@ -217,7 +224,6 @@ ChannelProxy::ChannelProxy(const std::wstring& channel_id, Channel::Mode mode,
}
ChannelProxy::ChannelProxy(const std::wstring& channel_id, Channel::Mode mode,
- Channel::Listener* listener, MessageFilter* filter,
MessageLoop* ipc_thread, Context* context,
bool create_pipe_now)
: context_(context) {
diff --git a/chrome/common/ipc_channel_proxy.h b/chrome/common/ipc_channel_proxy.h
index ca8abb4..82b3d88 100644
--- a/chrome/common/ipc_channel_proxy.h
+++ b/chrome/common/ipc_channel_proxy.h
@@ -149,7 +149,6 @@ class ChannelProxy : public Message::Sender {
// to the internal state. If create_pipe_now is true, the pipe is created
// immediately. Otherwise it's created on the IO thread.
ChannelProxy(const std::wstring& channel_id, Channel::Mode mode,
- Channel::Listener* listener, MessageFilter* filter,
MessageLoop* ipc_thread_loop, Context* context,
bool create_pipe_now);
@@ -170,6 +169,10 @@ class ChannelProxy : public Message::Sender {
Channel::Listener* listener() const { return listener_; }
const std::wstring& channel_id() const { return channel_id_; }
+ // Gives the filters a chance at processing |message|.
+ // Returns true if the message was processed, false otherwise.
+ bool TryFilters(const Message& message);
+
private:
friend class ChannelProxy;
// Create the Channel
diff --git a/chrome/common/ipc_sync_channel.cc b/chrome/common/ipc_sync_channel.cc
index 897e7bf..e01759a 100644
--- a/chrome/common/ipc_sync_channel.cc
+++ b/chrome/common/ipc_sync_channel.cc
@@ -70,7 +70,6 @@ class SyncChannel::ReceivedSyncMsgQueue :
~ReceivedSyncMsgQueue() {
CloseHandle(blocking_event_);
- ThreadLocalStorage::Set(g_tls_index, NULL);
}
// Called on IPC thread when a synchronous message or reply arrives.
@@ -252,13 +251,20 @@ SyncChannel::SyncContext::SyncContext(
// Stash a pointer to the listener thread's ReceivedSyncMsgQueue, as we
// need to be able to access it in the IPC thread.
received_sync_msgs_ = new ReceivedSyncMsgQueue();
+ // TODO(jcampan): http:///b/1319842 we are adding an extra-ref to keep this
+ // object around for the duration of the process. We used to remove it from
+ // the TLS when it was destroyed, but that was causing problems as we could
+ // be destroyed in a different thread then the thread we had been created
+ // on. This needs to be revisited at some point.
+ received_sync_msgs_->AddRef();
+
ThreadLocalStorage::Set(g_tls_index, received_sync_msgs_.get());
}
}
SyncChannel::SyncContext::~SyncContext() {
while (!deserializers_.empty())
- PopDeserializer();
+ PopDeserializer(true);
}
// Adds information about an outgoing sync message to the context so that
@@ -297,7 +303,7 @@ bool SyncChannel::SyncContext::UnblockListener(const Message* msg) {
reply_deserialize_result_ = false;
if (!deserializers_.empty()) {
reply_event = deserializers_.top().reply_event;
- PopDeserializer();
+ PopDeserializer(false);
}
} else {
if (deserializers_.empty())
@@ -317,7 +323,7 @@ bool SyncChannel::SyncContext::UnblockListener(const Message* msg) {
// Can't CloseHandle the event just yet, since doing so might cause the
// Wait call above to never return.
reply_event = deserializers_.top().reply_event;
- PopDeserializer();
+ PopDeserializer(false);
}
}
@@ -336,6 +342,10 @@ bool SyncChannel::SyncContext::UnblockListener(const Message* msg) {
// Called on the IPC thread.
void SyncChannel::SyncContext::OnMessageReceived(const Message& msg) {
+ // Give the filters a chance at processing this message.
+ if (TryFilters(msg))
+ return;
+
if (UnblockListener(&msg))
return;
@@ -360,19 +370,23 @@ void SyncChannel::SyncContext::OnChannelError() {
Context::OnChannelError();
}
-void SyncChannel::SyncContext::PopDeserializer() {
- delete deserializers_.top().deserializer;
+void SyncChannel::SyncContext::PopDeserializer(bool close_reply_event) {
+ PendingSyncMsg msg = deserializers_.top();
+ delete msg.deserializer;
+ if (close_reply_event)
+ CloseHandle(msg.reply_event);
deserializers_.pop();
}
SyncChannel::SyncChannel(const std::wstring& channel_id, Channel::Mode mode,
- Channel::Listener* listener,
+ Channel::Listener* listener, MessageFilter* filter,
MessageLoop* ipc_message_loop,
- bool create_pipe_now)
- : ChannelProxy(channel_id, mode, listener, NULL, ipc_message_loop,
- new SyncContext(listener, NULL, ipc_message_loop),
+ bool create_pipe_now, HANDLE shutdown_event)
+ : ChannelProxy(channel_id, mode, ipc_message_loop,
+ new SyncContext(listener, filter, ipc_message_loop),
create_pipe_now),
- shutdown_event_(ChildProcess::GetShutDownEvent()) {
+ shutdown_event_(shutdown_event),
+ sync_messages_with_no_timeout_allowed_(true) {
DCHECK(shutdown_event_ != NULL);
}
@@ -385,11 +399,16 @@ SyncChannel::~SyncChannel() {
}
bool SyncChannel::Send(IPC::Message* message) {
+ return SendWithTimeout(message, INFINITE);
+}
+
+bool SyncChannel::SendWithTimeout(IPC::Message* message, int timeout_ms) {
bool message_is_sync = message->is_sync();
HANDLE pump_messages_event = NULL;
HANDLE reply_event = NULL;
if (message_is_sync) {
+ DCHECK(sync_messages_with_no_timeout_allowed_ || timeout_ms != INFINITE);
IPC::SyncMessage* sync_msg = static_cast<IPC::SyncMessage*>(message);
reply_event = sync_context()->Push(sync_msg);
pump_messages_event = sync_msg->pump_messages_event();
@@ -409,24 +428,32 @@ bool SyncChannel::Send(IPC::Message* message) {
pump_messages_event};
DWORD result;
+ TimeTicks before = TimeTicks::Now();
if (pump_messages_event == NULL) {
// No need to pump messages since we didn't get an event to check.
- result = WaitForMultipleObjects(3, objects, FALSE, INFINITE);
+ result = WaitForMultipleObjects(3, objects, FALSE, timeout_ms);
} else {
// If the event is set, then we pump messages. Otherwise we also wait on
// it so that if it gets set we start pumping messages.
if (WaitForSingleObject(pump_messages_event, 0) == WAIT_OBJECT_0) {
- result = MsgWaitForMultipleObjects(3, objects, FALSE, INFINITE,
- QS_ALLINPUT);
+ // Before calling MsgWaitForMultipleObjects() we check that our events
+ // are not signaled. The windows message queue might always have events
+ // starving the checking of our events otherwise.
+ result = WaitForMultipleObjects(3, objects, FALSE, 0);
+ if (result == WAIT_TIMEOUT) {
+ result = MsgWaitForMultipleObjects(3, objects, FALSE, timeout_ms,
+ QS_ALLINPUT);
+ }
} else {
- result = WaitForMultipleObjects(4, objects, FALSE, INFINITE);
+ result = WaitForMultipleObjects(4, objects, FALSE, timeout_ms);
}
}
- if (result == WAIT_OBJECT_0) {
- // Process shut down before we can get a reply to a synchronous message.
- // Unblock the thread.
- // Leak reply_event. Since we're shutting down, it's not a big deal.
+ if (result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT) {
+ // Process shut down before we can get a reply to a synchronous message,
+ // or timed-out. Unblock the thread.
+ AutoLock auto_lock(*(sync_context()->deserializers_lock()));
+ sync_context()->PopDeserializer(true);
return false;
}
@@ -458,7 +485,19 @@ bool SyncChannel::Send(IPC::Message* message) {
// MsgWaitForMultipleObjects instead.
}
- // Continue looping until we get the reply to our synchronous message.
+ if (timeout_ms != INFINITE) {
+ TimeDelta time_delta = TimeTicks::Now() - before;
+ timeout_ms -= static_cast<int>(time_delta.InMilliseconds());
+ if (timeout_ms <= 0) {
+ // We timed-out while processing messages.
+ AutoLock auto_lock(*(sync_context()->deserializers_lock()));
+ sync_context()->PopDeserializer(true);
+ return false;
+ }
+ }
+
+ // Continue looping until we either get the reply to our synchronous message
+ // or we time-out.
} while (true);
}
diff --git a/chrome/common/ipc_sync_channel.h b/chrome/common/ipc_sync_channel.h
index af6e153..bd473d4 100644
--- a/chrome/common/ipc_sync_channel.h
+++ b/chrome/common/ipc_sync_channel.h
@@ -52,13 +52,20 @@ class SyncMessage;
class SyncChannel : public ChannelProxy {
public:
SyncChannel(const std::wstring& channel_id, Channel::Mode mode,
- Channel::Listener* listener, MessageLoop* ipc_message_loop,
- bool create_pipe_now);
+ Channel::Listener* listener, MessageFilter* filter,
+ MessageLoop* ipc_message_loop, bool create_pipe_now,
+ HANDLE shutdown_handle);
~SyncChannel();
virtual bool Send(Message* message);
+ virtual bool SendWithTimeout(Message* message, int timeout_ms);
bool UnblockListener(Message* message);
+ // Whether we allow sending messages with no time-out.
+ void set_sync_messages_with_no_timeout_allowed(bool value) {
+ sync_messages_with_no_timeout_allowed_ = value;
+ }
+
protected:
class ReceivedSyncMsgQueue;
friend class ReceivedSyncMsgQueue;
@@ -95,6 +102,14 @@ class SyncChannel : public ChannelProxy {
// Otherwise the function returns false.
bool UnblockListener(const Message* msg);
+
+ // Cleanly remove the top deserializer (and throw it away).
+ // You need to acquire the deserializers_lock before calling this.
+ void PopDeserializer(bool close_reply_event);
+
+ // Returns the lock that should be acquired before calling PopDeserializer.
+ Lock* deserializers_lock() { return &deserializers_lock_; }
+
private:
void OnMessageReceived(const Message& msg);
void OnChannelError();
@@ -109,9 +124,6 @@ class SyncChannel : public ChannelProxy {
HANDLE reply_event;
};
- // Cleanly remove the top deserializer (and throw it away).
- void PopDeserializer();
-
typedef std::stack<PendingSyncMsg> PendingSyncMessageQueue;
PendingSyncMessageQueue deserializers_;
Lock deserializers_lock_;
@@ -130,6 +142,8 @@ class SyncChannel : public ChannelProxy {
std::stack<HANDLE> pump_messages_events_;
+ bool sync_messages_with_no_timeout_allowed_;
+
DISALLOW_EVIL_CONSTRUCTORS(SyncChannel);
};
diff --git a/chrome/common/ipc_sync_channel_unittest.cc b/chrome/common/ipc_sync_channel_unittest.cc
index 74f046a..78d825c 100644
--- a/chrome/common/ipc_sync_channel_unittest.cc
+++ b/chrome/common/ipc_sync_channel_unittest.cc
@@ -116,6 +116,9 @@ class Worker : public Channel::Listener, public Message::Sender {
void AddRef() { }
void Release() { }
bool Send(Message* msg) { return channel_->Send(msg); }
+ bool SendWithTimeout(Message* msg, int timeout_ms) {
+ return channel_->SendWithTimeout(msg, timeout_ms);
+ }
void WaitForChannelCreation() { channel_created_.Wait(); }
void CloseChannel() { channel_.reset(); }
void Start() {
@@ -158,7 +161,8 @@ class Worker : public Channel::Listener, public Message::Sender {
ipc_thread_.Start();
// Link ipc_thread_, listener_thread_ and channel_ altogether.
channel_.reset(new SyncChannel(
- channel_name_, mode_, this, ipc_thread_.message_loop(), true));
+ channel_name_, mode_, this, NULL, ipc_thread_.message_loop(), true,
+ TestProcess::GetShutDownEvent()));
channel_created_.Set();
Run();
}
@@ -623,3 +627,102 @@ TEST(IPCSyncChannelTest, ChattyServer) {
workers.push_back(new ChattyRecursiveClient());
RunTest(workers);
}
+
+
+//------------------------------------------------------------------------------
+class TimeoutServer : public Worker {
+ public:
+ TimeoutServer(int timeout_ms,
+ std::vector<bool> timeout_seq)
+ : Worker(Channel::MODE_SERVER, "timeout_server"),
+ timeout_ms_(timeout_ms),
+ timeout_seq_(timeout_seq) {
+ }
+
+ void Run() {
+ for (std::vector<bool>::const_iterator iter = timeout_seq_.begin();
+ iter != timeout_seq_.end(); ++iter) {
+ int answer = 0;
+ bool result =
+ SendWithTimeout(new SyncChannelTestMsg_AnswerToLife(&answer),
+ timeout_ms_);
+ if (*iter) {
+ // Time-out expected.
+ DCHECK(!result);
+ DCHECK(answer == 0);
+ } else {
+ DCHECK(result);
+ DCHECK(answer == 42);
+ }
+ }
+ Done();
+ }
+
+ private:
+ int timeout_ms_;
+ std::vector<bool> timeout_seq_;
+};
+
+class UnresponsiveClient : public Worker {
+ public:
+ UnresponsiveClient(std::vector<bool> timeout_seq)
+ : Worker(Channel::MODE_CLIENT, "unresponsive_client"),
+ timeout_seq_(timeout_seq) {
+ }
+
+ void OnAnswerDelay(Message* reply_msg) {
+ DCHECK(!timeout_seq_.empty());
+ if (!timeout_seq_[0]) {
+ SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42);
+ Send(reply_msg);
+ } else {
+ // Don't reply.
+ }
+ timeout_seq_.erase(timeout_seq_.begin());
+ if (timeout_seq_.empty())
+ Done();
+ }
+
+ private:
+ // Whether we should time-out or respond to the various messages we receive.
+ std::vector<bool> timeout_seq_;
+};
+
+// Tests that SendWithTimeout does not time-out if the response comes back fast
+// enough.
+TEST(IPCSyncChannelTest, SendWithTimeoutOK) {
+ std::vector<Worker*> workers;
+ std::vector<bool> timeout_seq;
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(false);
+ workers.push_back(new TimeoutServer(5000, timeout_seq));
+ workers.push_back(new SimpleClient());
+ RunTest(workers);
+}
+
+// Tests that SendWithTimeout does time-out.
+TEST(IPCSyncChannelTest, SendWithTimeoutTimeout) {
+ std::vector<Worker*> workers;
+ std::vector<bool> timeout_seq;
+ timeout_seq.push_back(true);
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(false);
+ workers.push_back(new TimeoutServer(100, timeout_seq));
+ workers.push_back(new UnresponsiveClient(timeout_seq));
+ RunTest(workers);
+}
+
+// Sends some message that time-out and some that succeed.
+TEST(IPCSyncChannelTest, SendWithTimeoutMixedOKAndTimeout) {
+ std::vector<Worker*> workers;
+ std::vector<bool> timeout_seq;
+ timeout_seq.push_back(true);
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(false);
+ timeout_seq.push_back(true);
+ timeout_seq.push_back(false);
+ workers.push_back(new TimeoutServer(100, timeout_seq));
+ workers.push_back(new UnresponsiveClient(timeout_seq));
+ RunTest(workers);
+}
diff --git a/chrome/common/ipc_sync_message.cc b/chrome/common/ipc_sync_message.cc
index 43d6d5e..8dc75c4 100644
--- a/chrome/common/ipc_sync_message.cc
+++ b/chrome/common/ipc_sync_message.cc
@@ -38,6 +38,8 @@ namespace IPC {
uint32 SyncMessage::next_id_ = 0;
#define kSyncMessageHeaderSize 4
+// A dummy handle used by EnableMessagePumping.
+HANDLE dummy_event = ::CreateEvent(NULL, TRUE, TRUE, NULL);
SyncMessage::SyncMessage(
int32 routing_id,
@@ -63,6 +65,11 @@ MessageReplyDeserializer* SyncMessage::GetReplyDeserializer() {
return rv;
}
+void SyncMessage::EnableMessagePumping() {
+ DCHECK(!pump_messages_event_);
+ set_pump_messages_event(dummy_event);
+}
+
bool SyncMessage::IsMessageReplyTo(const Message& msg, int request_id) {
if (!msg.is_reply())
return false;
diff --git a/chrome/common/ipc_sync_message.h b/chrome/common/ipc_sync_message.h
index bcf47cbe..8ab6cae 100644
--- a/chrome/common/ipc_sync_message.h
+++ b/chrome/common/ipc_sync_message.h
@@ -63,6 +63,10 @@ class SyncMessage : public Message {
}
}
+ // Call this if you always want to pump messages. You can call this method
+ // or set_pump_messages_event but not both.
+ void EnableMessagePumping();
+
HANDLE pump_messages_event() const { return pump_messages_event_; }
// Returns true if the message is a reply to the given request id.
diff --git a/chrome/plugin/plugin_channel_base.cc b/chrome/plugin/plugin_channel_base.cc
index 1cb3111..fec5bd4 100644
--- a/chrome/plugin/plugin_channel_base.cc
+++ b/chrome/plugin/plugin_channel_base.cc
@@ -33,6 +33,7 @@
#include "chrome/plugin/plugin_channel_base.h"
#include "chrome/common/ipc_sync_message.h"
+#include "chrome/plugin/plugin_process.h"
typedef stdext::hash_map<std::wstring, scoped_refptr<PluginChannelBase> >
PluginChannelMap;
@@ -100,8 +101,9 @@ void PluginChannelBase::CleanupChannels() {
bool PluginChannelBase::Init(MessageLoop* ipc_message_loop,
bool create_pipe_now) {
- channel_.reset(new IPC::SyncChannel(channel_name_, mode_, this,
- ipc_message_loop, create_pipe_now));
+ channel_.reset(new IPC::SyncChannel(channel_name_, mode_, this, NULL,
+ ipc_message_loop, create_pipe_now,
+ PluginProcess::GetShutDownEvent()));
channel_valid_ = true;
return true;
}
diff --git a/chrome/plugin/plugin_thread.cc b/chrome/plugin/plugin_thread.cc
index 3c117df..199ab2c 100644
--- a/chrome/plugin/plugin_thread.cc
+++ b/chrome/plugin/plugin_thread.cc
@@ -92,7 +92,8 @@ void PluginThread::Init() {
PatchNPNFunctions();
CoInitialize(NULL);
channel_.reset(new IPC::SyncChannel(channel_name_,
- IPC::Channel::MODE_CLIENT, this, owner_loop_, true));
+ IPC::Channel::MODE_CLIENT, this, NULL, owner_loop_, true,
+ PluginProcess::GetShutDownEvent()));
notification_service_.reset(new NotificationService);
resource_dispatcher_ = new ResourceDispatcher(this);
diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc
index 4288377..b61e1eb 100644
--- a/chrome/renderer/render_thread.cc
+++ b/chrome/renderer/render_thread.cc
@@ -113,7 +113,8 @@ void RenderThread::Init() {
new ScopedRunnableMethodFactory<RenderThread>(this));
channel_.reset(new IPC::SyncChannel(channel_name_,
- IPC::Channel::MODE_CLIENT, this, owner_loop_, true));
+ IPC::Channel::MODE_CLIENT, this, NULL, owner_loop_, true,
+ RenderProcess::GetShutDownEvent()));
ThreadLocalStorage::Set(tls_index_, this);
diff --git a/chrome/test/testing_browser_process.h b/chrome/test/testing_browser_process.h
index 650aa5b..5048022 100644
--- a/chrome/test/testing_browser_process.h
+++ b/chrome/test/testing_browser_process.h
@@ -37,6 +37,7 @@
#include <string>
+#include "base/shared_event.h"
#include "base/string_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/notification_service.h"
@@ -44,7 +45,9 @@
class TestingBrowserProcess : public BrowserProcess {
public:
- TestingBrowserProcess() {}
+ TestingBrowserProcess() {
+ shutdown_event_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+ }
virtual ~TestingBrowserProcess() {
}
@@ -145,8 +148,11 @@ class TestingBrowserProcess : public BrowserProcess {
virtual bool IsUsingNewFrames() { return false; }
+ virtual HANDLE shutdown_event() { return shutdown_event_; }
+
private:
NotificationService notification_service_;
+ HANDLE shutdown_event_;
DISALLOW_EVIL_CONSTRUCTORS(TestingBrowserProcess);
};