summaryrefslogtreecommitdiffstats
path: root/chrome_frame
diff options
context:
space:
mode:
authorstoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-10 00:18:27 +0000
committerstoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-02-10 00:18:27 +0000
commite655884873225a8b146ee2f992baafbfdcacb344 (patch)
tree98af614d241770030e081551d5c9998bd8b8cda7 /chrome_frame
parentae7593642c76769c2eb4c56428b861d1327f7a87 (diff)
downloadchromium_src-e655884873225a8b146ee2f992baafbfdcacb344.zip
chromium_src-e655884873225a8b146ee2f992baafbfdcacb344.tar.gz
chromium_src-e655884873225a8b146ee2f992baafbfdcacb344.tar.bz2
Fix crash. Happens in LaunchDelegate::LaunchComplete if delegate (ChromeFrameAutomationClient) is destroyed before launch callback is invoked. Happens when underlying automation proxy is going to be shared to new instance of ChromeFrameAutomationClient and we destroy that instance before receiving LaunchComplete callback.
Still have a race (and possible crash) for automation_server_ in ReleaseAutomationServer and LaunchComplete, TEST=test added. Review URL: http://codereview.chromium.org/582018 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@38551 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame')
-rw-r--r--chrome_frame/chrome_frame_automation.cc42
-rw-r--r--chrome_frame/chrome_frame_automation.h2
-rw-r--r--chrome_frame/test/proxy_factory_mock.cc40
3 files changed, 71 insertions, 13 deletions
diff --git a/chrome_frame/chrome_frame_automation.cc b/chrome_frame/chrome_frame_automation.cc
index dce4b78..f505ffe 100644
--- a/chrome_frame/chrome_frame_automation.cc
+++ b/chrome_frame/chrome_frame_automation.cc
@@ -14,6 +14,7 @@
#include "base/singleton.h"
#include "base/string_util.h"
#include "base/sys_info.h"
+#include "base/waitable_event.h"
#include "chrome/app/client_util.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
@@ -329,37 +330,54 @@ bool ProxyFactory::ReleaseAutomationServer(void* server_id) {
ProxyCacheEntry* entry = reinterpret_cast<ProxyCacheEntry*>(server_id);
+#ifndef NDEBUG
lock_.Acquire();
Vector::ContainerType::iterator it = std::find(proxies_.container().begin(),
proxies_.container().end(),
entry);
DCHECK(it != proxies_.container().end());
DCHECK(entry->thread->thread_id() != PlatformThread::CurrentId());
- if (--entry->ref_count == 0) {
- proxies_.container().erase(it);
- }
+ DCHECK_GT(entry->ref_count, 0);
lock_.Release();
+#endif
- // Destroy it.
+ base::WaitableEvent done(true, false);
+ entry->thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &ProxyFactory::ReleaseProxy, entry, &done));
+ done.Wait();
+
+ // Stop the thread and destroy the entry if there is no more clients.
if (entry->ref_count == 0) {
- entry->thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this,
- &ProxyFactory::DestroyProxy, entry));
- // Wait until thread exits
- entry->thread.reset();
DCHECK(entry->proxy == NULL);
+ entry->thread.reset();
delete entry;
}
return true;
}
-void ProxyFactory::DestroyProxy(ProxyCacheEntry* entry) {
+void ProxyFactory::ReleaseProxy(ProxyCacheEntry* entry,
+ base::WaitableEvent* done) {
DCHECK(entry->thread->thread_id() == PlatformThread::CurrentId());
+
+ lock_.Acquire();
+ if (!--entry->ref_count) {
+ Vector::ContainerType::iterator it = std::find(proxies_.container().begin(),
+ proxies_.container().end(),
+ entry);
+ proxies_->erase(it);
+ }
+ lock_.Release();
+
// Send pending UMA data if any.
- SendUMAData(entry);
- delete entry->proxy;
- entry->proxy = NULL;
+ if (!entry->ref_count) {
+ SendUMAData(entry);
+ delete entry->proxy;
+ entry->proxy = NULL;
+ }
+
+ done->Signal();
}
Singleton<ProxyFactory> g_proxy_factory;
diff --git a/chrome_frame/chrome_frame_automation.h b/chrome_frame/chrome_frame_automation.h
index 4cfa187..8b2f801 100644
--- a/chrome_frame/chrome_frame_automation.h
+++ b/chrome_frame/chrome_frame_automation.h
@@ -122,7 +122,7 @@ class ProxyFactory {
void CreateProxy(ProxyCacheEntry* entry,
const ChromeFrameLaunchParams& params,
LaunchDelegate* delegate);
- void DestroyProxy(ProxyCacheEntry* entry);
+ void ReleaseProxy(ProxyCacheEntry* entry, base::WaitableEvent* done);
void SendUMAData(ProxyCacheEntry* proxy_entry);
diff --git a/chrome_frame/test/proxy_factory_mock.cc b/chrome_frame/test/proxy_factory_mock.cc
index a50dc33..2a587f4 100644
--- a/chrome_frame/test/proxy_factory_mock.cc
+++ b/chrome_frame/test/proxy_factory_mock.cc
@@ -1,6 +1,7 @@
// Copyright (c) 2006-2010 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 "base/waitable_event.h"
#include "chrome_frame/test/proxy_factory_mock.h"
#define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING
@@ -84,3 +85,42 @@ TEST(ProxyFactoryTest, CreateDifferentProfiles) {
f.ReleaseAutomationServer(i1);
}
+TEST(ProxyFactoryTest, FastCreateDestroy) {
+ ProxyFactory f;
+ LaunchDelegateMock* d1 = new LaunchDelegateMock();
+ LaunchDelegateMock* d2 = new LaunchDelegateMock();
+
+ ChromeFrameLaunchParams params;
+ params.automation_server_launch_timeout = 10000;
+ params.profile_name = L"Dr. Gratiano Forbeson";
+ params.extra_chrome_arguments = L"";
+ params.perform_version_check = false;
+ params.incognito_mode = false;
+
+ void* i1 = NULL;
+ base::WaitableEvent launched(true, false);
+ EXPECT_CALL(*d1, LaunchComplete(testing::NotNull(), AUTOMATION_SUCCESS))
+ .Times(1)
+ .WillOnce(testing::InvokeWithoutArgs(&launched,
+ &base::WaitableEvent::Signal));
+ f.GetAutomationServer(d1, params, &i1);
+ // Wait for launch
+ ASSERT_TRUE(launched.TimedWait(base::TimeDelta::FromSeconds(10)));
+
+ // Expect second launch to succeed too
+ EXPECT_CALL(*d2, LaunchComplete(testing::NotNull(), AUTOMATION_SUCCESS))
+ .Times(1);
+
+ // Boost thread priority so we call ReleaseAutomationServer before
+ // LaunchComplete callback have a chance to be executed.
+ ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
+ void* i2 = NULL;
+ f.GetAutomationServer(d2, params, &i2);
+ EXPECT_EQ(i1, i2);
+ f.ReleaseAutomationServer(i2);
+ delete d2;
+
+ ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_NORMAL);
+ f.ReleaseAutomationServer(i1);
+ delete d1;
+}