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