summaryrefslogtreecommitdiffstats
path: root/xpcom/tests/gtest/TestSynchronization.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /xpcom/tests/gtest/TestSynchronization.cpp
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xpcom/tests/gtest/TestSynchronization.cpp')
-rw-r--r--xpcom/tests/gtest/TestSynchronization.cpp324
1 files changed, 324 insertions, 0 deletions
diff --git a/xpcom/tests/gtest/TestSynchronization.cpp b/xpcom/tests/gtest/TestSynchronization.cpp
new file mode 100644
index 0000000000..c4a1f5c99e
--- /dev/null
+++ b/xpcom/tests/gtest/TestSynchronization.cpp
@@ -0,0 +1,324 @@
+/* -*- 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();
+}