/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "mozilla/CondVar.h" #include "mozilla/Monitor.h" #include "mozilla/ReentrantMonitor.h" #include "mozilla/Mutex.h" #include "gtest/gtest.h" using namespace mozilla; static PRThread* spawn(void (*run)(void*), void* arg) { return PR_CreateThread(PR_SYSTEM_THREAD, run, arg, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); } //----------------------------------------------------------------------------- // Sanity check: tests that can be done on a single thread // TEST(Synchronization, Sanity) MOZ_NO_THREAD_SAFETY_ANALYSIS { Mutex lock("sanity::lock"); lock.Lock(); lock.AssertCurrentThreadOwns(); lock.Unlock(); { MutexAutoLock autolock(lock); lock.AssertCurrentThreadOwns(); } lock.Lock(); lock.AssertCurrentThreadOwns(); { MutexAutoUnlock autounlock(lock); } lock.AssertCurrentThreadOwns(); lock.Unlock(); ReentrantMonitor mon("sanity::monitor"); mon.Enter(); mon.AssertCurrentThreadIn(); mon.Enter(); mon.AssertCurrentThreadIn(); mon.Exit(); mon.AssertCurrentThreadIn(); mon.Exit(); { ReentrantMonitorAutoEnter automon(mon); mon.AssertCurrentThreadIn(); } } //----------------------------------------------------------------------------- // Mutex contention tests // static Mutex* gLock1; static void MutexContention_thread(void* /*arg*/) { for (int i = 0; i < 100000; ++i) { gLock1->Lock(); gLock1->AssertCurrentThreadOwns(); gLock1->Unlock(); } } TEST(Synchronization, MutexContention) { gLock1 = new Mutex("lock1"); // PURPOSELY not checking for OOM. YAY! PRThread* t1 = spawn(MutexContention_thread, nullptr); PRThread* t2 = spawn(MutexContention_thread, nullptr); PRThread* t3 = spawn(MutexContention_thread, nullptr); PR_JoinThread(t1); PR_JoinThread(t2); PR_JoinThread(t3); delete gLock1; } //----------------------------------------------------------------------------- // Monitor tests // static Monitor* gMon1; static void MonitorContention_thread(void* /*arg*/) { for (int i = 0; i < 100000; ++i) { gMon1->Lock(); gMon1->AssertCurrentThreadOwns(); gMon1->Unlock(); } } TEST(Synchronization, MonitorContention) { gMon1 = new Monitor("mon1"); PRThread* t1 = spawn(MonitorContention_thread, nullptr); PRThread* t2 = spawn(MonitorContention_thread, nullptr); PRThread* t3 = spawn(MonitorContention_thread, nullptr); PR_JoinThread(t1); PR_JoinThread(t2); PR_JoinThread(t3); delete gMon1; } static ReentrantMonitor* gMon2; static void MonitorContention2_thread(void* /*arg*/) MOZ_NO_THREAD_SAFETY_ANALYSIS { for (int i = 0; i < 100000; ++i) { gMon2->Enter(); gMon2->AssertCurrentThreadIn(); { gMon2->Enter(); gMon2->AssertCurrentThreadIn(); gMon2->Exit(); } gMon2->AssertCurrentThreadIn(); gMon2->Exit(); } } TEST(Synchronization, MonitorContention2) { gMon2 = new ReentrantMonitor("mon1"); PRThread* t1 = spawn(MonitorContention2_thread, nullptr); PRThread* t2 = spawn(MonitorContention2_thread, nullptr); PRThread* t3 = spawn(MonitorContention2_thread, nullptr); PR_JoinThread(t1); PR_JoinThread(t2); PR_JoinThread(t3); delete gMon2; } static ReentrantMonitor* gMon3; static int32_t gMonFirst; static void MonitorSyncSanity_thread(void* /*arg*/) MOZ_NO_THREAD_SAFETY_ANALYSIS { gMon3->Enter(); gMon3->AssertCurrentThreadIn(); if (gMonFirst) { gMonFirst = 0; gMon3->Wait(); gMon3->Enter(); } else { gMon3->Notify(); gMon3->Enter(); } gMon3->AssertCurrentThreadIn(); gMon3->Exit(); gMon3->AssertCurrentThreadIn(); gMon3->Exit(); } TEST(Synchronization, MonitorSyncSanity) { gMon3 = new ReentrantMonitor("monitor::syncsanity"); for (int32_t i = 0; i < 10000; ++i) { gMonFirst = 1; PRThread* ping = spawn(MonitorSyncSanity_thread, nullptr); PRThread* pong = spawn(MonitorSyncSanity_thread, nullptr); PR_JoinThread(ping); PR_JoinThread(pong); } delete gMon3; } //----------------------------------------------------------------------------- // Condvar tests // static Mutex* gCvlock1; static CondVar* gCv1; static int32_t gCvFirst; static void CondVarSanity_thread(void* /*arg*/) { gCvlock1->Lock(); gCvlock1->AssertCurrentThreadOwns(); if (gCvFirst) { gCvFirst = 0; gCv1->Wait(); } else { gCv1->Notify(); } gCvlock1->AssertCurrentThreadOwns(); gCvlock1->Unlock(); } TEST(Synchronization, CondVarSanity) { gCvlock1 = new Mutex("cvlock1"); gCv1 = new CondVar(*gCvlock1, "cvlock1"); for (int32_t i = 0; i < 10000; ++i) { gCvFirst = 1; PRThread* ping = spawn(CondVarSanity_thread, nullptr); PRThread* pong = spawn(CondVarSanity_thread, nullptr); PR_JoinThread(ping); PR_JoinThread(pong); } delete gCv1; delete gCvlock1; } //----------------------------------------------------------------------------- // AutoLock tests // TEST(Synchronization, AutoLock) { Mutex l1 MOZ_UNANNOTATED("autolock"); MutexAutoLock autol1(l1); l1.AssertCurrentThreadOwns(); { Mutex l2 MOZ_UNANNOTATED("autolock2"); MutexAutoLock autol2(l2); l1.AssertCurrentThreadOwns(); l2.AssertCurrentThreadOwns(); } l1.AssertCurrentThreadOwns(); } //----------------------------------------------------------------------------- // AutoTryLock tests // // The thread owns assertions make mutex analysis throw spurious warnings TEST(Synchronization, AutoTryLock) MOZ_NO_THREAD_SAFETY_ANALYSIS { Mutex l1 MOZ_UNANNOTATED("autotrylock"); MutexAutoTryLock autol1(l1); EXPECT_TRUE(autol1); l1.AssertCurrentThreadOwns(); MutexAutoTryLock autol2(l1); EXPECT_TRUE(autol1); EXPECT_FALSE(autol2); l1.AssertCurrentThreadOwns(); { Mutex l2 MOZ_UNANNOTATED("autotrylock2"); MutexAutoTryLock autol3(l2); EXPECT_TRUE(autol3); l1.AssertCurrentThreadOwns(); l2.AssertCurrentThreadOwns(); } l1.AssertCurrentThreadOwns(); } //----------------------------------------------------------------------------- // AutoUnlock tests // TEST(Synchronization, AutoUnlock) { Mutex l1 MOZ_UNANNOTATED("autounlock"); Mutex l2 MOZ_UNANNOTATED("autounlock2"); l1.Lock(); l1.AssertCurrentThreadOwns(); { MutexAutoUnlock autol1(l1); { l2.Lock(); l2.AssertCurrentThreadOwns(); MutexAutoUnlock autol2(l2); } l2.AssertCurrentThreadOwns(); l2.Unlock(); } l1.AssertCurrentThreadOwns(); l1.Unlock(); } //----------------------------------------------------------------------------- // AutoMonitor tests // TEST(Synchronization, AutoMonitor) MOZ_NO_THREAD_SAFETY_ANALYSIS { ReentrantMonitor m1("automonitor"); ReentrantMonitor m2("automonitor2"); m1.Enter(); m1.AssertCurrentThreadIn(); { ReentrantMonitorAutoEnter autom1(m1); m1.AssertCurrentThreadIn(); m2.Enter(); m2.AssertCurrentThreadIn(); { ReentrantMonitorAutoEnter autom2(m2); m1.AssertCurrentThreadIn(); m2.AssertCurrentThreadIn(); } m2.AssertCurrentThreadIn(); m2.Exit(); m1.AssertCurrentThreadIn(); } m1.AssertCurrentThreadIn(); m1.Exit(); }