Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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();
}