/* -*- 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 "nsIThreadManager.h" #include "nsCOMPtr.h" #include "nsIThread.h" #include "nsXPCOM.h" #include "nsThreadUtils.h" #include "nsServiceManagerUtils.h" #include "mozilla/Atomics.h" #include "gtest/gtest.h" #include "mozilla/gtest/MozAssertions.h" using mozilla::Atomic; using mozilla::Runnable; class WaitCondition final : public nsINestedEventLoopCondition { public: NS_DECL_THREADSAFE_ISUPPORTS WaitCondition(Atomic& aCounter, uint32_t aMaxCount) : mCounter(aCounter), mMaxCount(aMaxCount) {} NS_IMETHODIMP IsDone(bool* aDone) override { *aDone = (mCounter == mMaxCount); return NS_OK; } private: ~WaitCondition() = default; Atomic& mCounter; const uint32_t mMaxCount; }; NS_IMPL_ISUPPORTS(WaitCondition, nsINestedEventLoopCondition) class SpinRunnable final : public Runnable { public: explicit SpinRunnable(nsINestedEventLoopCondition* aCondition) : Runnable("SpinRunnable"), mCondition(aCondition), mResult(NS_OK) {} NS_IMETHODIMP Run() { nsCOMPtr threadMan = do_GetService("@mozilla.org/thread-manager;1"); mResult = threadMan->SpinEventLoopUntil( "xpcom:TestThreadManager.cpp:SpinRunnable->Run()"_ns, mCondition); return NS_OK; } nsresult SpinLoopResult() { return mResult; } private: ~SpinRunnable() = default; nsCOMPtr mCondition; Atomic mResult; }; class CountRunnable final : public Runnable { public: explicit CountRunnable(Atomic& aCounter) : Runnable("CountRunnable"), mCounter(aCounter) {} NS_IMETHODIMP Run() { mCounter++; return NS_OK; } private: Atomic& mCounter; }; TEST(ThreadManager, SpinEventLoopUntilSuccess) { const uint32_t kRunnablesToDispatch = 100; nsresult rv; mozilla::Atomic count(0); nsCOMPtr condition = new WaitCondition(count, kRunnablesToDispatch); RefPtr spinner = new SpinRunnable(condition); nsCOMPtr thread; rv = NS_NewNamedThread("SpinEventLoop", getter_AddRefs(thread), spinner); ASSERT_NS_SUCCEEDED(rv); nsCOMPtr counter = new CountRunnable(count); for (uint32_t i = 0; i < kRunnablesToDispatch; ++i) { rv = thread->Dispatch(counter, NS_DISPATCH_NORMAL); ASSERT_NS_SUCCEEDED(rv); } rv = thread->Shutdown(); ASSERT_NS_SUCCEEDED(rv); ASSERT_NS_SUCCEEDED(spinner->SpinLoopResult()); } class ErrorCondition final : public nsINestedEventLoopCondition { public: NS_DECL_THREADSAFE_ISUPPORTS ErrorCondition(Atomic& aCounter, uint32_t aMaxCount) : mCounter(aCounter), mMaxCount(aMaxCount) {} NS_IMETHODIMP IsDone(bool* aDone) override { if (mCounter == mMaxCount) { return NS_ERROR_ILLEGAL_VALUE; } return NS_OK; } private: ~ErrorCondition() = default; Atomic& mCounter; const uint32_t mMaxCount; }; NS_IMPL_ISUPPORTS(ErrorCondition, nsINestedEventLoopCondition) TEST(ThreadManager, SpinEventLoopUntilError) { const uint32_t kRunnablesToDispatch = 100; nsresult rv; mozilla::Atomic count(0); nsCOMPtr condition = new ErrorCondition(count, kRunnablesToDispatch); RefPtr spinner = new SpinRunnable(condition); nsCOMPtr thread; rv = NS_NewNamedThread("SpinEventLoop", getter_AddRefs(thread), spinner); ASSERT_NS_SUCCEEDED(rv); nsCOMPtr counter = new CountRunnable(count); for (uint32_t i = 0; i < kRunnablesToDispatch; ++i) { rv = thread->Dispatch(counter, NS_DISPATCH_NORMAL); ASSERT_NS_SUCCEEDED(rv); } rv = thread->Shutdown(); ASSERT_NS_SUCCEEDED(rv); ASSERT_NS_FAILED(spinner->SpinLoopResult()); }