michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: sw=4 ts=4 et : 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 "mozilla/CondVar.h" michael@0: #include "mozilla/Monitor.h" michael@0: #include "mozilla/Mutex.h" michael@0: #include "nsAutoLock.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: static PRThread* michael@0: spawn(void (*run)(void*), void* arg) michael@0: { michael@0: return PR_CreateThread(PR_SYSTEM_THREAD, michael@0: run, michael@0: arg, michael@0: PR_PRIORITY_NORMAL, michael@0: PR_GLOBAL_THREAD, michael@0: PR_JOINABLE_THREAD, michael@0: 0); michael@0: } michael@0: michael@0: michael@0: #define PASS() \ michael@0: do { \ michael@0: passed(__FUNCTION__); \ michael@0: return NS_OK; \ michael@0: } while (0) michael@0: michael@0: #define FAIL(why) \ michael@0: do { \ michael@0: fail("%s | %s - %s", __FILE__, __FUNCTION__, why); \ michael@0: return NS_ERROR_FAILURE; \ michael@0: } while (0) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // Sanity check: tests that can be done on a single thread michael@0: // michael@0: static nsresult michael@0: Sanity() michael@0: { michael@0: Mutex lock("sanity::lock"); michael@0: lock.Lock(); michael@0: lock.AssertCurrentThreadOwns(); michael@0: lock.Unlock(); michael@0: michael@0: { michael@0: MutexAutoLock autolock(lock); michael@0: lock.AssertCurrentThreadOwns(); michael@0: } michael@0: michael@0: lock.Lock(); michael@0: lock.AssertCurrentThreadOwns(); michael@0: { michael@0: MutexAutoUnlock autounlock(lock); michael@0: } michael@0: lock.AssertCurrentThreadOwns(); michael@0: lock.Unlock(); michael@0: michael@0: Monitor mon("sanity::monitor"); michael@0: mon.Enter(); michael@0: mon.AssertCurrentThreadIn(); michael@0: mon.Enter(); michael@0: mon.AssertCurrentThreadIn(); michael@0: mon.Exit(); michael@0: mon.AssertCurrentThreadIn(); michael@0: mon.Exit(); michael@0: michael@0: { michael@0: MonitorAutoEnter automon(mon); michael@0: mon.AssertCurrentThreadIn(); michael@0: } michael@0: michael@0: PASS(); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // Mutex contention tests michael@0: // michael@0: static Mutex* gLock1; michael@0: michael@0: static void michael@0: MutexContention_thread(void* /*arg*/) michael@0: { michael@0: for (int i = 0; i < 100000; ++i) { michael@0: gLock1->Lock(); michael@0: gLock1->AssertCurrentThreadOwns(); michael@0: gLock1->Unlock(); michael@0: } michael@0: } michael@0: michael@0: static nsresult michael@0: MutexContention() michael@0: { michael@0: gLock1 = new Mutex("lock1"); michael@0: // PURPOSELY not checking for OOM. YAY! michael@0: michael@0: PRThread* t1 = spawn(MutexContention_thread, nullptr); michael@0: PRThread* t2 = spawn(MutexContention_thread, nullptr); michael@0: PRThread* t3 = spawn(MutexContention_thread, nullptr); michael@0: michael@0: PR_JoinThread(t1); michael@0: PR_JoinThread(t2); michael@0: PR_JoinThread(t3); michael@0: michael@0: delete gLock1; michael@0: michael@0: PASS(); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // Monitor tests michael@0: // michael@0: static Monitor* gMon1; michael@0: michael@0: static void michael@0: MonitorContention_thread(void* /*arg*/) michael@0: { michael@0: for (int i = 0; i < 100000; ++i) { michael@0: gMon1->Enter(); michael@0: gMon1->AssertCurrentThreadIn(); michael@0: gMon1->Exit(); michael@0: } michael@0: } michael@0: michael@0: static nsresult michael@0: MonitorContention() michael@0: { michael@0: gMon1 = new Monitor("mon1"); michael@0: michael@0: PRThread* t1 = spawn(MonitorContention_thread, nullptr); michael@0: PRThread* t2 = spawn(MonitorContention_thread, nullptr); michael@0: PRThread* t3 = spawn(MonitorContention_thread, nullptr); michael@0: michael@0: PR_JoinThread(t1); michael@0: PR_JoinThread(t2); michael@0: PR_JoinThread(t3); michael@0: michael@0: delete gMon1; michael@0: michael@0: PASS(); michael@0: } michael@0: michael@0: michael@0: static Monitor* gMon2; michael@0: michael@0: static void michael@0: MonitorContention2_thread(void* /*arg*/) michael@0: { michael@0: for (int i = 0; i < 100000; ++i) { michael@0: gMon2->Enter(); michael@0: gMon2->AssertCurrentThreadIn(); michael@0: { michael@0: gMon2->Enter(); michael@0: gMon2->AssertCurrentThreadIn(); michael@0: gMon2->Exit(); michael@0: } michael@0: gMon2->AssertCurrentThreadIn(); michael@0: gMon2->Exit(); michael@0: } michael@0: } michael@0: michael@0: static nsresult michael@0: MonitorContention2() michael@0: { michael@0: gMon2 = new Monitor("mon1"); michael@0: michael@0: PRThread* t1 = spawn(MonitorContention2_thread, nullptr); michael@0: PRThread* t2 = spawn(MonitorContention2_thread, nullptr); michael@0: PRThread* t3 = spawn(MonitorContention2_thread, nullptr); michael@0: michael@0: PR_JoinThread(t1); michael@0: PR_JoinThread(t2); michael@0: PR_JoinThread(t3); michael@0: michael@0: delete gMon2; michael@0: michael@0: PASS(); michael@0: } michael@0: michael@0: michael@0: static Monitor* gMon3; michael@0: static int32_t gMonFirst; michael@0: michael@0: static void michael@0: MonitorSyncSanity_thread(void* /*arg*/) michael@0: { michael@0: gMon3->Enter(); michael@0: gMon3->AssertCurrentThreadIn(); michael@0: if (gMonFirst) { michael@0: gMonFirst = 0; michael@0: gMon3->Wait(); michael@0: gMon3->Enter(); michael@0: } else { michael@0: gMon3->Notify(); michael@0: gMon3->Enter(); michael@0: } michael@0: gMon3->AssertCurrentThreadIn(); michael@0: gMon3->Exit(); michael@0: gMon3->AssertCurrentThreadIn(); michael@0: gMon3->Exit(); michael@0: } michael@0: michael@0: static nsresult michael@0: MonitorSyncSanity() michael@0: { michael@0: gMon3 = new Monitor("monitor::syncsanity"); michael@0: michael@0: for (int32_t i = 0; i < 10000; ++i) { michael@0: gMonFirst = 1; michael@0: PRThread* ping = spawn(MonitorSyncSanity_thread, nullptr); michael@0: PRThread* pong = spawn(MonitorSyncSanity_thread, nullptr); michael@0: PR_JoinThread(ping); michael@0: PR_JoinThread(pong); michael@0: } michael@0: michael@0: delete gMon3; michael@0: michael@0: PASS(); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // Condvar tests michael@0: // michael@0: static Mutex* gCvlock1; michael@0: static CondVar* gCv1; michael@0: static int32_t gCvFirst; michael@0: michael@0: static void michael@0: CondVarSanity_thread(void* /*arg*/) michael@0: { michael@0: gCvlock1->Lock(); michael@0: gCvlock1->AssertCurrentThreadOwns(); michael@0: if (gCvFirst) { michael@0: gCvFirst = 0; michael@0: gCv1->Wait(); michael@0: } else { michael@0: gCv1->Notify(); michael@0: } michael@0: gCvlock1->AssertCurrentThreadOwns(); michael@0: gCvlock1->Unlock(); michael@0: } michael@0: michael@0: static nsresult michael@0: CondVarSanity() michael@0: { michael@0: gCvlock1 = new Mutex("cvlock1"); michael@0: gCv1 = new CondVar(*gCvlock1, "cvlock1"); michael@0: michael@0: for (int32_t i = 0; i < 10000; ++i) { michael@0: gCvFirst = 1; michael@0: PRThread* ping = spawn(CondVarSanity_thread, nullptr); michael@0: PRThread* pong = spawn(CondVarSanity_thread, nullptr); michael@0: PR_JoinThread(ping); michael@0: PR_JoinThread(pong); michael@0: } michael@0: michael@0: delete gCv1; michael@0: delete gCvlock1; michael@0: michael@0: PASS(); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // AutoLock tests michael@0: // michael@0: static nsresult michael@0: AutoLock() michael@0: { michael@0: Mutex l1("autolock"); michael@0: MutexAutoLock autol1(l1); michael@0: michael@0: l1.AssertCurrentThreadOwns(); michael@0: michael@0: { michael@0: Mutex l2("autolock2"); michael@0: MutexAutoLock autol2(l2); michael@0: michael@0: l1.AssertCurrentThreadOwns(); michael@0: l2.AssertCurrentThreadOwns(); michael@0: } michael@0: michael@0: l1.AssertCurrentThreadOwns(); michael@0: michael@0: PASS(); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // AutoUnlock tests michael@0: // michael@0: static nsresult michael@0: AutoUnlock() michael@0: { michael@0: Mutex l1("autounlock"); michael@0: Mutex l2("autounlock2"); michael@0: michael@0: l1.Lock(); michael@0: l1.AssertCurrentThreadOwns(); michael@0: michael@0: { michael@0: MutexAutoUnlock autol1(l1); michael@0: { michael@0: l2.Lock(); michael@0: l2.AssertCurrentThreadOwns(); michael@0: michael@0: MutexAutoUnlock autol2(l2); michael@0: } michael@0: l2.AssertCurrentThreadOwns(); michael@0: l2.Unlock(); michael@0: } michael@0: l1.AssertCurrentThreadOwns(); michael@0: michael@0: l1.Unlock(); michael@0: michael@0: PASS(); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // AutoMonitor tests michael@0: // michael@0: static nsresult michael@0: AutoMonitor() michael@0: { michael@0: Monitor m1("automonitor"); michael@0: Monitor m2("automonitor2"); michael@0: michael@0: m1.Enter(); michael@0: m1.AssertCurrentThreadIn(); michael@0: { michael@0: MonitorAutoEnter autom1(m1); michael@0: m1.AssertCurrentThreadIn(); michael@0: michael@0: m2.Enter(); michael@0: m2.AssertCurrentThreadIn(); michael@0: { michael@0: MonitorAutoEnter autom2(m2); michael@0: m1.AssertCurrentThreadIn(); michael@0: m2.AssertCurrentThreadIn(); michael@0: } michael@0: m2.AssertCurrentThreadIn(); michael@0: m2.Exit(); michael@0: michael@0: m1.AssertCurrentThreadIn(); michael@0: } michael@0: m1.AssertCurrentThreadIn(); michael@0: m1.Exit(); michael@0: michael@0: PASS(); michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: int michael@0: main(int argc, char** argv) michael@0: { michael@0: ScopedXPCOM xpcom("Synchronization (" __FILE__ ")"); michael@0: if (xpcom.failed()) michael@0: return 1; michael@0: michael@0: int rv = 0; michael@0: michael@0: if (NS_FAILED(Sanity())) michael@0: rv = 1; michael@0: if (NS_FAILED(MutexContention())) michael@0: rv = 1; michael@0: if (NS_FAILED(MonitorContention())) michael@0: rv = 1; michael@0: if (NS_FAILED(MonitorContention2())) michael@0: rv = 1; michael@0: if (NS_FAILED(MonitorSyncSanity())) michael@0: rv = 1; michael@0: if (NS_FAILED(CondVarSanity())) michael@0: rv = 1; michael@0: if (NS_FAILED(AutoLock())) michael@0: rv = 1; michael@0: if (NS_FAILED(AutoUnlock())) michael@0: rv = 1; michael@0: if (NS_FAILED(AutoMonitor())) michael@0: rv = 1; michael@0: michael@0: return rv; michael@0: }