Bug 1397052 - Add gtest for concurrent accessing. r=froydnj
authorXidorn Quan <me@upsuper.org>
Thu, 07 Sep 2017 10:08:31 +1000
changeset 429146 44fb9477f42fe368b4c9ebd968887439d70dd93d
parent 429097 dd75dcec7da162d8ceaaf0883e0e7561bd772992
child 429147 fcf98211c15147988c652193dd7d65b8c292a16d
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1397052
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1397052 - Add gtest for concurrent accessing. r=froydnj MozReview-Commit-ID: K3enhwjyGYE
xpcom/ds/nsAtomTable.cpp
xpcom/tests/gtest/TestAtoms.cpp
xpcom/tests/gtest/TestAutoRefCnt.cpp
xpcom/tests/gtest/TestEventPriorities.cpp
xpcom/tests/gtest/moz.build
--- a/xpcom/ds/nsAtomTable.cpp
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -791,16 +791,22 @@ NS_AtomizeMainThread(const nsAString& aU
 nsrefcnt
 NS_GetNumberOfAtoms(void)
 {
   Atom::GCAtomTable(); // Trigger a GC so that we return a deterministic result.
   MutexAutoLock lock(*gAtomTableLock);
   return gAtomTable->EntryCount();
 }
 
+uint32_t
+NS_GetUnusedAtomCount(void)
+{
+  return gUnusedAtomCount;
+}
+
 nsIAtom*
 NS_GetStaticAtom(const nsAString& aUTF16String)
 {
   NS_PRECONDITION(gStaticAtomTable, "Static atom table not created yet.");
   NS_PRECONDITION(gStaticAtomTableSealed, "Static atom table not sealed yet.");
   StaticAtomEntry* entry = gStaticAtomTable->GetEntry(aUTF16String);
   return entry ? entry->mAtom : nullptr;
 }
--- a/xpcom/tests/gtest/TestAtoms.cpp
+++ b/xpcom/tests/gtest/TestAtoms.cpp
@@ -6,21 +6,24 @@
 
 #include "mozilla/ArrayUtils.h"
 
 #include "nsIAtom.h"
 #include "nsString.h"
 #include "UTFStrings.h"
 #include "nsIServiceManager.h"
 #include "nsStaticAtom.h"
+#include "nsThreadUtils.h"
 
 #include "gtest/gtest.h"
 
 using namespace mozilla;
 
+uint32_t NS_GetUnusedAtomCount(void);
+
 namespace TestAtoms {
 
 TEST(Atoms, Basic)
 {
   for (unsigned int i = 0; i < ArrayLength(ValidStrings); ++i) {
     nsDependentString str16(ValidStrings[i].m16);
     nsDependentCString str8(ValidStrings[i].m8);
 
@@ -145,9 +148,46 @@ TEST(Atoms, Table)
   nsCOMPtr<nsIAtom> thirdDynamic = NS_Atomize(THIRD_ATOM_STR);
 
   EXPECT_FALSE(isStaticAtom(thirdDynamic));
 
   EXPECT_TRUE(thirdDynamic);
   EXPECT_EQ(NS_GetNumberOfAtoms(), count + 1);
 }
 
+class nsAtomRunner final : public nsIRunnable
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  NS_IMETHOD Run() final
+  {
+    for (int i = 0; i < 10000; i++) {
+      nsCOMPtr<nsIAtom> atom = NS_Atomize(u"A Testing Atom");
+    }
+    return NS_OK;
+  }
+
+private:
+  ~nsAtomRunner() {}
+};
+
+NS_IMPL_ISUPPORTS(nsAtomRunner, nsIRunnable)
+
+TEST(Atoms, ConcurrentAccessing)
+{
+  static const size_t kThreadCount = 4;
+  // Force a GC before so that we don't have any unused atom.
+  NS_GetNumberOfAtoms();
+  EXPECT_EQ(NS_GetUnusedAtomCount(), uint32_t(0));
+  nsCOMPtr<nsIThread> threads[kThreadCount];
+  for (size_t i = 0; i < kThreadCount; i++) {
+    nsresult rv = NS_NewThread(getter_AddRefs(threads[i]), new nsAtomRunner);
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+  }
+  for (size_t i = 0; i < kThreadCount; i++) {
+    threads[i]->Shutdown();
+  }
+  // We should have one unused atom from this test.
+  EXPECT_EQ(NS_GetUnusedAtomCount(), uint32_t(1));
 }
+
+}
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/gtest/TestAutoRefCnt.cpp
@@ -0,0 +1,67 @@
+/* -*- 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 "nsISupportsImpl.h"
+
+#include "mozilla/Atomics.h"
+#include "nsThreadUtils.h"
+
+#include "gtest/gtest.h"
+
+using namespace mozilla;
+
+class nsThreadSafeAutoRefCntRunner final : public nsIRunnable
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  NS_IMETHOD Run() final
+  {
+    for (int i = 0; i < 10000; i++) {
+      if (++sRefCnt == 1) {
+        sIncToOne++;
+      }
+      if (--sRefCnt == 0) {
+        sDecToZero++;
+      }
+    }
+    return NS_OK;
+  }
+
+  static ThreadSafeAutoRefCnt sRefCnt;
+  static Atomic<uint32_t, Relaxed> sIncToOne;
+  static Atomic<uint32_t, Relaxed> sDecToZero;
+
+private:
+  ~nsThreadSafeAutoRefCntRunner() {}
+};
+
+NS_IMPL_ISUPPORTS(nsThreadSafeAutoRefCntRunner, nsIRunnable)
+
+ThreadSafeAutoRefCnt nsThreadSafeAutoRefCntRunner::sRefCnt;
+Atomic<uint32_t, Relaxed> nsThreadSafeAutoRefCntRunner::sIncToOne(0);
+Atomic<uint32_t, Relaxed> nsThreadSafeAutoRefCntRunner::sDecToZero(0);
+
+// When a refcounted object is actually owned by a cache, we may not
+// want to release the object after last reference gets released. In
+// this pattern, the cache may rely on the balance of increment to one
+// and decrement to zero, so that it can maintain a counter for GC.
+TEST(AutoRefCnt, ThreadSafeAutoRefCntBalance)
+{
+  static const size_t kThreadCount = 4;
+  nsCOMPtr<nsIThread> threads[kThreadCount];
+  for (size_t i = 0; i < kThreadCount; i++) {
+    nsresult rv = NS_NewThread(getter_AddRefs(threads[i]),
+                               new nsThreadSafeAutoRefCntRunner);
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+  }
+  for (size_t i = 0; i < kThreadCount; i++) {
+    threads[i]->Shutdown();
+  }
+  EXPECT_EQ(nsThreadSafeAutoRefCntRunner::sRefCnt, nsrefcnt(0));
+  EXPECT_EQ(nsThreadSafeAutoRefCntRunner::sIncToOne,
+            nsThreadSafeAutoRefCntRunner::sDecToZero);
+}
--- a/xpcom/tests/gtest/TestEventPriorities.cpp
+++ b/xpcom/tests/gtest/TestEventPriorities.cpp
@@ -6,17 +6,19 @@
 
 #include "nsIThreadManager.h"
 #include "nsCOMPtr.h"
 #include "nsIRunnable.h"
 #include "nsXPCOM.h"
 #include "nsThreadUtils.h"
 #include "gtest/gtest.h"
 
-using mozilla::Runnable;
+#include <functional>
+
+using namespace mozilla;
 
 class TestEvent final : public Runnable, nsIRunnablePriority
 {
 public:
   explicit TestEvent(int* aCounter, std::function<void()>&& aCheck, uint32_t aPriority = nsIRunnablePriority::PRIORITY_NORMAL)
     : Runnable("TestEvent")
     , mCounter(aCounter)
     , mCheck(Move(aCheck))
--- a/xpcom/tests/gtest/moz.build
+++ b/xpcom/tests/gtest/moz.build
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
     'Helpers.cpp',
     'TestArenaAllocator.cpp',
     'TestAtoms.cpp',
     'TestAutoPtr.cpp',
     'TestAutoRef.cpp',
+    'TestAutoRefCnt.cpp',
     'TestBase64.cpp',
     'TestCallTemplates.cpp',
     'TestCloneInputStream.cpp',
     'TestCOMPtrEq.cpp',
     'TestCRT.cpp',
     'TestDafsa.cpp',
     'TestEncoding.cpp',
     'TestEscape.cpp',