Back to index

lightning-sunbird  0.9+nobinonly
TestThreadedIO.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Bill Law          <law@netscape.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 #include <stdio.h>
00039 #include "nsCOMPtr.h"
00040 #include "nsIEventQueueService.h"
00041 #include "nsIServiceManager.h"
00042 #include "nsIStreamListener.h"
00043 #include "nsIURI.h"
00044 #include "nsNetUtil.h"
00045 //#include "prthread.h"
00046 
00047 // This test attempts to load a URL on a separate thread.  It is currently
00048 // designed simply to expose the problems inherent in such an ambitous task
00049 // (i.e., it don't work).
00050 
00051 // Utility functions...
00052 
00053 // Create event queue for current thread.
00054 static nsCOMPtr<nsIEventQueue>
00055 createEventQueue() {
00056     nsCOMPtr<nsIEventQueue> result;
00057     // Get event queue service.
00058     nsresult rv = NS_OK;
00059     nsCOMPtr<nsIEventQueueService> eqs = 
00060              do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
00061     if ( NS_SUCCEEDED( rv ) ) {
00062             eqs->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(result));
00063     } else {
00064         printf( "%s %d: NS_WITH_SERVICE(nsIEventQueueService) failed, rv=0x%08X\n",
00065                 (char*)__FILE__, (int)__LINE__, (int)rv );
00066     }
00067     return result;
00068 }
00069 
00070 // Create channel for requested URL.
00071 static nsCOMPtr<nsIChannel>
00072 createChannel( const char *url ) {
00073     nsCOMPtr<nsIInputStream> result;
00074 
00075     nsCOMPtr<nsIURI> uri;
00076     printf( "Calling NS_NewURI for %s...\n", url );
00077     nsresult rv = NS_NewURI( getter_AddRefs( uri ), url );
00078 
00079     if ( NS_SUCCEEDED( rv ) ) {
00080         printf( "...NS_NewURI completed OK\n" );
00081 
00082         // Allocate a new input channel on this thread.
00083         printf( "Calling NS_OpenURI...\n" );
00084         nsresult rv = NS_OpenURI( getter_AddRefs( result ), uri, 0 );
00085 
00086         if ( NS_SUCCEEDED( rv ) ) {
00087             printf( "...NS_OpenURI completed OK\n" );
00088         } else {
00089             printf( "%s %d: NS_OpenURI failed, rv=0x%08X\n",
00090                     (char*)__FILE__, (int)__LINE__, (int)rv );
00091         }
00092     } else {
00093         printf( "%s %d: NS_NewURI failed, rv=0x%08X\n",
00094                 (char*)__FILE__, (int)__LINE__, (int)rv );
00095     }
00096     return result;
00097 }
00098 
00099 // Test listener.  It basically dumps incoming data to console.
00100 class TestListener : public nsIStreamListener {
00101 public:
00102     NS_DECL_ISUPPORTS
00103     NS_DECL_NSISTREAMLISTENER
00104     NS_DECL_NSISTREAMOBSERVER
00105 
00106     TestListener();
00107     ~TestListener();
00108     static void IOThread( void *p );
00109 
00110 private:
00111     PRBool mDone;
00112     int    mThreadNo;
00113     FILE  *mFile;
00114     static int threadCount;
00115 }; // class TestListener
00116 
00117 int TestListener::threadCount = 0;
00118 
00119 TestListener::TestListener()
00120     : mDone( PR_FALSE ), mThreadNo( ++threadCount ) {
00121     printf( "TestListener ctor called on thread %d\n", mThreadNo );
00122 }
00123 
00124 TestListener::~TestListener() {
00125     printf( "TestListener dtor called on thread %d\n", mThreadNo );
00126 }
00127 
00128 NS_IMPL_ISUPPORTS2( TestListener, nsIStreamListener, nsIRequestObserver )
00129 
00130 NS_IMETHODIMP
00131 TestListener::OnStartRequest( nsIChannel *aChannel, nsISupports *aContext ) {
00132     nsresult rv = NS_OK;
00133 
00134     printf( "TestListener::OnStartRequest called on thread %d\n", mThreadNo );
00135 
00136     // Open output file.
00137     char fileName[32];
00138     sprintf( fileName, "%s%d", "thread", mThreadNo );
00139     mFile = fopen( fileName, "wb" );
00140     setbuf( mFile, 0 );
00141 
00142     return rv;
00143 }
00144 
00145 NS_IMETHODIMP
00146 TestListener::OnStopRequest( nsIChannel *aChannel,
00147                              nsISupports *aContext,
00148                              nsresult aStatus,
00149                              const PRUnichar *aMsg ) {
00150     nsresult rv = NS_OK;
00151 
00152     printf( "TestListener::OnStopRequest called on thread %d\n", mThreadNo );
00153 
00154     fclose( mFile );
00155     mDone = PR_TRUE;
00156 
00157     return rv;
00158 }
00159 
00160 NS_IMETHODIMP
00161 TestListener::OnDataAvailable( nsIChannel *aChannel,
00162                                nsISupports *aContext,
00163                                nsIInputStream *aStream,
00164                                PRUint32 offset,
00165                                PRUint32 aLength ) {
00166     nsresult rv = NS_OK;
00167 
00168     printf( "TestListener::OnDataAvailable called on thread %d\n", mThreadNo );
00169 
00170     // Write the data to the console.
00171     // Read a buffer full till aLength bytes have been processed.
00172     char buffer[ 8192 ];
00173     unsigned long bytesRemaining = aLength;
00174     while ( bytesRemaining ) {
00175         unsigned int bytesRead;
00176         // Read a buffer full or the number remaining (whichever is smaller).
00177         rv = aStream->Read( buffer,
00178                             PR_MIN( sizeof( buffer ), bytesRemaining ),
00179                             &bytesRead );
00180         if ( NS_SUCCEEDED( rv ) ) {
00181             // Write the bytes just read to the output file.
00182             fwrite( buffer, 1, bytesRead, mFile );
00183             bytesRemaining -= bytesRead;
00184         } else {
00185             printf( "%s %d: Read error, rv=0x%08X\n",
00186                     (char*)__FILE__, (int)__LINE__, (int)rv );
00187             break;
00188         }
00189     }
00190     printf( "\n" );
00191 
00192     return rv;
00193 }
00194 
00195 // IOThread: this function creates a new TestListener object (on the new
00196 // thread), opens a channel, and does AsyncRead to it.
00197 void
00198 TestListener::IOThread( void *p ) {
00199     printf( "I/O thread (0x%08X) started...\n", (int)(void*)PR_GetCurrentThread() );
00200 
00201     // Argument is pointer to the nsIEventQueue for the main thread.
00202     nsIEventQueue *mainThreadQ = NS_STATIC_CAST( nsIEventQueue*, p );
00203 
00204     // Create channel for random web page.
00205     nsCOMPtr<nsIChannel> channel = createChannel( (const char*)p );
00206 
00207     if ( channel ) {
00208         // Create event queue.
00209         nsCOMPtr<nsIEventQueue> ioEventQ = createEventQueue();
00210 
00211         if ( ioEventQ ) {
00212             // Create test listener.
00213             TestListener *testListener = new TestListener();
00214             testListener->AddRef();
00215 
00216             // Read the channel.
00217             printf( "Doing AsyncRead...\n" );
00218             nsresult rv = channel->AsyncRead( testListener, 0 );
00219 
00220             if ( NS_SUCCEEDED( rv ) ) {
00221                 printf( "...AsyncRead completed OK\n" );
00222 
00223                 // Process events till testListener says stop.
00224                 printf( "Start event loop on io thread %d...\n", testListener->mThreadNo );
00225                 while ( !testListener->mDone ) {
00226                     PLEvent *event;
00227                     ioEventQ->GetEvent( &event );
00228                     ioEventQ->HandleEvent( event );
00229                 }
00230                 printf( "...io thread %d event loop exiting\n", testListener->mThreadNo );
00231             } else {
00232                 printf( "%s %d: AsyncRead failed on thread %d, rv=0x%08X\n",
00233                         (char*)__FILE__, (int)__LINE__, testListener->mThreadNo, (int)rv );
00234             }
00235 
00236             // Release the test listener.
00237             testListener->Release();
00238         }
00239     }
00240 
00241     printf( "...I/O thread terminating\n" );
00242 }
00243 
00244 static const int maxThreads = 5;
00245 
00246 int
00247 main( int argc, char* argv[] ) {
00248     setbuf( stdout, 0 );
00249     if ( argc < 2 || argc > maxThreads + 1 ) {
00250         printf( "usage: testThreadedIO url1 <url2>...\n"
00251                 "where <url#> is a location to be loaded on a separate thread\n"
00252                 "limit is %d urls/threads", maxThreads );
00253         return -1;
00254     }
00255 
00256     nsresult rv= (nsresult)-1;
00257 
00258     printf( "Test starting...\n" );
00259 
00260     // Initialize XPCOM.
00261     printf( "Initializing XPCOM...\n" );
00262     rv = NS_InitXPCOM2(nsnull, nsnull, nsnull);
00263     if ( NS_FAILED( rv ) ) {
00264         printf( "%s %d: NS_InitXPCOM failed, rv=0x%08X\n",
00265                 (char*)__FILE__, (int)__LINE__, (int)rv );
00266         return rv;
00267     }
00268     printf( "...XPCOM initialized OK\n" );
00269     // Create the Event Queue for this thread...
00270     printf( "Creating event queue for main thread (0x%08X)...\n",
00271             (int)(void*)PR_GetCurrentThread() );
00272     {
00273         nsCOMPtr<nsIEventQueue> mainThreadQ = createEventQueue();
00274 
00275         if ( mainThreadQ ) {
00276             printf( "...main thread's event queue created OK\n" );
00277 
00278             // Spawn threads to do I/O.
00279             int goodThreads = 0;
00280             PRThread *thread[ maxThreads ];
00281             for ( int threadNo = 1; threadNo < argc; threadNo++ ) {
00282                 printf( "Creating I/O thread %d to load %s...\n", threadNo, argv[threadNo] );
00283                 PRThread *ioThread = PR_CreateThread( PR_USER_THREAD,
00284                                                       TestListener::IOThread,
00285                                                       argv[threadNo],
00286                                                       PR_PRIORITY_NORMAL,
00287                                                       PR_LOCAL_THREAD,
00288                                                       PR_JOINABLE_THREAD,
00289                                                       0 );
00290                 if ( ioThread ) {
00291                     thread[ goodThreads++ ] = ioThread;
00292                     printf( "...I/O thread %d (0x%08X) created OK\n",
00293                             threadNo, (int)(void*)ioThread );
00294                 } else {
00295                     printf( "%s %d: PR_CreateThread for thread %d failed\n",
00296                             (char*)__FILE__, (int)__LINE__, threadNo );
00297                 }
00298             }
00299 
00300             // Wait for all the threads to terminate.
00301             for ( int joinThread = 0; joinThread < goodThreads; joinThread++ ) {
00302                 printf( "Waiting for thread %d to terminate...\n", joinThread+1 );
00303                 PR_JoinThread( thread[ joinThread ] );
00304             }
00305         }
00306     } // this scopes the nsCOMPtrs
00307     // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
00308     // Shut down XPCOM.
00309     printf( "Shutting down XPCOM...\n" );
00310     NS_ShutdownXPCOM( 0 );
00311     printf( "...XPCOM shutdown complete\n" );
00312 
00313     // Exit.
00314     printf( "...test complete, rv=0x%08X\n", (int)rv );
00315     return rv;
00316 }