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 #include "nsIThreadPool.h"
8
9 #include "nsComponentManagerUtils.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 {
32 static nsIThread** gCreatedThreadList = nullptr;
33 static nsIThread** gShutDownThreadList = nullptr;
34
35 static ReentrantMonitor* gReentrantMonitor = nullptr;
36
37 static bool gAllRunnablesPosted = false;
38 static bool gAllThreadsCreated = false;
39 static bool gAllThreadsShutDown = false;
40
41 class Listener final : public nsIThreadPoolListener
42 {
~Listener()43 ~Listener() {}
44
45 public:
46 NS_DECL_THREADSAFE_ISUPPORTS
47 NS_DECL_NSITHREADPOOLLISTENER
48 };
49
NS_IMPL_ISUPPORTS(Listener,nsIThreadPoolListener)50 NS_IMPL_ISUPPORTS(Listener, nsIThreadPoolListener)
51
52 NS_IMETHODIMP
53 Listener::OnThreadCreated()
54 {
55 nsCOMPtr<nsIThread> current(do_GetCurrentThread());
56 EXPECT_TRUE(current) << "Couldn't get current thread!";
57
58 ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
59
60 while (!gAllRunnablesPosted) {
61 mon.Wait();
62 }
63
64 for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) {
65 nsIThread* thread = gCreatedThreadList[i];
66 EXPECT_NE(thread, current) << "Saw the same thread twice!";
67
68 if (!thread) {
69 gCreatedThreadList[i] = current;
70 if (i == (NUMBER_OF_THREADS - 1)) {
71 gAllThreadsCreated = true;
72 mon.NotifyAll();
73 }
74 return NS_OK;
75 }
76 }
77
78 EXPECT_TRUE(false) << "Too many threads!";
79 return NS_ERROR_FAILURE;
80 }
81
82 NS_IMETHODIMP
OnThreadShuttingDown()83 Listener::OnThreadShuttingDown()
84 {
85 nsCOMPtr<nsIThread> current(do_GetCurrentThread());
86 EXPECT_TRUE(current) << "Couldn't get current thread!";
87
88 ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
89
90 for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) {
91 nsIThread* thread = gShutDownThreadList[i];
92 EXPECT_NE(thread, current) << "Saw the same thread twice!";
93
94 if (!thread) {
95 gShutDownThreadList[i] = current;
96 if (i == (NUMBER_OF_THREADS - 1)) {
97 gAllThreadsShutDown = true;
98 mon.NotifyAll();
99 }
100 return NS_OK;
101 }
102 }
103
104 EXPECT_TRUE(false) << "Too many threads!";
105 return NS_ERROR_FAILURE;
106 }
107
108 class AutoCreateAndDestroyReentrantMonitor
109 {
110 public:
AutoCreateAndDestroyReentrantMonitor(ReentrantMonitor ** aReentrantMonitorPtr)111 explicit AutoCreateAndDestroyReentrantMonitor(ReentrantMonitor** aReentrantMonitorPtr)
112 : mReentrantMonitorPtr(aReentrantMonitorPtr) {
113 *aReentrantMonitorPtr = new ReentrantMonitor("TestThreadPoolListener::AutoMon");
114 MOZ_RELEASE_ASSERT(*aReentrantMonitorPtr, "Out of memory!");
115 }
116
~AutoCreateAndDestroyReentrantMonitor()117 ~AutoCreateAndDestroyReentrantMonitor() {
118 delete *mReentrantMonitorPtr;
119 *mReentrantMonitorPtr = nullptr;
120 }
121
122 private:
123 ReentrantMonitor** mReentrantMonitorPtr;
124 };
125
TEST(ThreadPoolListener,Test)126 TEST(ThreadPoolListener, Test)
127 {
128 nsIThread* createdThreadList[NUMBER_OF_THREADS] = { nullptr };
129 gCreatedThreadList = createdThreadList;
130
131 nsIThread* shutDownThreadList[NUMBER_OF_THREADS] = { nullptr };
132 gShutDownThreadList = shutDownThreadList;
133
134 AutoCreateAndDestroyReentrantMonitor newMon(&gReentrantMonitor);
135 ASSERT_TRUE(gReentrantMonitor);
136
137 nsresult rv;
138
139 nsCOMPtr<nsIThreadPool> pool =
140 do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
141 ASSERT_TRUE(NS_SUCCEEDED(rv));
142
143 rv = pool->SetThreadLimit(NUMBER_OF_THREADS);
144 ASSERT_TRUE(NS_SUCCEEDED(rv));
145
146 rv = pool->SetIdleThreadLimit(NUMBER_OF_THREADS);
147 ASSERT_TRUE(NS_SUCCEEDED(rv));
148
149 rv = pool->SetIdleThreadTimeout(IDLE_THREAD_TIMEOUT);
150 ASSERT_TRUE(NS_SUCCEEDED(rv));
151
152 nsCOMPtr<nsIThreadPoolListener> listener = new Listener();
153 ASSERT_TRUE(listener);
154
155 rv = pool->SetListener(listener);
156 ASSERT_TRUE(NS_SUCCEEDED(rv));
157
158 {
159 ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
160
161 for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) {
162 nsCOMPtr<nsIRunnable> runnable = new Runnable();
163 ASSERT_TRUE(runnable);
164
165 rv = pool->Dispatch(runnable, NS_DISPATCH_NORMAL);
166 ASSERT_TRUE(NS_SUCCEEDED(rv));
167 }
168
169 gAllRunnablesPosted = true;
170 mon.NotifyAll();
171 }
172
173 {
174 ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
175 while (!gAllThreadsCreated) {
176 mon.Wait();
177 }
178 }
179
180 rv = pool->Shutdown();
181 ASSERT_TRUE(NS_SUCCEEDED(rv));
182
183 {
184 ReentrantMonitorAutoEnter mon(*gReentrantMonitor);
185 while (!gAllThreadsShutDown) {
186 mon.Wait();
187 }
188 }
189
190 for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) {
191 nsIThread* created = gCreatedThreadList[i];
192 ASSERT_TRUE(created);
193
194 bool match = false;
195 for (uint32_t j = 0; j < NUMBER_OF_THREADS; j++) {
196 nsIThread* destroyed = gShutDownThreadList[j];
197 ASSERT_TRUE(destroyed);
198
199 if (destroyed == created) {
200 match = true;
201 break;
202 }
203 }
204
205 ASSERT_TRUE(match);
206 }
207 }
208
209 } // namespace TestThreadPoolListener
210