|
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 "TestHarness.h" |
|
7 |
|
8 #include "nsIThread.h" |
|
9 #include "nsIThreadPool.h" |
|
10 |
|
11 #include "nsThreadUtils.h" |
|
12 #include "nsXPCOMCIDInternal.h" |
|
13 #include "pratom.h" |
|
14 #include "prinrval.h" |
|
15 #include "prmon.h" |
|
16 #include "prthread.h" |
|
17 #include "mozilla/Attributes.h" |
|
18 |
|
19 #include "mozilla/ReentrantMonitor.h" |
|
20 using namespace mozilla; |
|
21 |
|
22 #define NUMBER_OF_THREADS 4 |
|
23 |
|
24 // One hour... because test boxes can be slow! |
|
25 #define IDLE_THREAD_TIMEOUT 3600000 |
|
26 |
|
27 static nsIThread** gCreatedThreadList = nullptr; |
|
28 static nsIThread** gShutDownThreadList = nullptr; |
|
29 |
|
30 static ReentrantMonitor* gReentrantMonitor = nullptr; |
|
31 |
|
32 static bool gAllRunnablesPosted = false; |
|
33 static bool gAllThreadsCreated = false; |
|
34 static bool gAllThreadsShutDown = false; |
|
35 |
|
36 #ifdef DEBUG |
|
37 #define TEST_ASSERTION(_test, _msg) \ |
|
38 NS_ASSERTION(_test, _msg); |
|
39 #else |
|
40 #define TEST_ASSERTION(_test, _msg) \ |
|
41 PR_BEGIN_MACRO \ |
|
42 if (!(_test)) { \ |
|
43 NS_DebugBreak(NS_DEBUG_ABORT, _msg, #_test, __FILE__, __LINE__); \ |
|
44 } \ |
|
45 PR_END_MACRO |
|
46 #endif |
|
47 |
|
48 class Listener MOZ_FINAL : public nsIThreadPoolListener |
|
49 { |
|
50 public: |
|
51 NS_DECL_THREADSAFE_ISUPPORTS |
|
52 NS_DECL_NSITHREADPOOLLISTENER |
|
53 }; |
|
54 |
|
55 NS_IMPL_ISUPPORTS(Listener, nsIThreadPoolListener) |
|
56 |
|
57 NS_IMETHODIMP |
|
58 Listener::OnThreadCreated() |
|
59 { |
|
60 nsCOMPtr<nsIThread> current(do_GetCurrentThread()); |
|
61 TEST_ASSERTION(current, "Couldn't get current thread!"); |
|
62 |
|
63 ReentrantMonitorAutoEnter mon(*gReentrantMonitor); |
|
64 |
|
65 while (!gAllRunnablesPosted) { |
|
66 mon.Wait(); |
|
67 } |
|
68 |
|
69 for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) { |
|
70 nsIThread* thread = gCreatedThreadList[i]; |
|
71 TEST_ASSERTION(thread != current, "Saw the same thread twice!"); |
|
72 |
|
73 if (!thread) { |
|
74 gCreatedThreadList[i] = current; |
|
75 if (i == (NUMBER_OF_THREADS - 1)) { |
|
76 gAllThreadsCreated = true; |
|
77 mon.NotifyAll(); |
|
78 } |
|
79 return NS_OK; |
|
80 } |
|
81 } |
|
82 |
|
83 TEST_ASSERTION(false, "Too many threads!"); |
|
84 return NS_ERROR_FAILURE; |
|
85 } |
|
86 |
|
87 NS_IMETHODIMP |
|
88 Listener::OnThreadShuttingDown() |
|
89 { |
|
90 nsCOMPtr<nsIThread> current(do_GetCurrentThread()); |
|
91 TEST_ASSERTION(current, "Couldn't get current thread!"); |
|
92 |
|
93 ReentrantMonitorAutoEnter mon(*gReentrantMonitor); |
|
94 |
|
95 for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) { |
|
96 nsIThread* thread = gShutDownThreadList[i]; |
|
97 TEST_ASSERTION(thread != current, "Saw the same thread twice!"); |
|
98 |
|
99 if (!thread) { |
|
100 gShutDownThreadList[i] = current; |
|
101 if (i == (NUMBER_OF_THREADS - 1)) { |
|
102 gAllThreadsShutDown = true; |
|
103 mon.NotifyAll(); |
|
104 } |
|
105 return NS_OK; |
|
106 } |
|
107 } |
|
108 |
|
109 TEST_ASSERTION(false, "Too many threads!"); |
|
110 return NS_ERROR_FAILURE; |
|
111 } |
|
112 |
|
113 class AutoCreateAndDestroyReentrantMonitor |
|
114 { |
|
115 public: |
|
116 AutoCreateAndDestroyReentrantMonitor(ReentrantMonitor** aReentrantMonitorPtr) |
|
117 : mReentrantMonitorPtr(aReentrantMonitorPtr) { |
|
118 *aReentrantMonitorPtr = new ReentrantMonitor("TestThreadPoolListener::AutoMon"); |
|
119 TEST_ASSERTION(*aReentrantMonitorPtr, "Out of memory!"); |
|
120 } |
|
121 |
|
122 ~AutoCreateAndDestroyReentrantMonitor() { |
|
123 if (*mReentrantMonitorPtr) { |
|
124 delete *mReentrantMonitorPtr; |
|
125 *mReentrantMonitorPtr = nullptr; |
|
126 } |
|
127 } |
|
128 |
|
129 private: |
|
130 ReentrantMonitor** mReentrantMonitorPtr; |
|
131 }; |
|
132 |
|
133 int main(int argc, char** argv) |
|
134 { |
|
135 ScopedXPCOM xpcom("ThreadPoolListener"); |
|
136 NS_ENSURE_FALSE(xpcom.failed(), 1); |
|
137 |
|
138 nsIThread* createdThreadList[NUMBER_OF_THREADS] = { nullptr }; |
|
139 gCreatedThreadList = createdThreadList; |
|
140 |
|
141 nsIThread* shutDownThreadList[NUMBER_OF_THREADS] = { nullptr }; |
|
142 gShutDownThreadList = shutDownThreadList; |
|
143 |
|
144 AutoCreateAndDestroyReentrantMonitor newMon(&gReentrantMonitor); |
|
145 NS_ENSURE_TRUE(gReentrantMonitor, 1); |
|
146 |
|
147 nsresult rv; |
|
148 |
|
149 nsCOMPtr<nsIThreadPool> pool = |
|
150 do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv); |
|
151 NS_ENSURE_SUCCESS(rv, 1); |
|
152 |
|
153 rv = pool->SetThreadLimit(NUMBER_OF_THREADS); |
|
154 NS_ENSURE_SUCCESS(rv, 1); |
|
155 |
|
156 rv = pool->SetIdleThreadLimit(NUMBER_OF_THREADS); |
|
157 NS_ENSURE_SUCCESS(rv, 1); |
|
158 |
|
159 rv = pool->SetIdleThreadTimeout(IDLE_THREAD_TIMEOUT); |
|
160 NS_ENSURE_SUCCESS(rv, 1); |
|
161 |
|
162 nsCOMPtr<nsIThreadPoolListener> listener = new Listener(); |
|
163 NS_ENSURE_TRUE(listener, 1); |
|
164 |
|
165 rv = pool->SetListener(listener); |
|
166 NS_ENSURE_SUCCESS(rv, 1); |
|
167 |
|
168 { |
|
169 ReentrantMonitorAutoEnter mon(*gReentrantMonitor); |
|
170 |
|
171 for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) { |
|
172 nsCOMPtr<nsIRunnable> runnable = new nsRunnable(); |
|
173 NS_ENSURE_TRUE(runnable, 1); |
|
174 |
|
175 rv = pool->Dispatch(runnable, NS_DISPATCH_NORMAL); |
|
176 NS_ENSURE_SUCCESS(rv, 1); |
|
177 } |
|
178 |
|
179 gAllRunnablesPosted = true; |
|
180 mon.NotifyAll(); |
|
181 } |
|
182 |
|
183 { |
|
184 ReentrantMonitorAutoEnter mon(*gReentrantMonitor); |
|
185 while (!gAllThreadsCreated) { |
|
186 mon.Wait(); |
|
187 } |
|
188 } |
|
189 |
|
190 rv = pool->Shutdown(); |
|
191 NS_ENSURE_SUCCESS(rv, 1); |
|
192 |
|
193 { |
|
194 ReentrantMonitorAutoEnter mon(*gReentrantMonitor); |
|
195 while (!gAllThreadsShutDown) { |
|
196 mon.Wait(); |
|
197 } |
|
198 } |
|
199 |
|
200 for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) { |
|
201 nsIThread* created = gCreatedThreadList[i]; |
|
202 NS_ENSURE_TRUE(created, 1); |
|
203 |
|
204 bool match = false; |
|
205 for (uint32_t j = 0; j < NUMBER_OF_THREADS; j++) { |
|
206 nsIThread* destroyed = gShutDownThreadList[j]; |
|
207 NS_ENSURE_TRUE(destroyed, 1); |
|
208 |
|
209 if (destroyed == created) { |
|
210 match = true; |
|
211 break; |
|
212 } |
|
213 } |
|
214 |
|
215 NS_ENSURE_TRUE(match, 1); |
|
216 } |
|
217 |
|
218 return 0; |
|
219 } |