michael@0: /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 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: michael@0: #include "TestHarness.h" michael@0: michael@0: #include "nsIFactory.h" michael@0: #include "mozilla/Module.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "nsIThread.h" michael@0: #include "nsIComponentRegistrar.h" michael@0: michael@0: #include "nsAutoPtr.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsXPCOMCIDInternal.h" michael@0: #include "pratom.h" michael@0: #include "prmon.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include "mozilla/ReentrantMonitor.h" michael@0: using namespace mozilla; michael@0: michael@0: #ifdef DEBUG michael@0: #define TEST_ASSERTION(_test, _msg) \ michael@0: NS_ASSERTION(_test, _msg); michael@0: #else michael@0: #define TEST_ASSERTION(_test, _msg) \ michael@0: PR_BEGIN_MACRO \ michael@0: if (!(_test)) { \ michael@0: NS_DebugBreak(NS_DEBUG_ABORT, _msg, #_test, __FILE__, __LINE__); \ michael@0: } \ michael@0: PR_END_MACRO michael@0: #endif michael@0: michael@0: /* f93f6bdc-88af-42d7-9d64-1b43c649a3e5 */ michael@0: #define FACTORY_CID1 \ michael@0: { \ michael@0: 0xf93f6bdc, \ michael@0: 0x88af, \ michael@0: 0x42d7, \ michael@0: { 0x9d, 0x64, 0x1b, 0x43, 0xc6, 0x49, 0xa3, 0xe5 } \ michael@0: } michael@0: NS_DEFINE_CID(kFactoryCID1, FACTORY_CID1); michael@0: michael@0: /* ef38ad65-6595-49f0-8048-e819f81d15e2 */ michael@0: #define FACTORY_CID2 \ michael@0: { \ michael@0: 0xef38ad65, \ michael@0: 0x6595, \ michael@0: 0x49f0, \ michael@0: { 0x80, 0x48, 0xe8, 0x19, 0xf8, 0x1d, 0x15, 0xe2 } \ michael@0: } michael@0: NS_DEFINE_CID(kFactoryCID2, FACTORY_CID2); michael@0: michael@0: #define FACTORY_CONTRACTID \ michael@0: "TestRacingThreadManager/factory;1" michael@0: michael@0: int32_t gComponent1Count = 0; michael@0: int32_t gComponent2Count = 0; michael@0: michael@0: ReentrantMonitor* gReentrantMonitor = nullptr; michael@0: michael@0: bool gCreateInstanceCalled = false; michael@0: bool gMainThreadWaiting = false; michael@0: michael@0: class AutoCreateAndDestroyReentrantMonitor michael@0: { michael@0: public: michael@0: AutoCreateAndDestroyReentrantMonitor(ReentrantMonitor** aReentrantMonitorPtr) michael@0: : mReentrantMonitorPtr(aReentrantMonitorPtr) { michael@0: *aReentrantMonitorPtr = michael@0: new ReentrantMonitor("TestRacingServiceManager::AutoMon"); michael@0: TEST_ASSERTION(*aReentrantMonitorPtr, "Out of memory!"); michael@0: } michael@0: michael@0: ~AutoCreateAndDestroyReentrantMonitor() { michael@0: if (*mReentrantMonitorPtr) { michael@0: delete *mReentrantMonitorPtr; michael@0: *mReentrantMonitorPtr = nullptr; michael@0: } michael@0: } michael@0: michael@0: private: michael@0: ReentrantMonitor** mReentrantMonitorPtr; michael@0: }; michael@0: michael@0: class Factory MOZ_FINAL : public nsIFactory michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: Factory() : mFirstComponentCreated(false) { } michael@0: michael@0: NS_IMETHOD CreateInstance(nsISupports* aDelegate, michael@0: const nsIID& aIID, michael@0: void** aResult); michael@0: michael@0: NS_IMETHOD LockFactory(bool aLock) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool mFirstComponentCreated; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(Factory, nsIFactory) michael@0: michael@0: class Component1 MOZ_FINAL : public nsISupports michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: Component1() { michael@0: // This is the real test - make sure that only one instance is ever created. michael@0: int32_t count = PR_AtomicIncrement(&gComponent1Count); michael@0: TEST_ASSERTION(count == 1, "Too many components created!"); michael@0: } michael@0: }; michael@0: michael@0: NS_IMPL_ADDREF(Component1) michael@0: NS_IMPL_RELEASE(Component1) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(Component1) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: class Component2 MOZ_FINAL : public nsISupports michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: Component2() { michael@0: // This is the real test - make sure that only one instance is ever created. michael@0: int32_t count = PR_AtomicIncrement(&gComponent2Count); michael@0: TEST_ASSERTION(count == 1, "Too many components created!"); michael@0: } michael@0: }; michael@0: michael@0: NS_IMPL_ADDREF(Component2) michael@0: NS_IMPL_RELEASE(Component2) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(Component2) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMETHODIMP michael@0: Factory::CreateInstance(nsISupports* aDelegate, michael@0: const nsIID& aIID, michael@0: void** aResult) michael@0: { michael@0: // Make sure that the second thread beat the main thread to the getService michael@0: // call. michael@0: TEST_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter mon(*gReentrantMonitor); michael@0: michael@0: gCreateInstanceCalled = true; michael@0: mon.Notify(); michael@0: michael@0: mon.Wait(PR_MillisecondsToInterval(3000)); michael@0: } michael@0: michael@0: NS_ENSURE_FALSE(aDelegate, NS_ERROR_NO_AGGREGATION); michael@0: NS_ENSURE_ARG_POINTER(aResult); michael@0: michael@0: nsCOMPtr instance; michael@0: michael@0: if (!mFirstComponentCreated) { michael@0: instance = new Component1(); michael@0: } michael@0: else { michael@0: instance = new Component2(); michael@0: } michael@0: NS_ENSURE_TRUE(instance, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: nsresult rv = instance->QueryInterface(aIID, aResult); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class Runnable : public nsRunnable michael@0: { michael@0: public: michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: Runnable() : mFirstRunnableDone(false) { } michael@0: michael@0: bool mFirstRunnableDone; michael@0: }; michael@0: michael@0: NS_IMETHODIMP michael@0: Runnable::Run() michael@0: { michael@0: { michael@0: ReentrantMonitorAutoEnter mon(*gReentrantMonitor); michael@0: michael@0: while (!gMainThreadWaiting) { michael@0: mon.Wait(); michael@0: } michael@0: } michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr component; michael@0: michael@0: if (!mFirstRunnableDone) { michael@0: component = do_GetService(kFactoryCID1, &rv); michael@0: } michael@0: else { michael@0: component = do_GetService(FACTORY_CONTRACTID, &rv); michael@0: } michael@0: TEST_ASSERTION(NS_SUCCEEDED(rv), "GetService failed!"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static Factory* gFactory; michael@0: michael@0: static already_AddRefed michael@0: CreateFactory(const mozilla::Module& module, const mozilla::Module::CIDEntry& entry) michael@0: { michael@0: if (!gFactory) { michael@0: gFactory = new Factory(); michael@0: NS_ADDREF(gFactory); michael@0: } michael@0: nsCOMPtr ret = gFactory; michael@0: return ret.forget(); michael@0: } michael@0: michael@0: static const mozilla::Module::CIDEntry kLocalCIDs[] = { michael@0: { &kFactoryCID1, false, CreateFactory, nullptr }, michael@0: { &kFactoryCID2, false, CreateFactory, nullptr }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module::ContractIDEntry kLocalContracts[] = { michael@0: { FACTORY_CONTRACTID, &kFactoryCID2 }, michael@0: { nullptr } michael@0: }; michael@0: michael@0: static const mozilla::Module kLocalModule = { michael@0: mozilla::Module::kVersion, michael@0: kLocalCIDs, michael@0: kLocalContracts michael@0: }; michael@0: michael@0: int main(int argc, char** argv) michael@0: { michael@0: nsresult rv; michael@0: XRE_AddStaticComponent(&kLocalModule); michael@0: michael@0: ScopedXPCOM xpcom("RacingServiceManager"); michael@0: NS_ENSURE_FALSE(xpcom.failed(), 1); michael@0: michael@0: AutoCreateAndDestroyReentrantMonitor mon(&gReentrantMonitor); michael@0: michael@0: nsRefPtr runnable = new Runnable(); michael@0: NS_ENSURE_TRUE(runnable, 1); michael@0: michael@0: // Run the classID test michael@0: nsCOMPtr newThread; michael@0: rv = NS_NewThread(getter_AddRefs(newThread), runnable); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter mon(*gReentrantMonitor); michael@0: michael@0: gMainThreadWaiting = true; michael@0: mon.Notify(); michael@0: michael@0: while (!gCreateInstanceCalled) { michael@0: mon.Wait(); michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr component(do_GetService(kFactoryCID1, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: // Reset for the contractID test michael@0: gMainThreadWaiting = gCreateInstanceCalled = false; michael@0: gFactory->mFirstComponentCreated = runnable->mFirstRunnableDone = true; michael@0: component = nullptr; michael@0: michael@0: rv = newThread->Dispatch(runnable, NS_DISPATCH_NORMAL); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter mon(*gReentrantMonitor); michael@0: michael@0: gMainThreadWaiting = true; michael@0: mon.Notify(); michael@0: michael@0: while (!gCreateInstanceCalled) { michael@0: mon.Wait(); michael@0: } michael@0: } michael@0: michael@0: component = do_GetService(FACTORY_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: NS_RELEASE(gFactory); michael@0: michael@0: return 0; michael@0: }