/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Pierre Phaneuf * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include #include "nsXPCOM.h" #include "nsXPCOMCIDInternal.h" #include "nsIComponentManager.h" #include "nsIComponentRegistrar.h" #include "nsIServiceManager.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsCOMArray.h" #include "nscore.h" #include "nspr.h" #include "prmon.h" #include "nsITestProxy.h" #include "nsIRunnable.h" #include "nsIProxyObjectManager.h" #include "nsIThreadPool.h" #include "nsXPCOMCIDInternal.h" #include "nsComponentManagerUtils.h" #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" #include "prlog.h" #ifdef PR_LOGGING static PRLogModuleInfo *sLog = PR_NewLogModule("Test"); #define LOG(args) PR_LOG(sLog, PR_LOG_DEBUG, args) #else #define LOG(args) printf args #endif static nsresult GetThreadFromPRThread(PRThread *prthread, nsIThread **result) { LOG(("TEST: GetThreadFromPRThread [%p]\n", prthread)); nsCOMPtr tm = do_GetService(NS_THREADMANAGER_CONTRACTID); NS_ENSURE_STATE(tm); return tm->GetThreadFromPRThread(prthread, result); } /***************************************************************************/ /* nsTestXPCFoo */ /***************************************************************************/ class nsTestXPCFoo : public nsITestProxy { NS_DECL_ISUPPORTS NS_IMETHOD Test(PRInt32 p1, PRInt32 p2, PRInt32* retval); NS_IMETHOD Test2(); NS_IMETHOD Test3(nsISupports *p1, nsISupports **p2); nsTestXPCFoo(); }; nsTestXPCFoo::nsTestXPCFoo() { NS_ADDREF_THIS(); } NS_IMPL_ISUPPORTS1(nsTestXPCFoo, nsITestProxy) NS_IMETHODIMP nsTestXPCFoo::Test(PRInt32 p1, PRInt32 p2, PRInt32* retval) { LOG(("TEST: Thread (%d) Test Called successfully! Party on...\n", p1)); *retval = p1+p2; return NS_OK; } NS_IMETHODIMP nsTestXPCFoo::Test2() { LOG(("TEST: The quick brown netscape jumped over the old lazy ie..\n")); return NS_OK; } NS_IMETHODIMP nsTestXPCFoo::Test3(nsISupports *p1, nsISupports **p2) { if (p1 != nsnull) { nsITestProxy *test; p1->QueryInterface(NS_GET_IID(nsITestProxy), (void**)&test); test->Test2(); PRInt32 a; test->Test( 1, 2, &a); LOG(("TEST: \n1+2=%d\n",a)); } *p2 = new nsTestXPCFoo(); return NS_OK; } /***************************************************************************/ /* nsTestXPCFoo2 */ /***************************************************************************/ class nsTestXPCFoo2 : public nsITestProxy { NS_DECL_ISUPPORTS NS_IMETHOD Test(PRInt32 p1, PRInt32 p2, PRInt32* retval); NS_IMETHOD Test2(); NS_IMETHOD Test3(nsISupports *p1, nsISupports **p2); nsTestXPCFoo2(); }; nsTestXPCFoo2::nsTestXPCFoo2() { NS_ADDREF_THIS(); } NS_IMPL_THREADSAFE_ISUPPORTS1(nsTestXPCFoo2, nsITestProxy) NS_IMETHODIMP nsTestXPCFoo2::Test(PRInt32 p1, PRInt32 p2, PRInt32* retval) { LOG(("TEST: calling back to caller!\n")); nsCOMPtr manager = do_GetService(NS_XPCOMPROXY_CONTRACTID); LOG(("TEST: ProxyObjectManager: %p \n", (void *) manager.get())); PR_ASSERT(manager); nsCOMPtr thread; GetThreadFromPRThread((PRThread *) p1, getter_AddRefs(thread)); NS_ENSURE_STATE(thread); nsCOMPtr proxyObject; manager->GetProxyForObject(thread, NS_GET_IID(nsITestProxy), this, NS_PROXY_SYNC, (void**)&proxyObject); proxyObject->Test3(nsnull, nsnull); LOG(("TEST: Deleting Proxy Object\n")); return NS_OK; } NS_IMETHODIMP nsTestXPCFoo2::Test2() { LOG(("TEST: nsTestXPCFoo2::Test2() called\n")); return NS_OK; } NS_IMETHODIMP nsTestXPCFoo2::Test3(nsISupports *p1, nsISupports **p2) { LOG(("TEST: Got called")); return NS_OK; } #if 0 struct ArgsStruct { nsIThread* thread; PRInt32 threadNumber; }; // This will create two objects both descendants of a single IID. void TestCase_TwoClassesOneInterface(void *arg) { ArgsStruct *argsStruct = (ArgsStruct*) arg; nsCOMPtr manager = do_GetService(NS_XPCOMPROXY_CONTRACTID); printf("ProxyObjectManager: %p \n", (void *) manager.get()); PR_ASSERT(manager); nsITestProxy *proxyObject; nsITestProxy *proxyObject2; nsTestXPCFoo* foo = new nsTestXPCFoo(); nsTestXPCFoo2* foo2 = new nsTestXPCFoo2(); PR_ASSERT(foo); PR_ASSERT(foo2); manager->GetProxyForObject(argsStruct->thread, NS_GET_IID(nsITestProxy), foo, NS_PROXY_SYNC, (void**)&proxyObject); manager->GetProxyForObject(argsStruct->thread, NS_GET_IID(nsITestProxy), foo2, NS_PROXY_SYNC, (void**)&proxyObject2); if (proxyObject && proxyObject2) { // release ownership of the real object. PRInt32 a; nsresult rv; PRInt32 threadNumber = argsStruct->threadNumber; printf("Deleting real Object (%d)\n", threadNumber); NS_RELEASE(foo); printf("Deleting real Object 2 (%d)\n", threadNumber); NS_RELEASE(foo2); printf("Thread (%d) Prior to calling proxyObject->Test.\n", threadNumber); rv = proxyObject->Test(threadNumber, 0, &a); printf("Thread (%d) error: %d.\n", threadNumber, rv); printf("Thread (%d) Prior to calling proxyObject->Test2.\n", threadNumber); rv = proxyObject->Test2(); printf("Thread (%d) error: %d.\n", threadNumber, rv); printf("Thread (%d) Prior to calling proxyObject2->Test2.\n", threadNumber); rv = proxyObject2->Test2(); printf("Thread (%d) proxyObject2 error: %d.\n", threadNumber, rv); printf("Deleting Proxy Object (%d)\n", threadNumber ); NS_RELEASE(proxyObject); printf("Deleting Proxy Object 2 (%d)\n", threadNumber ); NS_RELEASE(proxyObject2); } PR_Sleep( PR_MillisecondsToInterval(1000) ); // If your thread goes away, your stack goes away. Only use ASYNC on calls that do not have out parameters } #endif void TestCase_NestedLoop(nsIThread *thread, PRInt32 index) { nsCOMPtr manager = do_GetService(NS_XPCOMPROXY_CONTRACTID); LOG(("TEST: ProxyObjectManager: %p\n", (void *) manager.get())); PR_ASSERT(manager); nsITestProxy *proxyObject; nsTestXPCFoo2* foo = new nsTestXPCFoo2(); PR_ASSERT(foo); manager->GetProxyForObject(thread, NS_GET_IID(nsITestProxy), foo, NS_PROXY_SYNC, (void**)&proxyObject); if (proxyObject) { // release ownership of the real object. nsresult rv; LOG(("TEST: Deleting real Object (%d)\n", index)); NS_RELEASE(foo); PRInt32 retval; LOG(("TEST: Getting EventThread...\n")); //nsCOMPtr curThread = do_GetCurrentThread(); PRThread *curThread = PR_GetCurrentThread(); if (curThread) { LOG(("TEST: Thread (%d) Prior to calling proxyObject->Test.\n", index)); rv = proxyObject->Test(NS_PTR_TO_INT32((void*)curThread), 0, &retval); // XXX broken on 64-bit arch LOG(("TEST: Thread (%d) proxyObject error: %x.\n", index, rv)); LOG(("TEST: Deleting Proxy Object (%d)\n", index)); NS_RELEASE(proxyObject); } PR_Sleep( PR_MillisecondsToInterval(1000) ); // If your thread goes away, your stack goes away. Only use ASYNC on calls that do not have out parameters } } #if 0 void TestCase_nsISupports(void *arg) { ArgsStruct *argsStruct = (ArgsStruct*) arg; nsCOMPtr manager = do_GetService(NS_XPCOMPROXY_CONTRACTID); PR_ASSERT(manager); nsITestProxy *proxyObject; nsTestXPCFoo* foo = new nsTestXPCFoo(); PR_ASSERT(foo); manager->GetProxyForObject(argsStruct->thread, NS_GET_IID(nsITestProxy), foo, NS_PROXY_SYNC, (void**)&proxyObject); if (proxyObject != nsnull) { nsISupports *bISupports = nsnull, *cISupports = nsnull; proxyObject->Test3(foo, &bISupports); proxyObject->Test3(bISupports, &cISupports); nsITestProxy *test; bISupports->QueryInterface(NS_GET_IID(nsITestProxy), (void**)&test); test->Test2(); NS_RELEASE(foo); NS_RELEASE(proxyObject); } } #endif /***************************************************************************/ /* ProxyTest */ /***************************************************************************/ class ProxyTest : public nsIRunnable { public: NS_DECL_ISUPPORTS ProxyTest(PRThread *eventLoopThread, PRInt32 index) : mEventLoopThread(eventLoopThread) , mIndex(index) {} NS_IMETHOD Run() { //TestCase_TwoClassesOneInterface(arg); //TestCase_nsISupports(arg); nsCOMPtr thread; GetThreadFromPRThread(mEventLoopThread, getter_AddRefs(thread)); TestCase_NestedLoop(thread, mIndex); return NS_OK; } private: PRThread *mEventLoopThread; PRInt32 mIndex; }; NS_IMPL_THREADSAFE_ISUPPORTS1(ProxyTest, nsIRunnable) class TestSyncProxyToSelf : public nsIRunnable { public: NS_DECL_ISUPPORTS NS_IMETHOD Run() { LOG(("TEST: Verifing calling Proxy on eventQ thread.\n")); nsCOMPtr thread = do_GetCurrentThread(); nsITestProxy *proxyObject; nsTestXPCFoo *foo = new nsTestXPCFoo(); NS_ENSURE_STATE(foo); nsCOMPtr manager = do_GetService(NS_XPCOMPROXY_CONTRACTID); manager->GetProxyForObject(thread, NS_GET_IID(nsITestProxy), foo, NS_PROXY_SYNC, (void**)&proxyObject); PRInt32 a; proxyObject->Test(1, 2, &a); proxyObject->Test2(); NS_RELEASE(proxyObject); delete foo; LOG(("TEST: End of Verification calling Proxy on eventQ thread.\n")); return NS_OK; } }; NS_IMPL_THREADSAFE_ISUPPORTS1(TestSyncProxyToSelf, nsIRunnable) //--------------------------------------------------------------------------- // Test to make sure we can call methods on a "main thread only" object from // a background thread. class MainThreadOnly : public nsIRunnable { public: NS_DECL_ISUPPORTS NS_IMETHOD Run() { NS_ASSERTION(NS_IsMainThread(), "method called on wrong thread"); *mNumRuns -= 1; return NS_OK; } MainThreadOnly(PRUint32 *numRuns) : mNumRuns(numRuns) {} ~MainThreadOnly() { NS_ASSERTION(NS_IsMainThread(), "method called on wrong thread"); } PRBool IsDone() { return mNumRuns == 0; } private: PRUint32 *mNumRuns; }; NS_IMPL_ISUPPORTS1(MainThreadOnly, nsIRunnable) // not threadsafe! static nsresult RunApartmentTest() { LOG(("RunApartmentTest: start\n")); const PRUint32 numDispatched = 160; PRUint32 numCompleted = 0; nsCOMPtr obj = new MainThreadOnly(&numCompleted); nsCOMPtr manager = do_GetService(NS_XPCOMPROXY_CONTRACTID); nsCOMPtr objProxy; manager->GetProxyForObject(NS_PROXY_TO_CURRENT_THREAD, NS_GET_IID(nsIRunnable), obj, NS_PROXY_ASYNC, getter_AddRefs(objProxy)); nsCOMPtr thread; NS_NewThread(getter_AddRefs(thread)); obj = nsnull; nsCOMPtr pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID); pool->SetThreadLimit(8); for (PRUint32 i = 0; i < numDispatched; ++i) pool->Dispatch(objProxy, NS_DISPATCH_NORMAL); objProxy = nsnull; nsCOMPtr curThread = do_GetCurrentThread(); while (numCompleted < numDispatched) { NS_ProcessNextEvent(curThread); } pool->Shutdown(); LOG(("RunApartmentTest: end\n")); return NS_OK; } int main(int argc, char **argv) { int numberOfThreads = 1; if (argc > 1) numberOfThreads = atoi(argv[1]); NS_InitXPCOM2(nsnull, nsnull, nsnull); // Scope code so everything is destroyed before we run call NS_ShutdownXPCOM { nsCOMPtr registrar; NS_GetComponentRegistrar(getter_AddRefs(registrar)); registrar->AutoRegister(nsnull); RunApartmentTest(); nsCOMPtr eventLoopThread; NS_NewThread(getter_AddRefs(eventLoopThread)); nsCOMPtr test = new TestSyncProxyToSelf(); eventLoopThread->Dispatch(test, NS_DISPATCH_NORMAL); PRThread *eventLoopPRThread; eventLoopThread->GetPRThread(&eventLoopPRThread); PR_ASSERT(eventLoopPRThread); LOG(("TEST: Spawn Threads:\n")); nsCOMArray threads; for (PRInt32 spawn = 0; spawn < numberOfThreads; spawn++) { test = new ProxyTest(eventLoopPRThread, spawn); nsCOMPtr thread; NS_NewThread(getter_AddRefs(thread), test); threads.AppendObject(thread); LOG(("TEST: \tThread (%d) spawned\n", spawn)); PR_Sleep( PR_MillisecondsToInterval(250) ); } LOG(("TEST: All Threads Spawned.\n")); LOG(("TEST: Wait for threads.\n")); for (PRInt32 i = 0; i < numberOfThreads; i++) { LOG(("TEST: Thread (%d) Join...\n", i)); nsresult rv = threads[i]->Shutdown(); LOG(("TEST: Thread (%d) Joined. (error: %x).\n", i, rv)); } LOG(("TEST: Shutting down event loop thread\n")); eventLoopThread->Shutdown(); } LOG(("TEST: Calling Cleanup.\n")); NS_ShutdownXPCOM(nsnull); LOG(("TEST: Return zero.\n")); return 0; }