Bug 1339945: Refactor mscom::MainThreadInvoker's wait mechanism into a separate class; r?handyman
MozReview-Commit-ID: BikO5ng6joM
--- a/ipc/mscom/MainThreadInvoker.cpp
+++ b/ipc/mscom/MainThreadInvoker.cpp
@@ -4,100 +4,59 @@
* 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/. */
#include "mozilla/mscom/MainThreadInvoker.h"
#include "GeckoProfiler.h"
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
-#include "mozilla/Atomics.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/HangMonitor.h"
+#include "mozilla/mscom/SpinEvent.h"
#include "mozilla/RefPtr.h"
-#include "nsServiceManagerUtils.h"
-#include "nsSystemInfo.h"
#include "private/prpriv.h" // For PR_GetThreadID
#include "WinUtils.h"
-// This gives us compiler intrinsics for the x86 PAUSE instruction
-#if defined(_MSC_VER)
-#include <intrin.h>
-#pragma intrinsic(_mm_pause)
-#define CPU_PAUSE() _mm_pause()
-#elif defined(__GNUC__) || defined(__clang__)
-#define CPU_PAUSE() __builtin_ia32_pause()
-#endif
-
-static bool sIsMulticore;
-
namespace {
/**
* SyncRunnable implements different code paths depending on whether or not
* we are running on a multiprocessor system. In the multiprocessor case, we
* leave the thread in a spin loop while waiting for the main thread to execute
* our runnable. Since spinning is pointless in the uniprocessor case, we block
* on an event that is set by the main thread once it has finished the runnable.
*/
class MOZ_RAII SyncRunnable
{
public:
explicit SyncRunnable(already_AddRefed<nsIRunnable>&& aRunnable)
- : mDoneEvent(sIsMulticore ? nullptr :
- ::CreateEventW(nullptr, FALSE, FALSE, nullptr))
- , mDone(false)
- , mRunnable(aRunnable)
+ : mRunnable(aRunnable)
{
- MOZ_ASSERT(sIsMulticore || mDoneEvent);
MOZ_ASSERT(mRunnable);
}
- ~SyncRunnable()
- {
- if (mDoneEvent) {
- ::CloseHandle(mDoneEvent);
- }
- }
+ ~SyncRunnable() = default;
void Run()
{
mRunnable->Run();
- if (mDoneEvent) {
- ::SetEvent(mDoneEvent);
- } else {
- mDone = true;
- }
+ mEvent.Signal();
}
bool WaitUntilComplete()
{
- if (mDoneEvent) {
- HANDLE handles[] = {mDoneEvent,
- mozilla::mscom::MainThreadInvoker::GetTargetThread()};
- DWORD waitResult = ::WaitForMultipleObjects(mozilla::ArrayLength(handles),
- handles, FALSE, INFINITE);
- return waitResult == WAIT_OBJECT_0;
- }
-
- while (!mDone) {
- // The PAUSE instruction is a hint to the CPU that we're doing a spin
- // loop. It is a no-op on older processors that don't support it, so
- // it is safe to use here without any CPUID checks.
- CPU_PAUSE();
- }
- return true;
+ return mEvent.Wait(mozilla::mscom::MainThreadInvoker::GetTargetThread());
}
private:
- HANDLE mDoneEvent;
- mozilla::Atomic<bool> mDone;
- nsCOMPtr<nsIRunnable> mRunnable;
+ nsCOMPtr<nsIRunnable> mRunnable;
+ mozilla::mscom::SpinEvent mEvent;
};
} // anonymous namespace
namespace mozilla {
namespace mscom {
HANDLE MainThreadInvoker::sMainThread = nullptr;
@@ -115,24 +74,16 @@ MainThreadInvoker::InitStatics()
rv = mainThread->GetPRThread(&mainPrThread);
if (NS_FAILED(rv)) {
return false;
}
PRUint32 tid = ::PR_GetThreadID(mainPrThread);
sMainThread = ::OpenThread(SYNCHRONIZE | THREAD_SET_CONTEXT, FALSE, tid);
- nsCOMPtr<nsIPropertyBag2> infoService = do_GetService(NS_SYSTEMINFO_CONTRACTID);
- if (infoService) {
- uint32_t cpuCount;
- nsresult rv = infoService->GetPropertyAsUint32(NS_LITERAL_STRING("cpucount"),
- &cpuCount);
- sIsMulticore = NS_SUCCEEDED(rv) && cpuCount > 1;
- }
-
return !!sMainThread;
}
MainThreadInvoker::MainThreadInvoker()
{
static const bool gotStatics = InitStatics();
MOZ_ASSERT(gotStatics);
}
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/SpinEvent.cpp
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+#include "mozilla/mscom/SpinEvent.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsSystemInfo.h"
+
+// This gives us compiler intrinsics for the x86 PAUSE instruction
+#if defined(_MSC_VER)
+#include <intrin.h>
+#pragma intrinsic(_mm_pause)
+#define CPU_PAUSE() _mm_pause()
+#elif defined(__GNUC__) || defined(__clang__)
+#define CPU_PAUSE() __builtin_ia32_pause()
+#endif
+
+namespace mozilla {
+namespace mscom {
+
+SpinEvent::SpinEvent()
+ : mDone(false)
+{
+ static const bool sIsMulticore = []() {
+ nsCOMPtr<nsIPropertyBag2> infoService = do_GetService(NS_SYSTEMINFO_CONTRACTID);
+ if (!infoService) {
+ return false;
+ }
+
+ uint32_t cpuCount;
+ nsresult rv = infoService->GetPropertyAsUint32(NS_LITERAL_STRING("cpucount"),
+ &cpuCount);
+ return NS_SUCCEEDED(rv) && cpuCount > 1;
+ }();
+
+ if (!sIsMulticore) {
+ mDoneEvent.own(::CreateEventW(nullptr, FALSE, FALSE, nullptr));
+ MOZ_ASSERT(mDoneEvent);
+ }
+}
+
+bool
+SpinEvent::Wait(HANDLE aTargetThread)
+{
+ MOZ_ASSERT(aTargetThread);
+ if (!aTargetThread) {
+ return false;
+ }
+
+ if (mDoneEvent) {
+ HANDLE handles[] = {mDoneEvent, aTargetThread};
+ DWORD waitResult = ::WaitForMultipleObjects(mozilla::ArrayLength(handles),
+ handles, FALSE, INFINITE);
+ return waitResult == WAIT_OBJECT_0;
+ }
+
+ while (!mDone) {
+ // The PAUSE instruction is a hint to the CPU that we're doing a spin
+ // loop. It is a no-op on older processors that don't support it, so
+ // it is safe to use here without any CPUID checks.
+ CPU_PAUSE();
+ }
+ return true;
+}
+
+void
+SpinEvent::Signal()
+{
+ if (mDoneEvent) {
+ ::SetEvent(mDoneEvent);
+ } else {
+ mDone = true;
+ }
+}
+
+} // namespace mscom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/mscom/SpinEvent.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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_mscom_SpinEvent_h
+#define mozilla_mscom_SpinEvent_h
+
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "nsWindowsHelpers.h"
+
+namespace mozilla {
+namespace mscom {
+
+class MOZ_NON_TEMPORARY_CLASS SpinEvent final
+{
+public:
+ SpinEvent();
+ ~SpinEvent() = default;
+
+ bool Wait(HANDLE aTargetThread);
+ void Signal();
+
+ SpinEvent(const SpinEvent&) = delete;
+ SpinEvent(SpinEvent&&) = delete;
+ SpinEvent& operator=(SpinEvent&&) = delete;
+ SpinEvent& operator=(const SpinEvent&) = delete;
+
+private:
+ Atomic<bool, ReleaseAcquire> mDone;
+ nsAutoHandle mDoneEvent;
+};
+
+} // namespace mscom
+} // namespace mozilla
+
+#endif // mozilla_mscom_SpinEvent_h
--- a/ipc/mscom/moz.build
+++ b/ipc/mscom/moz.build
@@ -30,23 +30,25 @@ if CONFIG['ACCESSIBILITY']:
EXPORTS.mozilla.mscom += [
'ActivationContext.h',
'DispatchForwarder.h',
'Interceptor.h',
'InterceptorLog.h',
'MainThreadHandoff.h',
'MainThreadInvoker.h',
'Registration.h',
+ 'SpinEvent.h',
'StructStream.h',
'WeakRef.h',
]
SOURCES += [
'Interceptor.cpp',
'Registration.cpp',
+ 'SpinEvent.cpp',
'WeakRef.cpp',
]
UNIFIED_SOURCES += [
'ActivationContext.cpp',
'DispatchForwarder.cpp',
'InterceptorLog.cpp',
'MainThreadHandoff.cpp',