1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "nsIThread.h"
7
8 #include "nsComponentManagerUtils.h"
9 #include "nsThreadPool.h"
10 #include "nsThreadUtils.h"
11 #include "nsXPCOMCIDInternal.h"
12 #include "pratom.h"
13 #include "prinrval.h"
14 #include "prmon.h"
15 #include "prthread.h"
16 #include "mozilla/Assertions.h"
17 #include "mozilla/Attributes.h"
18
19 #include "mozilla/ReentrantMonitor.h"
20
21 #include "gtest/gtest.h"
22
23 using namespace mozilla;
24
25 #define NUMBER_OF_THREADS 4
26
27 // One hour... because test boxes can be slow!
28 #define IDLE_THREAD_TIMEOUT 3600000
29
30 namespace TestThreadPoolListener {
31 static nsIThread** gCreatedThreadList = nullptr;
32 static nsIThread** gShutDownThreadList = nullptr;
33
34 static ReentrantMonitor* gReentrantMonitor = nullptr;
35
36 static bool gAllRunnablesPosted = false;
37 static bool gAllThreadsCreated = false;
38 static bool gAllThreadsShutDown = false;
39
40 class Listener final : public nsIThreadPoolListener {
41 ~Listener() = default;
42
43 public:
44 NS_DECL_THREADSAFE_ISUPPORTS
45 NS_DECL_NSITHREADPOOLLISTENER
46 };
47
NS_IMPL_ISUPPORTS(Listener,nsIThreadPoolListener)48 NS_IMPL_ISUPPORTS(Listener, nsIThreadPoolListener)
49
50 NS_IMETHODIMP
51 Listener::OnThreadCreated() {
52 nsCOMPtr<nsIThread> current(do_GetCurrentThread());
53 EXPECT_TRUE(current) << "Couldn't get current thread!";
54
55 ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
56
57 while (!gAllRunnablesPosted) {
58 mon.Wait();
59 }
60
61 for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) {
62 nsIThread* thread = gCreatedThreadList[i];
63 EXPECT_NE(thread, current) << "Saw the same thread twice!";
64
65 if (!thread) {
66 gCreatedThreadList[i] = current;
67 if (i == (NUMBER_OF_THREADS - 1)) {
68 gAllThreadsCreated = true;
69 mon.NotifyAll();
70 }
71 return NS_OK;
72 }
73 }
74
75 EXPECT_TRUE(false) << "Too many threads!";
76 return NS_ERROR_FAILURE;
77 }
78
79 NS_IMETHODIMP
OnThreadShuttingDown()80 Listener::OnThreadShuttingDown() {
81 nsCOMPtr<nsIThread> current(do_GetCurrentThread());
82 EXPECT_TRUE(current) << "Couldn't get current thread!";
83
84 ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
85
86 for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) {
87 nsIThread* thread = gShutDownThreadList[i];
88 EXPECT_NE(thread, current) << "Saw the same thread twice!";
89
90 if (!thread) {
91 gShutDownThreadList[i] = current;
92 if (i == (NUMBER_OF_THREADS - 1)) {
93 gAllThreadsShutDown = true;
94 mon.NotifyAll();
95 }
96 return NS_OK;
97 }
98 }
99
100 EXPECT_TRUE(false) << "Too many threads!";
101 return NS_ERROR_FAILURE;
102 }
103
104 class AutoCreateAndDestroyReentrantMonitor {
105 public:
AutoCreateAndDestroyReentrantMonitor(ReentrantMonitor ** aReentrantMonitorPtr)106 explicit AutoCreateAndDestroyReentrantMonitor(
107 ReentrantMonitor** aReentrantMonitorPtr)
108 : mReentrantMonitorPtr(aReentrantMonitorPtr) {
109 *aReentrantMonitorPtr =
110 new ReentrantMonitor("TestThreadPoolListener::AutoMon");
111 MOZ_RELEASE_ASSERT(*aReentrantMonitorPtr, "Out of memory!");
112 }
113
~AutoCreateAndDestroyReentrantMonitor()114 ~AutoCreateAndDestroyReentrantMonitor() {
115 delete *mReentrantMonitorPtr;
116 *mReentrantMonitorPtr = nullptr;
117 }
118
119 private:
120 ReentrantMonitor** mReentrantMonitorPtr;
121 };
122
TEST(ThreadPoolListener,Test)123 TEST(ThreadPoolListener, Test)
124 {
125 nsIThread* createdThreadList[NUMBER_OF_THREADS] = {nullptr};
126 gCreatedThreadList = createdThreadList;
127
128 nsIThread* shutDownThreadList[NUMBER_OF_THREADS] = {nullptr};
129 gShutDownThreadList = shutDownThreadList;
130
131 AutoCreateAndDestroyReentrantMonitor newMon(&gReentrantMonitor);
132 ASSERT_TRUE(gReentrantMonitor);
133
134 nsresult rv;
135
136 nsCOMPtr<nsIThreadPool> pool = new nsThreadPool();
137
138 rv = pool->SetThreadLimit(NUMBER_OF_THREADS);
139 ASSERT_TRUE(NS_SUCCEEDED(rv));
140
141 rv = pool->SetIdleThreadLimit(NUMBER_OF_THREADS);
142 ASSERT_TRUE(NS_SUCCEEDED(rv));
143
144 rv = pool->SetIdleThreadTimeout(IDLE_THREAD_TIMEOUT);
145 ASSERT_TRUE(NS_SUCCEEDED(rv));
146
147 nsCOMPtr<nsIThreadPoolListener> listener = new Listener();
148 ASSERT_TRUE(listener);
149
150 rv = pool->SetListener(listener);
151 ASSERT_TRUE(NS_SUCCEEDED(rv));
152
153 {
154 ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
155
156 for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) {
157 nsCOMPtr<nsIRunnable> runnable = new Runnable("TestRunnable");
158 ASSERT_TRUE(runnable);
159
160 rv = pool->Dispatch(runnable, NS_DISPATCH_NORMAL);
161 ASSERT_TRUE(NS_SUCCEEDED(rv));
162 }
163
164 gAllRunnablesPosted = true;
165 mon.NotifyAll();
166 }
167
168 {
169 ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
170 while (!gAllThreadsCreated) {
171 mon.Wait();
172 }
173 }
174
175 rv = pool->Shutdown();
176 ASSERT_TRUE(NS_SUCCEEDED(rv));
177
178 {
179 ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
180 while (!gAllThreadsShutDown) {
181 mon.Wait();
182 }
183 }
184
185 for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) {
186 nsIThread* created = gCreatedThreadList[i];
187 ASSERT_TRUE(created);
188
189 bool match = false;
190 for (uint32_t j = 0; j < NUMBER_OF_THREADS; j++) {
191 nsIThread* destroyed = gShutDownThreadList[j];
192 ASSERT_TRUE(destroyed);
193
194 if (destroyed == created) {
195 match = true;
196 break;
197 }
198 }
199
200 ASSERT_TRUE(match);
201 }
202 }
203
204 } // namespace TestThreadPoolListener
205