Bug 1115438: Move PluginModuleParent's invocation of CreateToolhelp32Snapshot off the main thread; r=jimm

This commit is contained in:
Aaron Klotz 2015-01-08 17:38:49 -07:00
parent 14d009b649
commit 56c951c61f
4 changed files with 119 additions and 16 deletions

View File

@ -37,7 +37,6 @@
#ifdef XP_WIN
#include "mozilla/widget/AudioSession.h"
#include "nsWindowsHelpers.h"
#include "PluginHangUIParent.h"
#endif
@ -114,6 +113,63 @@ mozilla::plugins::SetupBridge(uint32_t aPluginId,
return PPluginModule::Bridge(aContentParent, chromeParent);
}
/**
* Use for executing CreateToolhelp32Snapshot off main thread
*/
class mozilla::plugins::FinishInjectorInitTask : public CancelableTask
{
public:
FinishInjectorInitTask()
: mMutex("FlashInjectorInitTask::mMutex")
, mParent(nullptr)
, mMainThreadMsgLoop(MessageLoop::current())
{
MOZ_ASSERT(NS_IsMainThread());
}
void Init(PluginModuleChromeParent* aParent)
{
MOZ_ASSERT(aParent);
mParent = aParent;
}
void PostToMainThread()
{
mSnapshot.own(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
bool deleteThis = false;
{ // Scope for lock
mozilla::MutexAutoLock lock(mMutex);
if (mMainThreadMsgLoop) {
mMainThreadMsgLoop->PostTask(FROM_HERE, this);
} else {
deleteThis = true;
}
}
if (deleteThis) {
delete this;
}
}
void Run() MOZ_OVERRIDE
{
mParent->DoInjection(mSnapshot);
}
void Cancel() MOZ_OVERRIDE
{
mozilla::MutexAutoLock lock(mMutex);
mMainThreadMsgLoop = nullptr;
}
private:
mozilla::Mutex mMutex;
nsAutoHandle mSnapshot;
PluginModuleChromeParent* mParent;
MessageLoop* mMainThreadMsgLoop;
};
namespace {
/**
* Objects of this class remain linked until either an error occurs in the
* plugin initialization sequence, or until
@ -264,6 +320,8 @@ PRCList PluginModuleMapping::sModuleListHead =
bool PluginModuleMapping::sIsLoadModuleOnStack = false;
} // anonymous namespace
void
mozilla::plugins::TerminatePlugin(uint32_t aPluginId)
{
@ -586,6 +644,7 @@ PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath, uint32
#ifdef MOZ_CRASHREPORTER_INJECTOR
, mFlashProcess1(0)
, mFlashProcess2(0)
, mFinishInitTask(nullptr)
#endif
, mInitOnAsyncConnect(false)
, mAsyncInitRv(NS_ERROR_NOT_INITIALIZED)
@ -633,6 +692,10 @@ PluginModuleChromeParent::~PluginModuleChromeParent()
UnregisterInjectorCallback(mFlashProcess1);
if (mFlashProcess2)
UnregisterInjectorCallback(mFlashProcess2);
if (mFinishInitTask) {
// mFinishInitTask will be deleted by the main thread message_loop
mFinishInitTask->Cancel();
}
#endif
UnregisterSettingsCallbacks();
@ -2606,22 +2669,41 @@ PluginModuleChromeParent::InitializeInjector()
return;
TimeStamp th32Start = TimeStamp::Now();
nsAutoHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
if (INVALID_HANDLE_VALUE == snapshot)
mFinishInitTask = mChromeTaskFactory.NewTask<FinishInjectorInitTask>();
mFinishInitTask->Init(this);
if (!::QueueUserWorkItem(&PluginModuleChromeParent::GetToolhelpSnapshot,
mFinishInitTask, WT_EXECUTEDEFAULT)) {
delete mFinishInitTask;
mFinishInitTask = nullptr;
return;
}
TimeStamp th32End = TimeStamp::Now();
mTimeBlocked += (th32End - th32Start);
}
void
PluginModuleChromeParent::DoInjection(const nsAutoHandle& aSnapshot)
{
DWORD pluginProcessPID = GetProcessId(Process()->GetChildProcessHandle());
mFlashProcess1 = GetFlashChildOfPID(pluginProcessPID, snapshot);
mFlashProcess1 = GetFlashChildOfPID(pluginProcessPID, aSnapshot);
if (mFlashProcess1) {
InjectCrashReporterIntoProcess(mFlashProcess1, this);
mFlashProcess2 = GetFlashChildOfPID(mFlashProcess1, snapshot);
mFlashProcess2 = GetFlashChildOfPID(mFlashProcess1, aSnapshot);
if (mFlashProcess2) {
InjectCrashReporterIntoProcess(mFlashProcess2, this);
}
}
mFinishInitTask = nullptr;
}
DWORD WINAPI
PluginModuleChromeParent::GetToolhelpSnapshot(LPVOID aContext)
{
FinishInjectorInitTask* task = static_cast<FinishInjectorInitTask*>(aContext);
MOZ_ASSERT(task);
task->PostToMainThread();
return 0;
}
void

View File

@ -11,11 +11,11 @@
#include "mozilla/FileUtils.h"
#include "mozilla/HangMonitor.h"
#include "mozilla/PluginLibrary.h"
#include "mozilla/plugins/ScopedMethodFactory.h"
#include "mozilla/plugins/PluginProcessParent.h"
#include "mozilla/plugins/PPluginModuleParent.h"
#include "mozilla/plugins/PluginMessageUtils.h"
#include "mozilla/plugins/PluginTypes.h"
#include "mozilla/plugins/TaskFactory.h"
#include "mozilla/TimeStamp.h"
#include "npapi.h"
#include "npfunctions.h"
@ -23,6 +23,9 @@
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsIObserver.h"
#ifdef XP_WIN
#include "nsWindowsHelpers.h"
#endif
#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
@ -46,6 +49,9 @@ class PluginInstanceParent;
#ifdef XP_WIN
class PluginHangUIParent;
#endif
#ifdef MOZ_CRASHREPORTER_INJECTOR
class FinishInjectorInitTask;
#endif
/**
* PluginModuleParent
@ -273,7 +279,7 @@ protected:
NPNetscapeFuncs* mNPNIface;
NPPluginFuncs* mNPPIface;
nsNPAPIPlugin* mPlugin;
ScopedMethodFactory<PluginModuleParent> mTaskFactory;
TaskFactory<PluginModuleParent> mTaskFactory;
nsString mPluginDumpID;
nsString mBrowserDumpID;
nsString mHangID;
@ -445,7 +451,7 @@ private:
PluginProcessParent* mSubprocess;
uint32_t mPluginId;
ScopedMethodFactory<PluginModuleChromeParent> mChromeTaskFactory;
TaskFactory<PluginModuleChromeParent> mChromeTaskFactory;
enum HangAnnotationFlags
{
@ -493,12 +499,17 @@ private:
friend class mozilla::plugins::PluginAsyncSurrogate;
#ifdef MOZ_CRASHREPORTER_INJECTOR
friend class mozilla::plugins::FinishInjectorInitTask;
void InitializeInjector();
void DoInjection(const nsAutoHandle& aSnapshot);
static DWORD WINAPI GetToolhelpSnapshot(LPVOID aContext);
void OnCrash(DWORD processID) MOZ_OVERRIDE;
DWORD mFlashProcess1;
DWORD mFlashProcess2;
mozilla::plugins::FinishInjectorInitTask* mFinishInitTask;
#endif
void OnProcessLaunched(const bool aSucceeded);

View File

@ -2,23 +2,25 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_plugins_ScopedMethodFactory_h
#define mozilla_plugins_ScopedMethodFactory_h
#ifndef mozilla_plugins_TaskFactory_h
#define mozilla_plugins_TaskFactory_h
#include <base/task.h>
/*
* This is based on the ScopedRunnableMethodFactory from ipc/chromium/src/base/task.h
* Chromiums factories assert if tasks are created and run on different threads,
* Chromium's factories assert if tasks are created and run on different threads,
* which is something we need to do in PluginModuleParent (hang UI vs. main thread).
* ScopedMethodFactory just provides cancellable tasks that don't assert this.
* TaskFactory just provides cancellable tasks that don't assert this.
* This version also allows both ScopedMethod and regular Tasks to be generated
* by the same Factory object.
*/
namespace mozilla {
namespace plugins {
template<class T>
class ScopedMethodFactory : public RevocableStore
class TaskFactory : public RevocableStore
{
private:
template<class TaskType>
@ -37,7 +39,15 @@ private:
};
public:
explicit ScopedMethodFactory(T* object) : object_(object) { }
explicit TaskFactory(T* object) : object_(object) { }
template <class TaskParamType>
inline TaskParamType* NewTask()
{
typedef TaskWrapper<TaskParamType> TaskWrapper;
TaskWrapper* task = new TaskWrapper(this);
return task;
}
template <class Method>
inline Task* NewRunnableMethod(Method method) {
@ -84,4 +94,4 @@ private:
} // namespace plugins
} // namespace mozilla
#endif // mozilla_plugins_ScopedMethodFactory_h
#endif // mozilla_plugins_TaskFactory_h

View File

@ -40,9 +40,9 @@ EXPORTS.mozilla.plugins += [
'PluginUtilsOSX.h',
'PluginWidgetChild.h',
'PluginWidgetParent.h',
'ScopedMethodFactory.h',
'StreamNotifyChild.h',
'StreamNotifyParent.h',
'TaskFactory.h',
]
if CONFIG['OS_ARCH'] == 'WINNT':