1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsIThreadManager.h"
8 #include "nsCOMPtr.h"
9 #include "nsXPCOM.h"
10 #include "nsThreadUtils.h"
11 #include "nsServiceManagerUtils.h"
12 #include "mozilla/Atomics.h"
13 #include "gtest/gtest.h"
14
15 using mozilla::Atomic;
16 using mozilla::Runnable;
17
18 class WaitCondition final : public nsINestedEventLoopCondition {
19 public:
20 NS_DECL_THREADSAFE_ISUPPORTS
21
WaitCondition(Atomic<uint32_t> & aCounter,uint32_t aMaxCount)22 WaitCondition(Atomic<uint32_t>& aCounter, uint32_t aMaxCount)
23 : mCounter(aCounter), mMaxCount(aMaxCount) {}
24
IsDone(bool * aDone)25 NS_IMETHODIMP IsDone(bool* aDone) override {
26 *aDone = (mCounter == mMaxCount);
27 return NS_OK;
28 }
29
30 private:
31 ~WaitCondition() = default;
32
33 Atomic<uint32_t>& mCounter;
34 const uint32_t mMaxCount;
35 };
36
37 NS_IMPL_ISUPPORTS(WaitCondition, nsINestedEventLoopCondition)
38
39 class SpinRunnable final : public Runnable {
40 public:
SpinRunnable(nsINestedEventLoopCondition * aCondition)41 explicit SpinRunnable(nsINestedEventLoopCondition* aCondition)
42 : Runnable("SpinRunnable"), mCondition(aCondition), mResult(NS_OK) {}
43
Run()44 NS_IMETHODIMP Run() {
45 nsCOMPtr<nsIThreadManager> threadMan =
46 do_GetService("@mozilla.org/thread-manager;1");
47 mResult = threadMan->SpinEventLoopUntil(mCondition);
48 return NS_OK;
49 }
50
SpinLoopResult()51 nsresult SpinLoopResult() { return mResult; }
52
53 private:
54 ~SpinRunnable() = default;
55
56 nsCOMPtr<nsINestedEventLoopCondition> mCondition;
57 Atomic<nsresult> mResult;
58 };
59
60 class CountRunnable final : public Runnable {
61 public:
CountRunnable(Atomic<uint32_t> & aCounter)62 explicit CountRunnable(Atomic<uint32_t>& aCounter)
63 : Runnable("CountRunnable"), mCounter(aCounter) {}
64
Run()65 NS_IMETHODIMP Run() {
66 mCounter++;
67 return NS_OK;
68 }
69
70 private:
71 Atomic<uint32_t>& mCounter;
72 };
73
TEST(ThreadManager,SpinEventLoopUntilSuccess)74 TEST(ThreadManager, SpinEventLoopUntilSuccess)
75 {
76 const uint32_t kRunnablesToDispatch = 100;
77 nsresult rv;
78 mozilla::Atomic<uint32_t> count(0);
79
80 nsCOMPtr<nsINestedEventLoopCondition> condition =
81 new WaitCondition(count, kRunnablesToDispatch);
82 RefPtr<SpinRunnable> spinner = new SpinRunnable(condition);
83 nsCOMPtr<nsIThread> thread;
84 rv = NS_NewNamedThread("SpinEventLoop", getter_AddRefs(thread), spinner);
85 ASSERT_TRUE(NS_SUCCEEDED(rv));
86
87 nsCOMPtr<nsIRunnable> counter = new CountRunnable(count);
88 for (uint32_t i = 0; i < kRunnablesToDispatch; ++i) {
89 rv = thread->Dispatch(counter, NS_DISPATCH_NORMAL);
90 ASSERT_TRUE(NS_SUCCEEDED(rv));
91 }
92
93 rv = thread->Shutdown();
94 ASSERT_TRUE(NS_SUCCEEDED(rv));
95 ASSERT_TRUE(NS_SUCCEEDED(spinner->SpinLoopResult()));
96 }
97
98 class ErrorCondition final : public nsINestedEventLoopCondition {
99 public:
100 NS_DECL_THREADSAFE_ISUPPORTS
101
ErrorCondition(Atomic<uint32_t> & aCounter,uint32_t aMaxCount)102 ErrorCondition(Atomic<uint32_t>& aCounter, uint32_t aMaxCount)
103 : mCounter(aCounter), mMaxCount(aMaxCount) {}
104
IsDone(bool * aDone)105 NS_IMETHODIMP IsDone(bool* aDone) override {
106 if (mCounter == mMaxCount) {
107 return NS_ERROR_ILLEGAL_VALUE;
108 }
109 return NS_OK;
110 }
111
112 private:
113 ~ErrorCondition() = default;
114
115 Atomic<uint32_t>& mCounter;
116 const uint32_t mMaxCount;
117 };
118
NS_IMPL_ISUPPORTS(ErrorCondition,nsINestedEventLoopCondition)119 NS_IMPL_ISUPPORTS(ErrorCondition, nsINestedEventLoopCondition)
120
121 TEST(ThreadManager, SpinEventLoopUntilError)
122 {
123 const uint32_t kRunnablesToDispatch = 100;
124 nsresult rv;
125 mozilla::Atomic<uint32_t> count(0);
126
127 nsCOMPtr<nsINestedEventLoopCondition> condition =
128 new ErrorCondition(count, kRunnablesToDispatch);
129 RefPtr<SpinRunnable> spinner = new SpinRunnable(condition);
130 nsCOMPtr<nsIThread> thread;
131 rv = NS_NewNamedThread("SpinEventLoop", getter_AddRefs(thread), spinner);
132 ASSERT_TRUE(NS_SUCCEEDED(rv));
133
134 nsCOMPtr<nsIRunnable> counter = new CountRunnable(count);
135 for (uint32_t i = 0; i < kRunnablesToDispatch; ++i) {
136 rv = thread->Dispatch(counter, NS_DISPATCH_NORMAL);
137 ASSERT_TRUE(NS_SUCCEEDED(rv));
138 }
139
140 rv = thread->Shutdown();
141 ASSERT_TRUE(NS_SUCCEEDED(rv));
142 ASSERT_TRUE(NS_FAILED(spinner->SpinLoopResult()));
143 }
144