mirror of https://github.com/grpc/grpc.git
Merge pull request #13984 from markdroth/ref_counting
Fix existing ref counting classes and add new ones.pull/14048/head
commit
8afe8d35e1
19 changed files with 504 additions and 2 deletions
@ -0,0 +1,171 @@ |
||||
/*
|
||||
* |
||||
* 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_ORPHANABLE_H |
||||
#define GRPC_CORE_LIB_SUPPORT_ORPHANABLE_H |
||||
|
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/sync.h> |
||||
|
||||
#include <memory> |
||||
|
||||
#include "src/core/lib/debug/trace.h" |
||||
#include "src/core/lib/support/abstract.h" |
||||
#include "src/core/lib/support/debug_location.h" |
||||
#include "src/core/lib/support/memory.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// A base class for orphanable objects, which have one external owner
|
||||
// but are not necessarily destroyed immediately when the external owner
|
||||
// gives up ownership. Instead, the owner calls the object's Orphan()
|
||||
// method, and the object then takes responsibility for its own cleanup
|
||||
// and destruction.
|
||||
class Orphanable { |
||||
public: |
||||
// Gives up ownership of the object. The implementation must arrange
|
||||
// to eventually destroy the object without further interaction from the
|
||||
// caller.
|
||||
virtual void Orphan() GRPC_ABSTRACT; |
||||
|
||||
// Not copyable or movable.
|
||||
Orphanable(const Orphanable&) = delete; |
||||
Orphanable& operator=(const Orphanable&) = delete; |
||||
|
||||
GRPC_ABSTRACT_BASE_CLASS |
||||
|
||||
protected: |
||||
Orphanable() {} |
||||
virtual ~Orphanable() {} |
||||
}; |
||||
|
||||
template <typename T> |
||||
class OrphanableDelete { |
||||
public: |
||||
void operator()(T* p) { p->Orphan(); } |
||||
}; |
||||
|
||||
template <typename T, typename Deleter = OrphanableDelete<T>> |
||||
using OrphanablePtr = std::unique_ptr<T, Deleter>; |
||||
|
||||
template <typename T, typename... Args> |
||||
inline OrphanablePtr<T> MakeOrphanable(Args&&... args) { |
||||
return OrphanablePtr<T>(New<T>(std::forward<Args>(args)...)); |
||||
} |
||||
|
||||
// A type of Orphanable with internal ref-counting.
|
||||
class InternallyRefCounted : public Orphanable { |
||||
public: |
||||
// Not copyable nor movable.
|
||||
InternallyRefCounted(const InternallyRefCounted&) = delete; |
||||
InternallyRefCounted& operator=(const InternallyRefCounted&) = delete; |
||||
|
||||
GRPC_ABSTRACT_BASE_CLASS |
||||
|
||||
protected: |
||||
InternallyRefCounted() { gpr_ref_init(&refs_, 1); } |
||||
virtual ~InternallyRefCounted() {} |
||||
|
||||
void Ref() { gpr_ref(&refs_); } |
||||
|
||||
void Unref() { |
||||
if (gpr_unref(&refs_)) { |
||||
Delete(this); |
||||
} |
||||
} |
||||
|
||||
// Allow Delete() to access destructor.
|
||||
template <typename T> |
||||
friend void Delete(T*); |
||||
|
||||
private: |
||||
gpr_refcount refs_; |
||||
}; |
||||
|
||||
// An alternative version of the InternallyRefCounted 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 InternallyRefCountedWithTracing : public Orphanable { |
||||
public: |
||||
// Not copyable nor movable.
|
||||
InternallyRefCountedWithTracing(const InternallyRefCountedWithTracing&) = |
||||
delete; |
||||
InternallyRefCountedWithTracing& operator=( |
||||
const InternallyRefCountedWithTracing&) = delete; |
||||
|
||||
GRPC_ABSTRACT_BASE_CLASS |
||||
|
||||
protected: |
||||
// Allow Delete() to access destructor.
|
||||
template <typename T> |
||||
friend void Delete(T*); |
||||
|
||||
InternallyRefCountedWithTracing() |
||||
: InternallyRefCountedWithTracing(static_cast<TraceFlag*>(nullptr)) {} |
||||
|
||||
explicit InternallyRefCountedWithTracing(TraceFlag* trace_flag) |
||||
: trace_flag_(trace_flag) { |
||||
gpr_ref_init(&refs_, 1); |
||||
} |
||||
|
||||
#ifdef NDEBUG |
||||
explicit InternallyRefCountedWithTracing(DebugOnlyTraceFlag* trace_flag) |
||||
: InternallyRefCountedWithTracing() {} |
||||
#endif |
||||
|
||||
virtual ~InternallyRefCountedWithTracing() {} |
||||
|
||||
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(); |
||||
} |
||||
|
||||
private: |
||||
TraceFlag* trace_flag_ = nullptr; |
||||
gpr_refcount refs_; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_LIB_SUPPORT_ORPHANABLE_H */ |
@ -0,0 +1,114 @@ |
||||
/*
|
||||
* |
||||
* 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/orphanable.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 Orphanable { |
||||
public: |
||||
Foo() : Foo(0) {} |
||||
explicit Foo(int value) : value_(value) {} |
||||
void Orphan() override { Delete(this); } |
||||
int value() const { return value_; } |
||||
|
||||
private: |
||||
int value_; |
||||
}; |
||||
|
||||
TEST(Orphanable, Basic) { |
||||
Foo* foo = New<Foo>(); |
||||
foo->Orphan(); |
||||
} |
||||
|
||||
TEST(OrphanablePtr, Basic) { |
||||
OrphanablePtr<Foo> foo(New<Foo>()); |
||||
EXPECT_EQ(0, foo->value()); |
||||
} |
||||
|
||||
TEST(MakeOrphanable, DefaultConstructor) { |
||||
auto foo = MakeOrphanable<Foo>(); |
||||
EXPECT_EQ(0, foo->value()); |
||||
} |
||||
|
||||
TEST(MakeOrphanable, WithParameters) { |
||||
auto foo = MakeOrphanable<Foo>(5); |
||||
EXPECT_EQ(5, foo->value()); |
||||
} |
||||
|
||||
class Bar : public InternallyRefCounted { |
||||
public: |
||||
Bar() : Bar(0) {} |
||||
explicit Bar(int value) : value_(value) {} |
||||
void Orphan() override { Unref(); } |
||||
int value() const { return value_; } |
||||
|
||||
void StartWork() { Ref(); } |
||||
void FinishWork() { Unref(); } |
||||
|
||||
private: |
||||
int value_; |
||||
}; |
||||
|
||||
TEST(OrphanablePtr, InternallyRefCounted) { |
||||
auto bar = MakeOrphanable<Bar>(); |
||||
bar->StartWork(); |
||||
bar->FinishWork(); |
||||
} |
||||
|
||||
// Note: We use DebugOnlyTraceFlag instead of TraceFlag to ensure that
|
||||
// things build properly in both debug and non-debug cases.
|
||||
DebugOnlyTraceFlag baz_tracer(true, "baz"); |
||||
|
||||
class Baz : public InternallyRefCountedWithTracing { |
||||
public: |
||||
Baz() : Baz(0) {} |
||||
explicit Baz(int value) |
||||
: InternallyRefCountedWithTracing(&baz_tracer), value_(value) {} |
||||
void Orphan() override { Unref(); } |
||||
int value() const { return value_; } |
||||
|
||||
void StartWork() { Ref(DEBUG_LOCATION, "work"); } |
||||
void FinishWork() { Unref(DEBUG_LOCATION, "work"); } |
||||
|
||||
private: |
||||
int value_; |
||||
}; |
||||
|
||||
TEST(OrphanablePtr, InternallyRefCountedWithTracing) { |
||||
auto baz = MakeOrphanable<Baz>(); |
||||
baz->StartWork(); |
||||
baz->FinishWork(); |
||||
} |
||||
|
||||
} // 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