|
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 "nsIFactory.h" |
|
9 #include "mozilla/Module.h" |
|
10 #include "nsXULAppAPI.h" |
|
11 #include "nsIThread.h" |
|
12 #include "nsIComponentRegistrar.h" |
|
13 |
|
14 #include "nsAutoPtr.h" |
|
15 #include "nsThreadUtils.h" |
|
16 #include "nsXPCOMCIDInternal.h" |
|
17 #include "pratom.h" |
|
18 #include "prmon.h" |
|
19 #include "mozilla/Attributes.h" |
|
20 |
|
21 #include "mozilla/ReentrantMonitor.h" |
|
22 using namespace mozilla; |
|
23 |
|
24 #ifdef DEBUG |
|
25 #define TEST_ASSERTION(_test, _msg) \ |
|
26 NS_ASSERTION(_test, _msg); |
|
27 #else |
|
28 #define TEST_ASSERTION(_test, _msg) \ |
|
29 PR_BEGIN_MACRO \ |
|
30 if (!(_test)) { \ |
|
31 NS_DebugBreak(NS_DEBUG_ABORT, _msg, #_test, __FILE__, __LINE__); \ |
|
32 } \ |
|
33 PR_END_MACRO |
|
34 #endif |
|
35 |
|
36 /* f93f6bdc-88af-42d7-9d64-1b43c649a3e5 */ |
|
37 #define FACTORY_CID1 \ |
|
38 { \ |
|
39 0xf93f6bdc, \ |
|
40 0x88af, \ |
|
41 0x42d7, \ |
|
42 { 0x9d, 0x64, 0x1b, 0x43, 0xc6, 0x49, 0xa3, 0xe5 } \ |
|
43 } |
|
44 NS_DEFINE_CID(kFactoryCID1, FACTORY_CID1); |
|
45 |
|
46 /* ef38ad65-6595-49f0-8048-e819f81d15e2 */ |
|
47 #define FACTORY_CID2 \ |
|
48 { \ |
|
49 0xef38ad65, \ |
|
50 0x6595, \ |
|
51 0x49f0, \ |
|
52 { 0x80, 0x48, 0xe8, 0x19, 0xf8, 0x1d, 0x15, 0xe2 } \ |
|
53 } |
|
54 NS_DEFINE_CID(kFactoryCID2, FACTORY_CID2); |
|
55 |
|
56 #define FACTORY_CONTRACTID \ |
|
57 "TestRacingThreadManager/factory;1" |
|
58 |
|
59 int32_t gComponent1Count = 0; |
|
60 int32_t gComponent2Count = 0; |
|
61 |
|
62 ReentrantMonitor* gReentrantMonitor = nullptr; |
|
63 |
|
64 bool gCreateInstanceCalled = false; |
|
65 bool gMainThreadWaiting = false; |
|
66 |
|
67 class AutoCreateAndDestroyReentrantMonitor |
|
68 { |
|
69 public: |
|
70 AutoCreateAndDestroyReentrantMonitor(ReentrantMonitor** aReentrantMonitorPtr) |
|
71 : mReentrantMonitorPtr(aReentrantMonitorPtr) { |
|
72 *aReentrantMonitorPtr = |
|
73 new ReentrantMonitor("TestRacingServiceManager::AutoMon"); |
|
74 TEST_ASSERTION(*aReentrantMonitorPtr, "Out of memory!"); |
|
75 } |
|
76 |
|
77 ~AutoCreateAndDestroyReentrantMonitor() { |
|
78 if (*mReentrantMonitorPtr) { |
|
79 delete *mReentrantMonitorPtr; |
|
80 *mReentrantMonitorPtr = nullptr; |
|
81 } |
|
82 } |
|
83 |
|
84 private: |
|
85 ReentrantMonitor** mReentrantMonitorPtr; |
|
86 }; |
|
87 |
|
88 class Factory MOZ_FINAL : public nsIFactory |
|
89 { |
|
90 public: |
|
91 NS_DECL_THREADSAFE_ISUPPORTS |
|
92 |
|
93 Factory() : mFirstComponentCreated(false) { } |
|
94 |
|
95 NS_IMETHOD CreateInstance(nsISupports* aDelegate, |
|
96 const nsIID& aIID, |
|
97 void** aResult); |
|
98 |
|
99 NS_IMETHOD LockFactory(bool aLock) { |
|
100 return NS_OK; |
|
101 } |
|
102 |
|
103 bool mFirstComponentCreated; |
|
104 }; |
|
105 |
|
106 NS_IMPL_ISUPPORTS(Factory, nsIFactory) |
|
107 |
|
108 class Component1 MOZ_FINAL : public nsISupports |
|
109 { |
|
110 public: |
|
111 NS_DECL_THREADSAFE_ISUPPORTS |
|
112 |
|
113 Component1() { |
|
114 // This is the real test - make sure that only one instance is ever created. |
|
115 int32_t count = PR_AtomicIncrement(&gComponent1Count); |
|
116 TEST_ASSERTION(count == 1, "Too many components created!"); |
|
117 } |
|
118 }; |
|
119 |
|
120 NS_IMPL_ADDREF(Component1) |
|
121 NS_IMPL_RELEASE(Component1) |
|
122 |
|
123 NS_INTERFACE_MAP_BEGIN(Component1) |
|
124 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
125 NS_INTERFACE_MAP_END |
|
126 |
|
127 class Component2 MOZ_FINAL : public nsISupports |
|
128 { |
|
129 public: |
|
130 NS_DECL_THREADSAFE_ISUPPORTS |
|
131 |
|
132 Component2() { |
|
133 // This is the real test - make sure that only one instance is ever created. |
|
134 int32_t count = PR_AtomicIncrement(&gComponent2Count); |
|
135 TEST_ASSERTION(count == 1, "Too many components created!"); |
|
136 } |
|
137 }; |
|
138 |
|
139 NS_IMPL_ADDREF(Component2) |
|
140 NS_IMPL_RELEASE(Component2) |
|
141 |
|
142 NS_INTERFACE_MAP_BEGIN(Component2) |
|
143 NS_INTERFACE_MAP_ENTRY(nsISupports) |
|
144 NS_INTERFACE_MAP_END |
|
145 |
|
146 NS_IMETHODIMP |
|
147 Factory::CreateInstance(nsISupports* aDelegate, |
|
148 const nsIID& aIID, |
|
149 void** aResult) |
|
150 { |
|
151 // Make sure that the second thread beat the main thread to the getService |
|
152 // call. |
|
153 TEST_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); |
|
154 |
|
155 { |
|
156 ReentrantMonitorAutoEnter mon(*gReentrantMonitor); |
|
157 |
|
158 gCreateInstanceCalled = true; |
|
159 mon.Notify(); |
|
160 |
|
161 mon.Wait(PR_MillisecondsToInterval(3000)); |
|
162 } |
|
163 |
|
164 NS_ENSURE_FALSE(aDelegate, NS_ERROR_NO_AGGREGATION); |
|
165 NS_ENSURE_ARG_POINTER(aResult); |
|
166 |
|
167 nsCOMPtr<nsISupports> instance; |
|
168 |
|
169 if (!mFirstComponentCreated) { |
|
170 instance = new Component1(); |
|
171 } |
|
172 else { |
|
173 instance = new Component2(); |
|
174 } |
|
175 NS_ENSURE_TRUE(instance, NS_ERROR_OUT_OF_MEMORY); |
|
176 |
|
177 nsresult rv = instance->QueryInterface(aIID, aResult); |
|
178 NS_ENSURE_SUCCESS(rv, rv); |
|
179 |
|
180 return NS_OK; |
|
181 } |
|
182 |
|
183 class Runnable : public nsRunnable |
|
184 { |
|
185 public: |
|
186 NS_DECL_NSIRUNNABLE |
|
187 |
|
188 Runnable() : mFirstRunnableDone(false) { } |
|
189 |
|
190 bool mFirstRunnableDone; |
|
191 }; |
|
192 |
|
193 NS_IMETHODIMP |
|
194 Runnable::Run() |
|
195 { |
|
196 { |
|
197 ReentrantMonitorAutoEnter mon(*gReentrantMonitor); |
|
198 |
|
199 while (!gMainThreadWaiting) { |
|
200 mon.Wait(); |
|
201 } |
|
202 } |
|
203 |
|
204 nsresult rv; |
|
205 nsCOMPtr<nsISupports> component; |
|
206 |
|
207 if (!mFirstRunnableDone) { |
|
208 component = do_GetService(kFactoryCID1, &rv); |
|
209 } |
|
210 else { |
|
211 component = do_GetService(FACTORY_CONTRACTID, &rv); |
|
212 } |
|
213 TEST_ASSERTION(NS_SUCCEEDED(rv), "GetService failed!"); |
|
214 |
|
215 return NS_OK; |
|
216 } |
|
217 |
|
218 static Factory* gFactory; |
|
219 |
|
220 static already_AddRefed<nsIFactory> |
|
221 CreateFactory(const mozilla::Module& module, const mozilla::Module::CIDEntry& entry) |
|
222 { |
|
223 if (!gFactory) { |
|
224 gFactory = new Factory(); |
|
225 NS_ADDREF(gFactory); |
|
226 } |
|
227 nsCOMPtr<nsIFactory> ret = gFactory; |
|
228 return ret.forget(); |
|
229 } |
|
230 |
|
231 static const mozilla::Module::CIDEntry kLocalCIDs[] = { |
|
232 { &kFactoryCID1, false, CreateFactory, nullptr }, |
|
233 { &kFactoryCID2, false, CreateFactory, nullptr }, |
|
234 { nullptr } |
|
235 }; |
|
236 |
|
237 static const mozilla::Module::ContractIDEntry kLocalContracts[] = { |
|
238 { FACTORY_CONTRACTID, &kFactoryCID2 }, |
|
239 { nullptr } |
|
240 }; |
|
241 |
|
242 static const mozilla::Module kLocalModule = { |
|
243 mozilla::Module::kVersion, |
|
244 kLocalCIDs, |
|
245 kLocalContracts |
|
246 }; |
|
247 |
|
248 int main(int argc, char** argv) |
|
249 { |
|
250 nsresult rv; |
|
251 XRE_AddStaticComponent(&kLocalModule); |
|
252 |
|
253 ScopedXPCOM xpcom("RacingServiceManager"); |
|
254 NS_ENSURE_FALSE(xpcom.failed(), 1); |
|
255 |
|
256 AutoCreateAndDestroyReentrantMonitor mon(&gReentrantMonitor); |
|
257 |
|
258 nsRefPtr<Runnable> runnable = new Runnable(); |
|
259 NS_ENSURE_TRUE(runnable, 1); |
|
260 |
|
261 // Run the classID test |
|
262 nsCOMPtr<nsIThread> newThread; |
|
263 rv = NS_NewThread(getter_AddRefs(newThread), runnable); |
|
264 NS_ENSURE_SUCCESS(rv, 1); |
|
265 |
|
266 { |
|
267 ReentrantMonitorAutoEnter mon(*gReentrantMonitor); |
|
268 |
|
269 gMainThreadWaiting = true; |
|
270 mon.Notify(); |
|
271 |
|
272 while (!gCreateInstanceCalled) { |
|
273 mon.Wait(); |
|
274 } |
|
275 } |
|
276 |
|
277 nsCOMPtr<nsISupports> component(do_GetService(kFactoryCID1, &rv)); |
|
278 NS_ENSURE_SUCCESS(rv, 1); |
|
279 |
|
280 // Reset for the contractID test |
|
281 gMainThreadWaiting = gCreateInstanceCalled = false; |
|
282 gFactory->mFirstComponentCreated = runnable->mFirstRunnableDone = true; |
|
283 component = nullptr; |
|
284 |
|
285 rv = newThread->Dispatch(runnable, NS_DISPATCH_NORMAL); |
|
286 NS_ENSURE_SUCCESS(rv, 1); |
|
287 |
|
288 { |
|
289 ReentrantMonitorAutoEnter mon(*gReentrantMonitor); |
|
290 |
|
291 gMainThreadWaiting = true; |
|
292 mon.Notify(); |
|
293 |
|
294 while (!gCreateInstanceCalled) { |
|
295 mon.Wait(); |
|
296 } |
|
297 } |
|
298 |
|
299 component = do_GetService(FACTORY_CONTRACTID, &rv); |
|
300 NS_ENSURE_SUCCESS(rv, 1); |
|
301 |
|
302 NS_RELEASE(gFactory); |
|
303 |
|
304 return 0; |
|
305 } |