|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
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 #include <stdio.h> |
|
6 #include "nsCOMPtr.h" |
|
7 #include "nsIEventQueueService.h" |
|
8 #include "nsIServiceManager.h" |
|
9 #include "nsIStreamListener.h" |
|
10 #include "nsIURI.h" |
|
11 #include "nsNetUtil.h" |
|
12 #include <algorithm> |
|
13 //#include "prthread.h" |
|
14 |
|
15 // This test attempts to load a URL on a separate thread. It is currently |
|
16 // designed simply to expose the problems inherent in such an ambitous task |
|
17 // (i.e., it don't work). |
|
18 |
|
19 // Utility functions... |
|
20 |
|
21 // Create event queue for current thread. |
|
22 static nsCOMPtr<nsIEventQueue> |
|
23 createEventQueue() { |
|
24 nsCOMPtr<nsIEventQueue> result; |
|
25 // Get event queue service. |
|
26 nsresult rv = NS_OK; |
|
27 nsCOMPtr<nsIEventQueueService> eqs = |
|
28 do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv); |
|
29 if ( NS_SUCCEEDED( rv ) ) { |
|
30 eqs->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(result)); |
|
31 } else { |
|
32 printf( "%s %d: NS_WITH_SERVICE(nsIEventQueueService) failed, rv=0x%08X\n", |
|
33 (char*)__FILE__, (int)__LINE__, (int)rv ); |
|
34 } |
|
35 return result; |
|
36 } |
|
37 |
|
38 // Create channel for requested URL. |
|
39 static nsCOMPtr<nsIChannel> |
|
40 createChannel( const char *url ) { |
|
41 nsCOMPtr<nsIInputStream> result; |
|
42 |
|
43 nsCOMPtr<nsIURI> uri; |
|
44 printf( "Calling NS_NewURI for %s...\n", url ); |
|
45 nsresult rv = NS_NewURI( getter_AddRefs( uri ), url ); |
|
46 |
|
47 if ( NS_SUCCEEDED( rv ) ) { |
|
48 printf( "...NS_NewURI completed OK\n" ); |
|
49 |
|
50 // Allocate a new input channel on this thread. |
|
51 printf( "Calling NS_OpenURI...\n" ); |
|
52 nsresult rv = NS_OpenURI( getter_AddRefs( result ), uri, 0 ); |
|
53 |
|
54 if ( NS_SUCCEEDED( rv ) ) { |
|
55 printf( "...NS_OpenURI completed OK\n" ); |
|
56 } else { |
|
57 printf( "%s %d: NS_OpenURI failed, rv=0x%08X\n", |
|
58 (char*)__FILE__, (int)__LINE__, (int)rv ); |
|
59 } |
|
60 } else { |
|
61 printf( "%s %d: NS_NewURI failed, rv=0x%08X\n", |
|
62 (char*)__FILE__, (int)__LINE__, (int)rv ); |
|
63 } |
|
64 return result; |
|
65 } |
|
66 |
|
67 // Test listener. It basically dumps incoming data to console. |
|
68 class TestListener : public nsIStreamListener { |
|
69 public: |
|
70 NS_DECL_ISUPPORTS |
|
71 NS_DECL_NSISTREAMLISTENER |
|
72 NS_DECL_NSISTREAMOBSERVER |
|
73 |
|
74 TestListener(); |
|
75 ~TestListener(); |
|
76 static void IOThread( void *p ); |
|
77 |
|
78 private: |
|
79 bool mDone; |
|
80 int mThreadNo; |
|
81 FILE *mFile; |
|
82 static int threadCount; |
|
83 }; // class TestListener |
|
84 |
|
85 int TestListener::threadCount = 0; |
|
86 |
|
87 TestListener::TestListener() |
|
88 : mDone( false ), mThreadNo( ++threadCount ) { |
|
89 printf( "TestListener ctor called on thread %d\n", mThreadNo ); |
|
90 } |
|
91 |
|
92 TestListener::~TestListener() { |
|
93 printf( "TestListener dtor called on thread %d\n", mThreadNo ); |
|
94 } |
|
95 |
|
96 NS_IMPL_ISUPPORTS( TestListener, nsIStreamListener, nsIRequestObserver ) |
|
97 |
|
98 NS_IMETHODIMP |
|
99 TestListener::OnStartRequest( nsIChannel *aChannel, nsISupports *aContext ) { |
|
100 nsresult rv = NS_OK; |
|
101 |
|
102 printf( "TestListener::OnStartRequest called on thread %d\n", mThreadNo ); |
|
103 |
|
104 // Open output file. |
|
105 char fileName[32]; |
|
106 sprintf( fileName, "%s%d", "thread", mThreadNo ); |
|
107 mFile = fopen( fileName, "wb" ); |
|
108 setbuf( mFile, 0 ); |
|
109 |
|
110 return rv; |
|
111 } |
|
112 |
|
113 NS_IMETHODIMP |
|
114 TestListener::OnStopRequest( nsIChannel *aChannel, |
|
115 nsISupports *aContext, |
|
116 nsresult aStatus, |
|
117 const char16_t *aMsg ) { |
|
118 nsresult rv = NS_OK; |
|
119 |
|
120 printf( "TestListener::OnStopRequest called on thread %d\n", mThreadNo ); |
|
121 |
|
122 fclose( mFile ); |
|
123 mDone = true; |
|
124 |
|
125 return rv; |
|
126 } |
|
127 |
|
128 NS_IMETHODIMP |
|
129 TestListener::OnDataAvailable( nsIChannel *aChannel, |
|
130 nsISupports *aContext, |
|
131 nsIInputStream *aStream, |
|
132 uint64_t offset, |
|
133 uint32_t aLength ) { |
|
134 nsresult rv = NS_OK; |
|
135 |
|
136 printf( "TestListener::OnDataAvailable called on thread %d\n", mThreadNo ); |
|
137 |
|
138 // Write the data to the console. |
|
139 // Read a buffer full till aLength bytes have been processed. |
|
140 char buffer[ 8192 ]; |
|
141 unsigned long bytesRemaining = aLength; |
|
142 while ( bytesRemaining ) { |
|
143 unsigned int bytesRead; |
|
144 // Read a buffer full or the number remaining (whichever is smaller). |
|
145 rv = aStream->Read( buffer, |
|
146 std::min( sizeof( buffer ), bytesRemaining ), |
|
147 &bytesRead ); |
|
148 if ( NS_SUCCEEDED( rv ) ) { |
|
149 // Write the bytes just read to the output file. |
|
150 fwrite( buffer, 1, bytesRead, mFile ); |
|
151 bytesRemaining -= bytesRead; |
|
152 } else { |
|
153 printf( "%s %d: Read error, rv=0x%08X\n", |
|
154 (char*)__FILE__, (int)__LINE__, (int)rv ); |
|
155 break; |
|
156 } |
|
157 } |
|
158 printf( "\n" ); |
|
159 |
|
160 return rv; |
|
161 } |
|
162 |
|
163 // IOThread: this function creates a new TestListener object (on the new |
|
164 // thread), opens a channel, and does AsyncRead to it. |
|
165 void |
|
166 TestListener::IOThread( void *p ) { |
|
167 printf( "I/O thread (0x%08X) started...\n", (int)(void*)PR_GetCurrentThread() ); |
|
168 |
|
169 // Argument is pointer to the nsIEventQueue for the main thread. |
|
170 nsIEventQueue *mainThreadQ = static_cast<nsIEventQueue*>(p); |
|
171 |
|
172 // Create channel for random web page. |
|
173 nsCOMPtr<nsIChannel> channel = createChannel( (const char*)p ); |
|
174 |
|
175 if ( channel ) { |
|
176 // Create event queue. |
|
177 nsCOMPtr<nsIEventQueue> ioEventQ = createEventQueue(); |
|
178 |
|
179 if ( ioEventQ ) { |
|
180 // Create test listener. |
|
181 TestListener *testListener = new TestListener(); |
|
182 testListener->AddRef(); |
|
183 |
|
184 // Read the channel. |
|
185 printf( "Doing AsyncRead...\n" ); |
|
186 nsresult rv = channel->AsyncRead( testListener, 0 ); |
|
187 |
|
188 if ( NS_SUCCEEDED( rv ) ) { |
|
189 printf( "...AsyncRead completed OK\n" ); |
|
190 |
|
191 // Process events till testListener says stop. |
|
192 printf( "Start event loop on io thread %d...\n", testListener->mThreadNo ); |
|
193 while ( !testListener->mDone ) { |
|
194 PLEvent *event; |
|
195 ioEventQ->GetEvent( &event ); |
|
196 ioEventQ->HandleEvent( event ); |
|
197 } |
|
198 printf( "...io thread %d event loop exiting\n", testListener->mThreadNo ); |
|
199 } else { |
|
200 printf( "%s %d: AsyncRead failed on thread %d, rv=0x%08X\n", |
|
201 (char*)__FILE__, (int)__LINE__, testListener->mThreadNo, (int)rv ); |
|
202 } |
|
203 |
|
204 // Release the test listener. |
|
205 testListener->Release(); |
|
206 } |
|
207 } |
|
208 |
|
209 printf( "...I/O thread terminating\n" ); |
|
210 } |
|
211 |
|
212 static const int maxThreads = 5; |
|
213 |
|
214 int |
|
215 main( int argc, char* argv[] ) { |
|
216 setbuf( stdout, 0 ); |
|
217 if ( argc < 2 || argc > maxThreads + 1 ) { |
|
218 printf( "usage: testThreadedIO url1 <url2>...\n" |
|
219 "where <url#> is a location to be loaded on a separate thread\n" |
|
220 "limit is %d urls/threads", maxThreads ); |
|
221 return -1; |
|
222 } |
|
223 |
|
224 nsresult rv= (nsresult)-1; |
|
225 |
|
226 printf( "Test starting...\n" ); |
|
227 |
|
228 // Initialize XPCOM. |
|
229 printf( "Initializing XPCOM...\n" ); |
|
230 rv = NS_InitXPCOM2(nullptr, nullptr, nullptr); |
|
231 if ( NS_FAILED( rv ) ) { |
|
232 printf( "%s %d: NS_InitXPCOM failed, rv=0x%08X\n", |
|
233 (char*)__FILE__, (int)__LINE__, (int)rv ); |
|
234 return rv; |
|
235 } |
|
236 printf( "...XPCOM initialized OK\n" ); |
|
237 // Create the Event Queue for this thread... |
|
238 printf( "Creating event queue for main thread (0x%08X)...\n", |
|
239 (int)(void*)PR_GetCurrentThread() ); |
|
240 { |
|
241 nsCOMPtr<nsIEventQueue> mainThreadQ = createEventQueue(); |
|
242 |
|
243 if ( mainThreadQ ) { |
|
244 printf( "...main thread's event queue created OK\n" ); |
|
245 |
|
246 // Spawn threads to do I/O. |
|
247 int goodThreads = 0; |
|
248 PRThread *thread[ maxThreads ]; |
|
249 for ( int threadNo = 1; threadNo < argc; threadNo++ ) { |
|
250 printf( "Creating I/O thread %d to load %s...\n", threadNo, argv[threadNo] ); |
|
251 PRThread *ioThread = PR_CreateThread( PR_USER_THREAD, |
|
252 TestListener::IOThread, |
|
253 argv[threadNo], |
|
254 PR_PRIORITY_NORMAL, |
|
255 PR_GLOBAL_THREAD, |
|
256 PR_JOINABLE_THREAD, |
|
257 0 ); |
|
258 if ( ioThread ) { |
|
259 thread[ goodThreads++ ] = ioThread; |
|
260 printf( "...I/O thread %d (0x%08X) created OK\n", |
|
261 threadNo, (int)(void*)ioThread ); |
|
262 } else { |
|
263 printf( "%s %d: PR_CreateThread for thread %d failed\n", |
|
264 (char*)__FILE__, (int)__LINE__, threadNo ); |
|
265 } |
|
266 } |
|
267 |
|
268 // Wait for all the threads to terminate. |
|
269 for ( int joinThread = 0; joinThread < goodThreads; joinThread++ ) { |
|
270 printf( "Waiting for thread %d to terminate...\n", joinThread+1 ); |
|
271 PR_JoinThread( thread[ joinThread ] ); |
|
272 } |
|
273 } |
|
274 } // this scopes the nsCOMPtrs |
|
275 // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM |
|
276 // Shut down XPCOM. |
|
277 printf( "Shutting down XPCOM...\n" ); |
|
278 NS_ShutdownXPCOM( 0 ); |
|
279 printf( "...XPCOM shutdown complete\n" ); |
|
280 |
|
281 // Exit. |
|
282 printf( "...test complete, rv=0x%08X\n", (int)rv ); |
|
283 return rv; |
|
284 } |