/* -*- 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 sIncToOne; static Atomic sDecToZero; private: ~nsThreadSafeAutoRefCntRunner() {} }; NS_IMPL_ISUPPORTS(nsThreadSafeAutoRefCntRunner, nsIRunnable) ThreadSafeAutoRefCnt nsThreadSafeAutoRefCntRunner::sRefCnt; Atomic nsThreadSafeAutoRefCntRunner::sIncToOne(0); Atomic 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 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); }