Mypal/xpcom/tests/gtest/TestSynchronization.cpp
2021-02-04 16:48:36 +02:00

313 lines
6.5 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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)
{
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*/)
{
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*/)
{
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("autolock");
MutexAutoLock autol1(l1);
l1.AssertCurrentThreadOwns();
{
Mutex l2("autolock2");
MutexAutoLock autol2(l2);
l1.AssertCurrentThreadOwns();
l2.AssertCurrentThreadOwns();
}
l1.AssertCurrentThreadOwns();
}
//-----------------------------------------------------------------------------
// AutoUnlock tests
//
TEST(Synchronization, AutoUnlock)
{
Mutex l1("autounlock");
Mutex l2("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)
{
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();
}