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 "nsIThread.h" michael@0: #include "nsIThreadPool.h" michael@0: michael@0: #include "nsThreadUtils.h" michael@0: #include "nsXPCOMCIDInternal.h" michael@0: #include "pratom.h" michael@0: #include "prinrval.h" michael@0: #include "prmon.h" michael@0: #include "prthread.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include "mozilla/ReentrantMonitor.h" michael@0: using namespace mozilla; michael@0: michael@0: #define NUMBER_OF_THREADS 4 michael@0: michael@0: // One hour... because test boxes can be slow! michael@0: #define IDLE_THREAD_TIMEOUT 3600000 michael@0: michael@0: static nsIThread** gCreatedThreadList = nullptr; michael@0: static nsIThread** gShutDownThreadList = nullptr; michael@0: michael@0: static ReentrantMonitor* gReentrantMonitor = nullptr; michael@0: michael@0: static bool gAllRunnablesPosted = false; michael@0: static bool gAllThreadsCreated = false; michael@0: static bool gAllThreadsShutDown = false; 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: class Listener MOZ_FINAL : public nsIThreadPoolListener michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSITHREADPOOLLISTENER michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(Listener, nsIThreadPoolListener) michael@0: michael@0: NS_IMETHODIMP michael@0: Listener::OnThreadCreated() michael@0: { michael@0: nsCOMPtr current(do_GetCurrentThread()); michael@0: TEST_ASSERTION(current, "Couldn't get current thread!"); michael@0: michael@0: ReentrantMonitorAutoEnter mon(*gReentrantMonitor); michael@0: michael@0: while (!gAllRunnablesPosted) { michael@0: mon.Wait(); michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) { michael@0: nsIThread* thread = gCreatedThreadList[i]; michael@0: TEST_ASSERTION(thread != current, "Saw the same thread twice!"); michael@0: michael@0: if (!thread) { michael@0: gCreatedThreadList[i] = current; michael@0: if (i == (NUMBER_OF_THREADS - 1)) { michael@0: gAllThreadsCreated = true; michael@0: mon.NotifyAll(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: TEST_ASSERTION(false, "Too many threads!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Listener::OnThreadShuttingDown() michael@0: { michael@0: nsCOMPtr current(do_GetCurrentThread()); michael@0: TEST_ASSERTION(current, "Couldn't get current thread!"); michael@0: michael@0: ReentrantMonitorAutoEnter mon(*gReentrantMonitor); michael@0: michael@0: for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) { michael@0: nsIThread* thread = gShutDownThreadList[i]; michael@0: TEST_ASSERTION(thread != current, "Saw the same thread twice!"); michael@0: michael@0: if (!thread) { michael@0: gShutDownThreadList[i] = current; michael@0: if (i == (NUMBER_OF_THREADS - 1)) { michael@0: gAllThreadsShutDown = true; michael@0: mon.NotifyAll(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: TEST_ASSERTION(false, "Too many threads!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: class AutoCreateAndDestroyReentrantMonitor michael@0: { michael@0: public: michael@0: AutoCreateAndDestroyReentrantMonitor(ReentrantMonitor** aReentrantMonitorPtr) michael@0: : mReentrantMonitorPtr(aReentrantMonitorPtr) { michael@0: *aReentrantMonitorPtr = new ReentrantMonitor("TestThreadPoolListener::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: int main(int argc, char** argv) michael@0: { michael@0: ScopedXPCOM xpcom("ThreadPoolListener"); michael@0: NS_ENSURE_FALSE(xpcom.failed(), 1); michael@0: michael@0: nsIThread* createdThreadList[NUMBER_OF_THREADS] = { nullptr }; michael@0: gCreatedThreadList = createdThreadList; michael@0: michael@0: nsIThread* shutDownThreadList[NUMBER_OF_THREADS] = { nullptr }; michael@0: gShutDownThreadList = shutDownThreadList; michael@0: michael@0: AutoCreateAndDestroyReentrantMonitor newMon(&gReentrantMonitor); michael@0: NS_ENSURE_TRUE(gReentrantMonitor, 1); michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr pool = michael@0: do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: rv = pool->SetThreadLimit(NUMBER_OF_THREADS); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: rv = pool->SetIdleThreadLimit(NUMBER_OF_THREADS); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: rv = pool->SetIdleThreadTimeout(IDLE_THREAD_TIMEOUT); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: nsCOMPtr listener = new Listener(); michael@0: NS_ENSURE_TRUE(listener, 1); michael@0: michael@0: rv = pool->SetListener(listener); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter mon(*gReentrantMonitor); michael@0: michael@0: for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) { michael@0: nsCOMPtr runnable = new nsRunnable(); michael@0: NS_ENSURE_TRUE(runnable, 1); michael@0: michael@0: rv = pool->Dispatch(runnable, NS_DISPATCH_NORMAL); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: } michael@0: michael@0: gAllRunnablesPosted = true; michael@0: mon.NotifyAll(); michael@0: } michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter mon(*gReentrantMonitor); michael@0: while (!gAllThreadsCreated) { michael@0: mon.Wait(); michael@0: } michael@0: } michael@0: michael@0: rv = pool->Shutdown(); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: michael@0: { michael@0: ReentrantMonitorAutoEnter mon(*gReentrantMonitor); michael@0: while (!gAllThreadsShutDown) { michael@0: mon.Wait(); michael@0: } michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) { michael@0: nsIThread* created = gCreatedThreadList[i]; michael@0: NS_ENSURE_TRUE(created, 1); michael@0: michael@0: bool match = false; michael@0: for (uint32_t j = 0; j < NUMBER_OF_THREADS; j++) { michael@0: nsIThread* destroyed = gShutDownThreadList[j]; michael@0: NS_ENSURE_TRUE(destroyed, 1); michael@0: michael@0: if (destroyed == created) { michael@0: match = true; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: NS_ENSURE_TRUE(match, 1); michael@0: } michael@0: michael@0: return 0; michael@0: }