Skip to content

Commit

Permalink
partition_alloc: copy ref_counted and scoped_refptr to PA library.
Browse files Browse the repository at this point in the history
PS1 = just copying ref_counted and scoped_refptr to PA library

Bug: 1151236
Change-Id: I0d35f6474f7f95bbf0766ba56d83aeae5b3afe1e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3637437
Reviewed-by: Anton Bikineev <bikineev@chromium.org>
Reviewed-by: Yuki Shiino <yukishiino@chromium.org>
Reviewed-by: Bartek Nowierski <bartekn@chromium.org>
Commit-Queue: Takashi Sakamoto <tasak@google.com>
Cr-Commit-Position: refs/heads/main@{#1002492}
  • Loading branch information
tasak authored and Chromium LUCI CQ committed May 12, 2022
1 parent 3a229d8 commit 88e1c54
Show file tree
Hide file tree
Showing 8 changed files with 681 additions and 10 deletions.
4 changes: 4 additions & 0 deletions base/allocator/partition_allocator/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ target(partition_alloc_target_type, "partition_alloc") {
"partition_alloc-inl.h",
"partition_alloc.cc",
"partition_alloc.h",
"partition_alloc_base/atomic_ref_count.h",
"partition_alloc_base/bits.h",
"partition_alloc_base/cpu.cc",
"partition_alloc_base/cpu.h",
Expand All @@ -83,6 +84,9 @@ target(partition_alloc_target_type, "partition_alloc") {
"partition_alloc_base/gtest_prod_util.h",
"partition_alloc_base/logging.cc",
"partition_alloc_base/logging.h",
"partition_alloc_base/memory/ref_counted.cc",
"partition_alloc_base/memory/ref_counted.h",
"partition_alloc_base/memory/scoped_refptr.h",
"partition_alloc_base/migration_adapter.h",
"partition_alloc_base/no_destructor.h",
"partition_alloc_base/numerics/checked_math.h",
Expand Down
2 changes: 0 additions & 2 deletions base/allocator/partition_allocator/DEPS
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ include_rules = [
"+base/mac/foundation_util.h",
"+base/mac/mac_util.h",
"+base/mac/scoped_cftyperef.h",
"+base/memory/ref_counted.h",
"+base/memory/scoped_refptr.h",
"+base/process/memory.h",
"+base/strings/stringprintf.h",
"+base/system/sys_info.h",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This is a low level implementation of atomic semantics for reference
// counting. Please use base/memory/ref_counted.h directly instead.

#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_ATOMIC_REF_COUNT_H_
#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_ATOMIC_REF_COUNT_H_

#include <atomic>

namespace partition_alloc::internal::base {

class AtomicRefCount {
public:
constexpr AtomicRefCount() : ref_count_(0) {}
explicit constexpr AtomicRefCount(int initial_value)
: ref_count_(initial_value) {}

// Increment a reference count.
// Returns the previous value of the count.
int Increment() { return Increment(1); }

// Increment a reference count by "increment", which must exceed 0.
// Returns the previous value of the count.
int Increment(int increment) {
return ref_count_.fetch_add(increment, std::memory_order_relaxed);
}

// Decrement a reference count, and return whether the result is non-zero.
// Insert barriers to ensure that state written before the reference count
// became zero will be visible to a thread that has just made the count zero.
bool Decrement() {
// TODO(jbroman): Technically this doesn't need to be an acquire operation
// unless the result is 1 (i.e., the ref count did indeed reach zero).
// However, there are toolchain issues that make that not work as well at
// present (notably TSAN doesn't like it).
return ref_count_.fetch_sub(1, std::memory_order_acq_rel) != 1;
}

// Return whether the reference count is one. If the reference count is used
// in the conventional way, a reference count of 1 implies that the current
// thread owns the reference and no other thread shares it. This call
// performs the test for a reference count of one, and performs the memory
// barrier needed for the owning thread to act on the object, knowing that it
// has exclusive access to the object.
bool IsOne() const { return ref_count_.load(std::memory_order_acquire) == 1; }

// Return whether the reference count is zero. With conventional object
// referencing counting, the object will be destroyed, so the reference count
// should never be zero. Hence this is generally used for a debug check.
bool IsZero() const {
return ref_count_.load(std::memory_order_acquire) == 0;
}

// Returns the current reference count (with no barriers). This is subtle, and
// should be used only for debugging.
int SubtleRefCountForDebug() const {
return ref_count_.load(std::memory_order_relaxed);
}

private:
std::atomic_int ref_count_;
};

} // namespace partition_alloc::internal::base

#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_ATOMIC_REF_COUNT_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/allocator/partition_allocator/partition_alloc_base/memory/ref_counted.h"

#include <limits>
#include <ostream>
#include <type_traits>

namespace partition_alloc::internal::base::subtle {

bool RefCountedThreadSafeBase::HasOneRef() const {
return ref_count_.IsOne();
}

bool RefCountedThreadSafeBase::HasAtLeastOneRef() const {
return !ref_count_.IsZero();
}

#if DCHECK_IS_ON()
RefCountedThreadSafeBase::~RefCountedThreadSafeBase() {
DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without "
"calling Release()";
}
#endif

// For security and correctness, we check the arithmetic on ref counts.
//
// In an attempt to avoid binary bloat (from inlining the `CHECK`), we define
// these functions out-of-line. However, compilers are wily. Further testing may
// show that `NOINLINE` helps or hurts.
//
#if !defined(ARCH_CPU_X86_FAMILY)
bool RefCountedThreadSafeBase::Release() const {
return ReleaseImpl();
}
void RefCountedThreadSafeBase::AddRef() const {
AddRefImpl();
}
void RefCountedThreadSafeBase::AddRefWithCheck() const {
AddRefWithCheckImpl();
}
#endif

} // namespace partition_alloc::internal::base::subtle
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_REF_COUNTED_H_
#define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_REF_COUNTED_H_

#include "base/allocator/partition_allocator/partition_alloc_base/atomic_ref_count.h"
#include "base/allocator/partition_allocator/partition_alloc_base/memory/scoped_refptr.h"
#include "base/base_export.h"
#include "base/check.h"
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/dcheck_is_on.h"
#include "build/build_config.h"

namespace partition_alloc::internal::base {
namespace subtle {

class BASE_EXPORT RefCountedThreadSafeBase {
public:
RefCountedThreadSafeBase(const RefCountedThreadSafeBase&) = delete;
RefCountedThreadSafeBase& operator=(const RefCountedThreadSafeBase&) = delete;

bool HasOneRef() const;
bool HasAtLeastOneRef() const;

protected:
explicit constexpr RefCountedThreadSafeBase(StartRefCountFromZeroTag) {}
explicit constexpr RefCountedThreadSafeBase(StartRefCountFromOneTag)
: ref_count_(1) {
#if DCHECK_IS_ON()
needs_adopt_ref_ = true;
#endif
}

#if DCHECK_IS_ON()
~RefCountedThreadSafeBase();
#else
~RefCountedThreadSafeBase() = default;
#endif

// Release and AddRef are suitable for inlining on X86 because they generate
// very small code sequences. On other platforms (ARM), it causes a size
// regression and is probably not worth it.
#if defined(ARCH_CPU_X86_FAMILY)
// Returns true if the object should self-delete.
bool Release() const { return ReleaseImpl(); }
void AddRef() const { AddRefImpl(); }
void AddRefWithCheck() const { AddRefWithCheckImpl(); }
#else
// Returns true if the object should self-delete.
bool Release() const;
void AddRef() const;
void AddRefWithCheck() const;
#endif

private:
template <typename U>
friend scoped_refptr<U> AdoptRef(U*);

void Adopted() const {
#if DCHECK_IS_ON()
DCHECK(needs_adopt_ref_);
needs_adopt_ref_ = false;
#endif
}

ALWAYS_INLINE void AddRefImpl() const {
#if DCHECK_IS_ON()
DCHECK(!in_dtor_);
// This RefCounted object is created with non-zero reference count.
// The first reference to such a object has to be made by AdoptRef or
// MakeRefCounted.
DCHECK(!needs_adopt_ref_);
#endif
ref_count_.Increment();
}

ALWAYS_INLINE void AddRefWithCheckImpl() const {
#if DCHECK_IS_ON()
DCHECK(!in_dtor_);
// This RefCounted object is created with non-zero reference count.
// The first reference to such a object has to be made by AdoptRef or
// MakeRefCounted.
DCHECK(!needs_adopt_ref_);
#endif
CHECK_GT(ref_count_.Increment(), 0);
}

ALWAYS_INLINE bool ReleaseImpl() const {
#if DCHECK_IS_ON()
DCHECK(!in_dtor_);
DCHECK(!ref_count_.IsZero());
#endif
if (!ref_count_.Decrement()) {
#if DCHECK_IS_ON()
in_dtor_ = true;
#endif
return true;
}
return false;
}

mutable AtomicRefCount ref_count_{0};
#if DCHECK_IS_ON()
mutable bool needs_adopt_ref_ = false;
mutable bool in_dtor_ = false;
#endif
};

} // namespace subtle

// Forward declaration.
template <class T, typename Traits>
class RefCountedThreadSafe;

// Default traits for RefCountedThreadSafe<T>. Deletes the object when its ref
// count reaches 0. Overload to delete it on a different thread etc.
template <typename T>
struct DefaultRefCountedThreadSafeTraits {
static void Destruct(const T* x) {
// Delete through RefCountedThreadSafe to make child classes only need to be
// friend with RefCountedThreadSafe instead of this struct, which is an
// implementation detail.
RefCountedThreadSafe<T, DefaultRefCountedThreadSafeTraits>::DeleteInternal(
x);
}
};

//
// A thread-safe variant of RefCounted<T>
//
// class MyFoo : public base::RefCountedThreadSafe<MyFoo> {
// ...
// };
//
// If you're using the default trait, then you should add compile time
// asserts that no one else is deleting your object. i.e.
// private:
// friend class base::RefCountedThreadSafe<MyFoo>;
// ~MyFoo();
//
// We can use REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE() with RefCountedThreadSafe
// too. See the comment above the RefCounted definition for details.
template <class T, typename Traits = DefaultRefCountedThreadSafeTraits<T>>
class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase {
public:
static constexpr subtle::StartRefCountFromZeroTag kRefCountPreference =
subtle::kStartRefCountFromZeroTag;

explicit RefCountedThreadSafe()
: subtle::RefCountedThreadSafeBase(T::kRefCountPreference) {}

RefCountedThreadSafe(const RefCountedThreadSafe&) = delete;
RefCountedThreadSafe& operator=(const RefCountedThreadSafe&) = delete;

void AddRef() const { AddRefImpl(T::kRefCountPreference); }

void Release() const {
if (subtle::RefCountedThreadSafeBase::Release()) {
ANALYZER_SKIP_THIS_PATH();
Traits::Destruct(static_cast<const T*>(this));
}
}

protected:
~RefCountedThreadSafe() = default;

private:
friend struct DefaultRefCountedThreadSafeTraits<T>;
template <typename U>
static void DeleteInternal(const U* x) {
delete x;
}

void AddRefImpl(subtle::StartRefCountFromZeroTag) const {
subtle::RefCountedThreadSafeBase::AddRef();
}

void AddRefImpl(subtle::StartRefCountFromOneTag) const {
subtle::RefCountedThreadSafeBase::AddRefWithCheck();
}
};

} // namespace partition_alloc::internal::base

#endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_BASE_MEMORY_REF_COUNTED_H_

0 comments on commit 88e1c54

Please sign in to comment.