mirror of https://github.com/grpc/grpc.git
Merge pull request #13357 from markdroth/reference_counted
Add ReferenceCounted base class.pull/13555/merge
commit
5f662537de
17 changed files with 874 additions and 0 deletions
@ -0,0 +1,52 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_CORE_LIB_SUPPORT_DEBUG_LOCATION_H |
||||
#define GRPC_CORE_LIB_SUPPORT_DEBUG_LOCATION_H |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// Used for tracking file and line where a call is made for debug builds.
|
||||
// No-op for non-debug builds.
|
||||
// Callers can use the DEBUG_LOCATION macro in either case.
|
||||
#ifndef NDEBUG |
||||
class DebugLocation { |
||||
public: |
||||
DebugLocation(const char* file, int line) : file_(file), line_(line) {} |
||||
bool Log() const { return true; } |
||||
const char* file() const { return file_; } |
||||
int line() const { return line_; } |
||||
|
||||
private: |
||||
const char* file_; |
||||
const int line_; |
||||
}; |
||||
#define DEBUG_LOCATION DebugLocation(__FILE__, __LINE__) |
||||
#else |
||||
class DebugLocation { |
||||
public: |
||||
bool Log() const { return false; } |
||||
const char* file() const { return nullptr; } |
||||
int line() const { return -1; } |
||||
}; |
||||
#define DEBUG_LOCATION DebugLocation() |
||||
#endif |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_LIB_SUPPORT_DEBUG_LOCATION_H */ |
@ -0,0 +1,122 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_CORE_LIB_SUPPORT_REF_COUNTED_H |
||||
#define GRPC_CORE_LIB_SUPPORT_REF_COUNTED_H |
||||
|
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/sync.h> |
||||
|
||||
#include "src/core/lib/debug/trace.h" |
||||
#include "src/core/lib/support/debug_location.h" |
||||
#include "src/core/lib/support/memory.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// A base class for reference-counted objects.
|
||||
// New objects should be created via New() and start with a refcount of 1.
|
||||
// When the refcount reaches 0, the object will be deleted via Delete().
|
||||
class RefCounted { |
||||
public: |
||||
void Ref() { gpr_ref(&refs_); } |
||||
|
||||
void Unref() { |
||||
if (gpr_unref(&refs_)) { |
||||
Delete(this); |
||||
} |
||||
} |
||||
|
||||
// Not copyable nor movable.
|
||||
RefCounted(const RefCounted&) = delete; |
||||
RefCounted& operator=(const RefCounted&) = delete; |
||||
|
||||
protected: |
||||
// Allow Delete() to access destructor.
|
||||
template <typename T> |
||||
friend void Delete(T*); |
||||
|
||||
RefCounted() { gpr_ref_init(&refs_, 1); } |
||||
|
||||
virtual ~RefCounted() {} |
||||
|
||||
private: |
||||
gpr_refcount refs_; |
||||
}; |
||||
|
||||
// An alternative version of the RefCounted base class that
|
||||
// supports tracing. This is intended to be used in cases where the
|
||||
// object will be handled both by idiomatic C++ code using smart
|
||||
// pointers and legacy code that is manually calling Ref() and Unref().
|
||||
// Once all of our code is converted to idiomatic C++, we may be able to
|
||||
// eliminate this class.
|
||||
class RefCountedWithTracing { |
||||
public: |
||||
void Ref() { gpr_ref(&refs_); } |
||||
|
||||
void Ref(const DebugLocation& location, const char* reason) { |
||||
if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) { |
||||
gpr_atm old_refs = gpr_atm_no_barrier_load(&refs_.count); |
||||
gpr_log(GPR_DEBUG, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s", |
||||
trace_flag_->name(), this, location.file(), location.line(), |
||||
old_refs, old_refs + 1, reason); |
||||
} |
||||
Ref(); |
||||
} |
||||
|
||||
void Unref() { |
||||
if (gpr_unref(&refs_)) { |
||||
Delete(this); |
||||
} |
||||
} |
||||
|
||||
void Unref(const DebugLocation& location, const char* reason) { |
||||
if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) { |
||||
gpr_atm old_refs = gpr_atm_no_barrier_load(&refs_.count); |
||||
gpr_log(GPR_DEBUG, "%s:%p %s:%d unref %" PRIdPTR " -> %" PRIdPTR " %s", |
||||
trace_flag_->name(), this, location.file(), location.line(), |
||||
old_refs, old_refs - 1, reason); |
||||
} |
||||
Unref(); |
||||
} |
||||
|
||||
// Not copyable nor movable.
|
||||
RefCountedWithTracing(const RefCountedWithTracing&) = delete; |
||||
RefCountedWithTracing& operator=(const RefCountedWithTracing&) = delete; |
||||
|
||||
protected: |
||||
// Allow Delete() to access destructor.
|
||||
template <typename T> |
||||
friend void Delete(T*); |
||||
|
||||
RefCountedWithTracing() : RefCountedWithTracing(nullptr) {} |
||||
|
||||
explicit RefCountedWithTracing(TraceFlag* trace_flag) |
||||
: trace_flag_(trace_flag) { |
||||
gpr_ref_init(&refs_, 1); |
||||
} |
||||
|
||||
virtual ~RefCountedWithTracing() {} |
||||
|
||||
private: |
||||
TraceFlag* trace_flag_ = nullptr; |
||||
gpr_refcount refs_; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_LIB_SUPPORT_REF_COUNTED_H */ |
@ -0,0 +1,90 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#ifndef GRPC_CORE_LIB_SUPPORT_REF_COUNTED_PTR_H |
||||
#define GRPC_CORE_LIB_SUPPORT_REF_COUNTED_PTR_H |
||||
|
||||
#include <utility> |
||||
|
||||
#include "src/core/lib/support/memory.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// A smart pointer class for objects that provide Ref() and Unref() methods,
|
||||
// such as those provided by the RefCounted base class.
|
||||
template <typename T> |
||||
class RefCountedPtr { |
||||
public: |
||||
RefCountedPtr() {} |
||||
|
||||
// If value is non-null, we take ownership of a ref to it.
|
||||
explicit RefCountedPtr(T* value) { value_ = value; } |
||||
|
||||
// Move support.
|
||||
RefCountedPtr(RefCountedPtr&& other) { |
||||
value_ = other.value_; |
||||
other.value_ = nullptr; |
||||
} |
||||
RefCountedPtr& operator=(RefCountedPtr&& other) { |
||||
if (value_ != nullptr) value_->Unref(); |
||||
value_ = other.value_; |
||||
other.value_ = nullptr; |
||||
return *this; |
||||
} |
||||
|
||||
// Copy support.
|
||||
RefCountedPtr(const RefCountedPtr& other) { |
||||
if (other.value_ != nullptr) other.value_->Ref(); |
||||
value_ = other.value_; |
||||
} |
||||
RefCountedPtr& operator=(const RefCountedPtr& other) { |
||||
// Note: Order of reffing and unreffing is important here in case value_
|
||||
// and other.value_ are the same object.
|
||||
if (other.value_ != nullptr) other.value_->Ref(); |
||||
if (value_ != nullptr) value_->Unref(); |
||||
value_ = other.value_; |
||||
return *this; |
||||
} |
||||
|
||||
~RefCountedPtr() { |
||||
if (value_ != nullptr) value_->Unref(); |
||||
} |
||||
|
||||
// If value is non-null, we take ownership of a ref to it.
|
||||
void reset(T* value = nullptr) { |
||||
if (value_ != nullptr) value_->Unref(); |
||||
value_ = value; |
||||
} |
||||
|
||||
T* get() const { return value_; } |
||||
|
||||
T& operator*() const { return *value_; } |
||||
T* operator->() const { return value_; } |
||||
|
||||
private: |
||||
T* value_ = nullptr; |
||||
}; |
||||
|
||||
template <typename T, typename... Args> |
||||
inline RefCountedPtr<T> MakeRefCounted(Args&&... args) { |
||||
return RefCountedPtr<T>(New<T>(std::forward<Args>(args)...)); |
||||
} |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_LIB_SUPPORT_REF_COUNTED_PTR_H */ |
@ -0,0 +1,172 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include "src/core/lib/support/ref_counted_ptr.h" |
||||
|
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <grpc/support/log.h> |
||||
|
||||
#include "src/core/lib/support/memory.h" |
||||
#include "src/core/lib/support/ref_counted.h" |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
namespace grpc_core { |
||||
namespace testing { |
||||
namespace { |
||||
|
||||
class Foo : public RefCounted { |
||||
public: |
||||
Foo() : value_(0) {} |
||||
|
||||
explicit Foo(int value) : value_(value) {} |
||||
|
||||
int value() const { return value_; } |
||||
|
||||
private: |
||||
int value_; |
||||
}; |
||||
|
||||
TEST(RefCountedPtr, DefaultConstructor) { RefCountedPtr<Foo> foo; } |
||||
|
||||
TEST(RefCountedPtr, ExplicitConstructorEmpty) { |
||||
RefCountedPtr<Foo> foo(nullptr); |
||||
} |
||||
|
||||
TEST(RefCountedPtr, ExplicitConstructor) { RefCountedPtr<Foo> foo(New<Foo>()); } |
||||
|
||||
TEST(RefCountedPtr, MoveConstructor) { |
||||
RefCountedPtr<Foo> foo(New<Foo>()); |
||||
RefCountedPtr<Foo> foo2(std::move(foo)); |
||||
EXPECT_EQ(nullptr, foo.get()); |
||||
EXPECT_NE(nullptr, foo2.get()); |
||||
} |
||||
|
||||
TEST(RefCountedPtr, MoveAssignment) { |
||||
RefCountedPtr<Foo> foo(New<Foo>()); |
||||
RefCountedPtr<Foo> foo2 = std::move(foo); |
||||
EXPECT_EQ(nullptr, foo.get()); |
||||
EXPECT_NE(nullptr, foo2.get()); |
||||
} |
||||
|
||||
TEST(RefCountedPtr, CopyConstructor) { |
||||
RefCountedPtr<Foo> foo(New<Foo>()); |
||||
RefCountedPtr<Foo> foo2(foo); |
||||
EXPECT_NE(nullptr, foo.get()); |
||||
EXPECT_EQ(foo.get(), foo2.get()); |
||||
} |
||||
|
||||
TEST(RefCountedPtr, CopyAssignment) { |
||||
RefCountedPtr<Foo> foo(New<Foo>()); |
||||
RefCountedPtr<Foo> foo2 = foo; |
||||
EXPECT_NE(nullptr, foo.get()); |
||||
EXPECT_EQ(foo.get(), foo2.get()); |
||||
} |
||||
|
||||
TEST(RefCountedPtr, CopyAssignmentWhenEmpty) { |
||||
RefCountedPtr<Foo> foo; |
||||
RefCountedPtr<Foo> foo2; |
||||
foo2 = foo; |
||||
EXPECT_EQ(nullptr, foo.get()); |
||||
EXPECT_EQ(nullptr, foo2.get()); |
||||
} |
||||
|
||||
TEST(RefCountedPtr, CopyAssignmentToSelf) { |
||||
RefCountedPtr<Foo> foo(New<Foo>()); |
||||
foo = foo; |
||||
} |
||||
|
||||
TEST(RefCountedPtr, EnclosedScope) { |
||||
RefCountedPtr<Foo> foo(New<Foo>()); |
||||
{ |
||||
RefCountedPtr<Foo> foo2(std::move(foo)); |
||||
EXPECT_EQ(nullptr, foo.get()); |
||||
EXPECT_NE(nullptr, foo2.get()); |
||||
} |
||||
EXPECT_EQ(nullptr, foo.get()); |
||||
} |
||||
|
||||
TEST(RefCountedPtr, ResetFromNullToNonNull) { |
||||
RefCountedPtr<Foo> foo; |
||||
EXPECT_EQ(nullptr, foo.get()); |
||||
foo.reset(New<Foo>()); |
||||
EXPECT_NE(nullptr, foo.get()); |
||||
} |
||||
|
||||
TEST(RefCountedPtr, ResetFromNonNullToNonNull) { |
||||
RefCountedPtr<Foo> foo(New<Foo>()); |
||||
EXPECT_NE(nullptr, foo.get()); |
||||
Foo* original = foo.get(); |
||||
foo.reset(New<Foo>()); |
||||
EXPECT_NE(nullptr, foo.get()); |
||||
EXPECT_NE(original, foo.get()); |
||||
} |
||||
|
||||
TEST(RefCountedPtr, ResetFromNonNullToNull) { |
||||
RefCountedPtr<Foo> foo(New<Foo>()); |
||||
EXPECT_NE(nullptr, foo.get()); |
||||
foo.reset(); |
||||
EXPECT_EQ(nullptr, foo.get()); |
||||
} |
||||
|
||||
TEST(RefCountedPtr, ResetFromNullToNull) { |
||||
RefCountedPtr<Foo> foo; |
||||
EXPECT_EQ(nullptr, foo.get()); |
||||
foo.reset(nullptr); |
||||
EXPECT_EQ(nullptr, foo.get()); |
||||
} |
||||
|
||||
TEST(RefCountedPtr, DerefernceOperators) { |
||||
RefCountedPtr<Foo> foo(New<Foo>()); |
||||
foo->value(); |
||||
Foo& foo_ref = *foo; |
||||
foo_ref.value(); |
||||
} |
||||
|
||||
TEST(MakeRefCounted, NoArgs) { |
||||
RefCountedPtr<Foo> foo = MakeRefCounted<Foo>(); |
||||
EXPECT_EQ(0, foo->value()); |
||||
} |
||||
|
||||
TEST(MakeRefCounted, Args) { |
||||
RefCountedPtr<Foo> foo = MakeRefCounted<Foo>(3); |
||||
EXPECT_EQ(3, foo->value()); |
||||
} |
||||
|
||||
TraceFlag foo_tracer(true, "foo"); |
||||
|
||||
class FooWithTracing : public RefCountedWithTracing { |
||||
public: |
||||
FooWithTracing() : RefCountedWithTracing(&foo_tracer) {} |
||||
}; |
||||
|
||||
TEST(RefCountedPtr, RefCountedWithTracing) { |
||||
RefCountedPtr<FooWithTracing> foo(New<FooWithTracing>()); |
||||
foo->Ref(DEBUG_LOCATION, "foo"); |
||||
foo->Unref(DEBUG_LOCATION, "foo"); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace testing
|
||||
} // namespace grpc_core
|
||||
|
||||
int main(int argc, char** argv) { |
||||
grpc_test_init(argc, argv); |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
@ -0,0 +1,72 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 gRPC authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
* |
||||
*/ |
||||
|
||||
#include "src/core/lib/support/ref_counted.h" |
||||
|
||||
#include <gtest/gtest.h> |
||||
|
||||
#include "src/core/lib/support/memory.h" |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
namespace grpc_core { |
||||
namespace testing { |
||||
namespace { |
||||
|
||||
class Foo : public RefCounted { |
||||
public: |
||||
Foo() {} |
||||
}; |
||||
|
||||
TEST(RefCounted, Basic) { |
||||
Foo* foo = New<Foo>(); |
||||
foo->Unref(); |
||||
} |
||||
|
||||
TEST(RefCounted, ExtraRef) { |
||||
Foo* foo = New<Foo>(); |
||||
foo->Ref(); |
||||
foo->Unref(); |
||||
foo->Unref(); |
||||
} |
||||
|
||||
TraceFlag foo_tracer(true, "foo"); |
||||
|
||||
class FooWithTracing : public RefCountedWithTracing { |
||||
public: |
||||
FooWithTracing() : RefCountedWithTracing(&foo_tracer) {} |
||||
}; |
||||
|
||||
TEST(RefCountedWithTracing, Basic) { |
||||
FooWithTracing* foo = New<FooWithTracing>(); |
||||
foo->Ref(DEBUG_LOCATION, "extra_ref"); |
||||
foo->Unref(DEBUG_LOCATION, "extra_ref"); |
||||
// Can use the no-argument methods, too.
|
||||
foo->Ref(); |
||||
foo->Unref(); |
||||
foo->Unref(DEBUG_LOCATION, "original_ref"); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace testing
|
||||
} // namespace grpc_core
|
||||
|
||||
int main(int argc, char** argv) { |
||||
grpc_test_init(argc, argv); |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
Loading…
Reference in new issue