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