diff -r 000000000000 -r 6474c204b198 xpcom/tests/TestThreadPoolListener.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xpcom/tests/TestThreadPoolListener.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,219 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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 "TestHarness.h" + +#include "nsIThread.h" +#include "nsIThreadPool.h" + +#include "nsThreadUtils.h" +#include "nsXPCOMCIDInternal.h" +#include "pratom.h" +#include "prinrval.h" +#include "prmon.h" +#include "prthread.h" +#include "mozilla/Attributes.h" + +#include "mozilla/ReentrantMonitor.h" +using namespace mozilla; + +#define NUMBER_OF_THREADS 4 + +// One hour... because test boxes can be slow! +#define IDLE_THREAD_TIMEOUT 3600000 + +static nsIThread** gCreatedThreadList = nullptr; +static nsIThread** gShutDownThreadList = nullptr; + +static ReentrantMonitor* gReentrantMonitor = nullptr; + +static bool gAllRunnablesPosted = false; +static bool gAllThreadsCreated = false; +static bool gAllThreadsShutDown = false; + +#ifdef DEBUG +#define TEST_ASSERTION(_test, _msg) \ + NS_ASSERTION(_test, _msg); +#else +#define TEST_ASSERTION(_test, _msg) \ + PR_BEGIN_MACRO \ + if (!(_test)) { \ + NS_DebugBreak(NS_DEBUG_ABORT, _msg, #_test, __FILE__, __LINE__); \ + } \ + PR_END_MACRO +#endif + +class Listener MOZ_FINAL : public nsIThreadPoolListener +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSITHREADPOOLLISTENER +}; + +NS_IMPL_ISUPPORTS(Listener, nsIThreadPoolListener) + +NS_IMETHODIMP +Listener::OnThreadCreated() +{ + nsCOMPtr current(do_GetCurrentThread()); + TEST_ASSERTION(current, "Couldn't get current thread!"); + + ReentrantMonitorAutoEnter mon(*gReentrantMonitor); + + while (!gAllRunnablesPosted) { + mon.Wait(); + } + + for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) { + nsIThread* thread = gCreatedThreadList[i]; + TEST_ASSERTION(thread != current, "Saw the same thread twice!"); + + if (!thread) { + gCreatedThreadList[i] = current; + if (i == (NUMBER_OF_THREADS - 1)) { + gAllThreadsCreated = true; + mon.NotifyAll(); + } + return NS_OK; + } + } + + TEST_ASSERTION(false, "Too many threads!"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +Listener::OnThreadShuttingDown() +{ + nsCOMPtr current(do_GetCurrentThread()); + TEST_ASSERTION(current, "Couldn't get current thread!"); + + ReentrantMonitorAutoEnter mon(*gReentrantMonitor); + + for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) { + nsIThread* thread = gShutDownThreadList[i]; + TEST_ASSERTION(thread != current, "Saw the same thread twice!"); + + if (!thread) { + gShutDownThreadList[i] = current; + if (i == (NUMBER_OF_THREADS - 1)) { + gAllThreadsShutDown = true; + mon.NotifyAll(); + } + return NS_OK; + } + } + + TEST_ASSERTION(false, "Too many threads!"); + return NS_ERROR_FAILURE; +} + +class AutoCreateAndDestroyReentrantMonitor +{ +public: + AutoCreateAndDestroyReentrantMonitor(ReentrantMonitor** aReentrantMonitorPtr) + : mReentrantMonitorPtr(aReentrantMonitorPtr) { + *aReentrantMonitorPtr = new ReentrantMonitor("TestThreadPoolListener::AutoMon"); + TEST_ASSERTION(*aReentrantMonitorPtr, "Out of memory!"); + } + + ~AutoCreateAndDestroyReentrantMonitor() { + if (*mReentrantMonitorPtr) { + delete *mReentrantMonitorPtr; + *mReentrantMonitorPtr = nullptr; + } + } + +private: + ReentrantMonitor** mReentrantMonitorPtr; +}; + +int main(int argc, char** argv) +{ + ScopedXPCOM xpcom("ThreadPoolListener"); + NS_ENSURE_FALSE(xpcom.failed(), 1); + + nsIThread* createdThreadList[NUMBER_OF_THREADS] = { nullptr }; + gCreatedThreadList = createdThreadList; + + nsIThread* shutDownThreadList[NUMBER_OF_THREADS] = { nullptr }; + gShutDownThreadList = shutDownThreadList; + + AutoCreateAndDestroyReentrantMonitor newMon(&gReentrantMonitor); + NS_ENSURE_TRUE(gReentrantMonitor, 1); + + nsresult rv; + + nsCOMPtr pool = + do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, 1); + + rv = pool->SetThreadLimit(NUMBER_OF_THREADS); + NS_ENSURE_SUCCESS(rv, 1); + + rv = pool->SetIdleThreadLimit(NUMBER_OF_THREADS); + NS_ENSURE_SUCCESS(rv, 1); + + rv = pool->SetIdleThreadTimeout(IDLE_THREAD_TIMEOUT); + NS_ENSURE_SUCCESS(rv, 1); + + nsCOMPtr listener = new Listener(); + NS_ENSURE_TRUE(listener, 1); + + rv = pool->SetListener(listener); + NS_ENSURE_SUCCESS(rv, 1); + + { + ReentrantMonitorAutoEnter mon(*gReentrantMonitor); + + for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) { + nsCOMPtr runnable = new nsRunnable(); + NS_ENSURE_TRUE(runnable, 1); + + rv = pool->Dispatch(runnable, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, 1); + } + + gAllRunnablesPosted = true; + mon.NotifyAll(); + } + + { + ReentrantMonitorAutoEnter mon(*gReentrantMonitor); + while (!gAllThreadsCreated) { + mon.Wait(); + } + } + + rv = pool->Shutdown(); + NS_ENSURE_SUCCESS(rv, 1); + + { + ReentrantMonitorAutoEnter mon(*gReentrantMonitor); + while (!gAllThreadsShutDown) { + mon.Wait(); + } + } + + for (uint32_t i = 0; i < NUMBER_OF_THREADS; i++) { + nsIThread* created = gCreatedThreadList[i]; + NS_ENSURE_TRUE(created, 1); + + bool match = false; + for (uint32_t j = 0; j < NUMBER_OF_THREADS; j++) { + nsIThread* destroyed = gShutDownThreadList[j]; + NS_ENSURE_TRUE(destroyed, 1); + + if (destroyed == created) { + match = true; + break; + } + } + + NS_ENSURE_TRUE(match, 1); + } + + return 0; +}