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 "nsITimer.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsThreadUtils.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: typedef nsresult(*TestFuncPtr)(); michael@0: michael@0: class AutoTestThread michael@0: { michael@0: public: michael@0: AutoTestThread() { michael@0: nsCOMPtr newThread; michael@0: nsresult rv = NS_NewThread(getter_AddRefs(newThread)); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: newThread.swap(mThread); michael@0: } michael@0: michael@0: ~AutoTestThread() { michael@0: mThread->Shutdown(); michael@0: } michael@0: michael@0: operator nsIThread*() const { michael@0: return mThread; michael@0: } michael@0: michael@0: nsIThread* operator->() const { michael@0: return mThread; michael@0: } michael@0: michael@0: private: michael@0: nsCOMPtr mThread; michael@0: }; michael@0: michael@0: class AutoCreateAndDestroyReentrantMonitor michael@0: { michael@0: public: michael@0: AutoCreateAndDestroyReentrantMonitor() { michael@0: mReentrantMonitor = new ReentrantMonitor("TestTimers::AutoMon"); michael@0: NS_ASSERTION(mReentrantMonitor, "Out of memory!"); michael@0: } michael@0: michael@0: ~AutoCreateAndDestroyReentrantMonitor() { michael@0: delete mReentrantMonitor; michael@0: } michael@0: michael@0: operator ReentrantMonitor* () { michael@0: return mReentrantMonitor; michael@0: } michael@0: michael@0: private: michael@0: ReentrantMonitor* mReentrantMonitor; michael@0: }; michael@0: michael@0: class TimerCallback MOZ_FINAL : public nsITimerCallback michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: michael@0: TimerCallback(nsIThread** aThreadPtr, ReentrantMonitor* aReentrantMonitor) michael@0: : mThreadPtr(aThreadPtr), mReentrantMonitor(aReentrantMonitor) { } michael@0: michael@0: NS_IMETHOD Notify(nsITimer* aTimer) { michael@0: NS_ASSERTION(mThreadPtr, "Callback was not supposed to be called!"); michael@0: nsCOMPtr current(do_GetCurrentThread()); michael@0: michael@0: ReentrantMonitorAutoEnter mon(*mReentrantMonitor); michael@0: michael@0: NS_ASSERTION(!*mThreadPtr, "Timer called back more than once!"); michael@0: *mThreadPtr = current; michael@0: michael@0: mon.Notify(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: private: michael@0: nsIThread** mThreadPtr; michael@0: ReentrantMonitor* mReentrantMonitor; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(TimerCallback, nsITimerCallback) michael@0: michael@0: nsresult michael@0: TestTargetedTimers() michael@0: { michael@0: AutoCreateAndDestroyReentrantMonitor newMon; michael@0: NS_ENSURE_TRUE(newMon, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: AutoTestThread testThread; michael@0: NS_ENSURE_TRUE(testThread, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsIEventTarget* target = static_cast(testThread); michael@0: michael@0: rv = timer->SetTarget(target); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsIThread* notifiedThread = nullptr; michael@0: michael@0: nsCOMPtr callback = michael@0: new TimerCallback(¬ifiedThread, newMon); michael@0: NS_ENSURE_TRUE(callback, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: rv = timer->InitWithCallback(callback, PR_MillisecondsToInterval(2000), michael@0: nsITimer::TYPE_ONE_SHOT); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: ReentrantMonitorAutoEnter mon(*newMon); michael@0: while (!notifiedThread) { michael@0: mon.Wait(); michael@0: } michael@0: NS_ENSURE_TRUE(notifiedThread == testThread, NS_ERROR_FAILURE); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: TestTimerWithStoppedTarget() michael@0: { michael@0: AutoTestThread testThread; michael@0: NS_ENSURE_TRUE(testThread, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: nsresult rv; michael@0: nsCOMPtr timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsIEventTarget* target = static_cast(testThread); michael@0: michael@0: rv = timer->SetTarget(target); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // If this is called, we'll assert michael@0: nsCOMPtr callback = michael@0: new TimerCallback(nullptr, nullptr); michael@0: NS_ENSURE_TRUE(callback, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: rv = timer->InitWithCallback(callback, PR_MillisecondsToInterval(100), michael@0: nsITimer::TYPE_ONE_SHOT); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: testThread->Shutdown(); michael@0: michael@0: PR_Sleep(400); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: int main(int argc, char** argv) michael@0: { michael@0: ScopedXPCOM xpcom("TestTimers"); michael@0: NS_ENSURE_FALSE(xpcom.failed(), 1); michael@0: michael@0: static TestFuncPtr testsToRun[] = { michael@0: TestTargetedTimers, michael@0: TestTimerWithStoppedTarget michael@0: }; michael@0: static uint32_t testCount = sizeof(testsToRun) / sizeof(testsToRun[0]); michael@0: michael@0: for (uint32_t i = 0; i < testCount; i++) { michael@0: nsresult rv = testsToRun[i](); michael@0: NS_ENSURE_SUCCESS(rv, 1); michael@0: } michael@0: michael@0: return 0; michael@0: }