michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIEventQueueService.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIStreamListener.h" michael@0: #include "nsIURI.h" michael@0: #include "nsNetUtil.h" michael@0: #include michael@0: //#include "prthread.h" michael@0: michael@0: // This test attempts to load a URL on a separate thread. It is currently michael@0: // designed simply to expose the problems inherent in such an ambitous task michael@0: // (i.e., it don't work). michael@0: michael@0: // Utility functions... michael@0: michael@0: // Create event queue for current thread. michael@0: static nsCOMPtr michael@0: createEventQueue() { michael@0: nsCOMPtr result; michael@0: // Get event queue service. michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr eqs = michael@0: do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv); michael@0: if ( NS_SUCCEEDED( rv ) ) { michael@0: eqs->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(result)); michael@0: } else { michael@0: printf( "%s %d: NS_WITH_SERVICE(nsIEventQueueService) failed, rv=0x%08X\n", michael@0: (char*)__FILE__, (int)__LINE__, (int)rv ); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // Create channel for requested URL. michael@0: static nsCOMPtr michael@0: createChannel( const char *url ) { michael@0: nsCOMPtr result; michael@0: michael@0: nsCOMPtr uri; michael@0: printf( "Calling NS_NewURI for %s...\n", url ); michael@0: nsresult rv = NS_NewURI( getter_AddRefs( uri ), url ); michael@0: michael@0: if ( NS_SUCCEEDED( rv ) ) { michael@0: printf( "...NS_NewURI completed OK\n" ); michael@0: michael@0: // Allocate a new input channel on this thread. michael@0: printf( "Calling NS_OpenURI...\n" ); michael@0: nsresult rv = NS_OpenURI( getter_AddRefs( result ), uri, 0 ); michael@0: michael@0: if ( NS_SUCCEEDED( rv ) ) { michael@0: printf( "...NS_OpenURI completed OK\n" ); michael@0: } else { michael@0: printf( "%s %d: NS_OpenURI failed, rv=0x%08X\n", michael@0: (char*)__FILE__, (int)__LINE__, (int)rv ); michael@0: } michael@0: } else { michael@0: printf( "%s %d: NS_NewURI failed, rv=0x%08X\n", michael@0: (char*)__FILE__, (int)__LINE__, (int)rv ); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: // Test listener. It basically dumps incoming data to console. michael@0: class TestListener : public nsIStreamListener { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSISTREAMLISTENER michael@0: NS_DECL_NSISTREAMOBSERVER michael@0: michael@0: TestListener(); michael@0: ~TestListener(); michael@0: static void IOThread( void *p ); michael@0: michael@0: private: michael@0: bool mDone; michael@0: int mThreadNo; michael@0: FILE *mFile; michael@0: static int threadCount; michael@0: }; // class TestListener michael@0: michael@0: int TestListener::threadCount = 0; michael@0: michael@0: TestListener::TestListener() michael@0: : mDone( false ), mThreadNo( ++threadCount ) { michael@0: printf( "TestListener ctor called on thread %d\n", mThreadNo ); michael@0: } michael@0: michael@0: TestListener::~TestListener() { michael@0: printf( "TestListener dtor called on thread %d\n", mThreadNo ); michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS( TestListener, nsIStreamListener, nsIRequestObserver ) michael@0: michael@0: NS_IMETHODIMP michael@0: TestListener::OnStartRequest( nsIChannel *aChannel, nsISupports *aContext ) { michael@0: nsresult rv = NS_OK; michael@0: michael@0: printf( "TestListener::OnStartRequest called on thread %d\n", mThreadNo ); michael@0: michael@0: // Open output file. michael@0: char fileName[32]; michael@0: sprintf( fileName, "%s%d", "thread", mThreadNo ); michael@0: mFile = fopen( fileName, "wb" ); michael@0: setbuf( mFile, 0 ); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestListener::OnStopRequest( nsIChannel *aChannel, michael@0: nsISupports *aContext, michael@0: nsresult aStatus, michael@0: const char16_t *aMsg ) { michael@0: nsresult rv = NS_OK; michael@0: michael@0: printf( "TestListener::OnStopRequest called on thread %d\n", mThreadNo ); michael@0: michael@0: fclose( mFile ); michael@0: mDone = true; michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: TestListener::OnDataAvailable( nsIChannel *aChannel, michael@0: nsISupports *aContext, michael@0: nsIInputStream *aStream, michael@0: uint64_t offset, michael@0: uint32_t aLength ) { michael@0: nsresult rv = NS_OK; michael@0: michael@0: printf( "TestListener::OnDataAvailable called on thread %d\n", mThreadNo ); michael@0: michael@0: // Write the data to the console. michael@0: // Read a buffer full till aLength bytes have been processed. michael@0: char buffer[ 8192 ]; michael@0: unsigned long bytesRemaining = aLength; michael@0: while ( bytesRemaining ) { michael@0: unsigned int bytesRead; michael@0: // Read a buffer full or the number remaining (whichever is smaller). michael@0: rv = aStream->Read( buffer, michael@0: std::min( sizeof( buffer ), bytesRemaining ), michael@0: &bytesRead ); michael@0: if ( NS_SUCCEEDED( rv ) ) { michael@0: // Write the bytes just read to the output file. michael@0: fwrite( buffer, 1, bytesRead, mFile ); michael@0: bytesRemaining -= bytesRead; michael@0: } else { michael@0: printf( "%s %d: Read error, rv=0x%08X\n", michael@0: (char*)__FILE__, (int)__LINE__, (int)rv ); michael@0: break; michael@0: } michael@0: } michael@0: printf( "\n" ); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // IOThread: this function creates a new TestListener object (on the new michael@0: // thread), opens a channel, and does AsyncRead to it. michael@0: void michael@0: TestListener::IOThread( void *p ) { michael@0: printf( "I/O thread (0x%08X) started...\n", (int)(void*)PR_GetCurrentThread() ); michael@0: michael@0: // Argument is pointer to the nsIEventQueue for the main thread. michael@0: nsIEventQueue *mainThreadQ = static_cast(p); michael@0: michael@0: // Create channel for random web page. michael@0: nsCOMPtr channel = createChannel( (const char*)p ); michael@0: michael@0: if ( channel ) { michael@0: // Create event queue. michael@0: nsCOMPtr ioEventQ = createEventQueue(); michael@0: michael@0: if ( ioEventQ ) { michael@0: // Create test listener. michael@0: TestListener *testListener = new TestListener(); michael@0: testListener->AddRef(); michael@0: michael@0: // Read the channel. michael@0: printf( "Doing AsyncRead...\n" ); michael@0: nsresult rv = channel->AsyncRead( testListener, 0 ); michael@0: michael@0: if ( NS_SUCCEEDED( rv ) ) { michael@0: printf( "...AsyncRead completed OK\n" ); michael@0: michael@0: // Process events till testListener says stop. michael@0: printf( "Start event loop on io thread %d...\n", testListener->mThreadNo ); michael@0: while ( !testListener->mDone ) { michael@0: PLEvent *event; michael@0: ioEventQ->GetEvent( &event ); michael@0: ioEventQ->HandleEvent( event ); michael@0: } michael@0: printf( "...io thread %d event loop exiting\n", testListener->mThreadNo ); michael@0: } else { michael@0: printf( "%s %d: AsyncRead failed on thread %d, rv=0x%08X\n", michael@0: (char*)__FILE__, (int)__LINE__, testListener->mThreadNo, (int)rv ); michael@0: } michael@0: michael@0: // Release the test listener. michael@0: testListener->Release(); michael@0: } michael@0: } michael@0: michael@0: printf( "...I/O thread terminating\n" ); michael@0: } michael@0: michael@0: static const int maxThreads = 5; michael@0: michael@0: int michael@0: main( int argc, char* argv[] ) { michael@0: setbuf( stdout, 0 ); michael@0: if ( argc < 2 || argc > maxThreads + 1 ) { michael@0: printf( "usage: testThreadedIO url1 ...\n" michael@0: "where is a location to be loaded on a separate thread\n" michael@0: "limit is %d urls/threads", maxThreads ); michael@0: return -1; michael@0: } michael@0: michael@0: nsresult rv= (nsresult)-1; michael@0: michael@0: printf( "Test starting...\n" ); michael@0: michael@0: // Initialize XPCOM. michael@0: printf( "Initializing XPCOM...\n" ); michael@0: rv = NS_InitXPCOM2(nullptr, nullptr, nullptr); michael@0: if ( NS_FAILED( rv ) ) { michael@0: printf( "%s %d: NS_InitXPCOM failed, rv=0x%08X\n", michael@0: (char*)__FILE__, (int)__LINE__, (int)rv ); michael@0: return rv; michael@0: } michael@0: printf( "...XPCOM initialized OK\n" ); michael@0: // Create the Event Queue for this thread... michael@0: printf( "Creating event queue for main thread (0x%08X)...\n", michael@0: (int)(void*)PR_GetCurrentThread() ); michael@0: { michael@0: nsCOMPtr mainThreadQ = createEventQueue(); michael@0: michael@0: if ( mainThreadQ ) { michael@0: printf( "...main thread's event queue created OK\n" ); michael@0: michael@0: // Spawn threads to do I/O. michael@0: int goodThreads = 0; michael@0: PRThread *thread[ maxThreads ]; michael@0: for ( int threadNo = 1; threadNo < argc; threadNo++ ) { michael@0: printf( "Creating I/O thread %d to load %s...\n", threadNo, argv[threadNo] ); michael@0: PRThread *ioThread = PR_CreateThread( PR_USER_THREAD, michael@0: TestListener::IOThread, michael@0: argv[threadNo], michael@0: PR_PRIORITY_NORMAL, michael@0: PR_GLOBAL_THREAD, michael@0: PR_JOINABLE_THREAD, michael@0: 0 ); michael@0: if ( ioThread ) { michael@0: thread[ goodThreads++ ] = ioThread; michael@0: printf( "...I/O thread %d (0x%08X) created OK\n", michael@0: threadNo, (int)(void*)ioThread ); michael@0: } else { michael@0: printf( "%s %d: PR_CreateThread for thread %d failed\n", michael@0: (char*)__FILE__, (int)__LINE__, threadNo ); michael@0: } michael@0: } michael@0: michael@0: // Wait for all the threads to terminate. michael@0: for ( int joinThread = 0; joinThread < goodThreads; joinThread++ ) { michael@0: printf( "Waiting for thread %d to terminate...\n", joinThread+1 ); michael@0: PR_JoinThread( thread[ joinThread ] ); michael@0: } michael@0: } michael@0: } // this scopes the nsCOMPtrs michael@0: // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM michael@0: // Shut down XPCOM. michael@0: printf( "Shutting down XPCOM...\n" ); michael@0: NS_ShutdownXPCOM( 0 ); michael@0: printf( "...XPCOM shutdown complete\n" ); michael@0: michael@0: // Exit. michael@0: printf( "...test complete, rv=0x%08X\n", (int)rv ); michael@0: return rv; michael@0: }