blob: 16c7912fc6bdb98b4cfab0c8d53436a4d708249b [file] [log] [blame]
Mark D. Roth70db6632017-11-27 14:53:26 -08001/*
2 *
3 * Copyright 2017 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
Mark D. Roth4f2b0fd2018-01-19 12:12:23 -080019#ifndef GRPC_CORE_LIB_GPRPP_REF_COUNTED_H
20#define GRPC_CORE_LIB_GPRPP_REF_COUNTED_H
Mark D. Roth70db6632017-11-27 14:53:26 -080021
Mark D. Rothb319f492017-11-28 14:50:07 -080022#include <grpc/support/log.h>
Mark D. Roth853fff82017-11-29 07:37:41 -080023#include <grpc/support/sync.h>
Mark D. Roth70db6632017-11-27 14:53:26 -080024
25#include "src/core/lib/debug/trace.h"
Mark D. Roth4f2b0fd2018-01-19 12:12:23 -080026#include "src/core/lib/gprpp/abstract.h"
27#include "src/core/lib/gprpp/debug_location.h"
28#include "src/core/lib/gprpp/memory.h"
Mark D. Roth08d9f3d2018-02-06 07:58:17 -080029#include "src/core/lib/gprpp/ref_counted_ptr.h"
Mark D. Roth70db6632017-11-27 14:53:26 -080030
31namespace grpc_core {
32
Mark D. Rothb319f492017-11-28 14:50:07 -080033// A base class for reference-counted objects.
34// New objects should be created via New() and start with a refcount of 1.
35// When the refcount reaches 0, the object will be deleted via Delete().
Mark D. Roth08d9f3d2018-02-06 07:58:17 -080036//
37// This will commonly be used by CRTP (curiously-recurring template pattern)
38// e.g., class MyClass : public RefCounted<MyClass>
39template <typename Child>
Mark D. Rothbf816d32017-11-29 11:25:34 -080040class RefCounted {
Mark D. Roth70db6632017-11-27 14:53:26 -080041 public:
Mark D. Roth08d9f3d2018-02-06 07:58:17 -080042 RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT {
43 IncrementRefCount();
44 return RefCountedPtr<Child>(reinterpret_cast<Child*>(this));
45 }
Mark D. Roth70db6632017-11-27 14:53:26 -080046
Mark D. Roth08d9f3d2018-02-06 07:58:17 -080047 // TODO(roth): Once all of our code is converted to C++ and can use
48 // RefCountedPtr<> instead of manual ref-counting, make this method
49 // private, since it will only be used by RefCountedPtr<>, which is a
50 // friend of this class.
Mark D. Roth853fff82017-11-29 07:37:41 -080051 void Unref() {
52 if (gpr_unref(&refs_)) {
53 Delete(this);
54 }
55 }
56
57 // Not copyable nor movable.
Mark D. Rothbf816d32017-11-29 11:25:34 -080058 RefCounted(const RefCounted&) = delete;
59 RefCounted& operator=(const RefCounted&) = delete;
Mark D. Roth853fff82017-11-29 07:37:41 -080060
Mark D. Roth324703d2018-01-11 07:41:31 -080061 GRPC_ABSTRACT_BASE_CLASS
62
Mark D. Roth853fff82017-11-29 07:37:41 -080063 protected:
64 // Allow Delete() to access destructor.
65 template <typename T>
66 friend void Delete(T*);
67
Mark D. Rothbf816d32017-11-29 11:25:34 -080068 RefCounted() { gpr_ref_init(&refs_, 1); }
Mark D. Roth853fff82017-11-29 07:37:41 -080069
Mark D. Rothbf816d32017-11-29 11:25:34 -080070 virtual ~RefCounted() {}
Mark D. Roth853fff82017-11-29 07:37:41 -080071
72 private:
Mark D. Roth08d9f3d2018-02-06 07:58:17 -080073 // Allow RefCountedPtr<> to access IncrementRefCount().
74 friend class RefCountedPtr<Child>;
75
76 void IncrementRefCount() { gpr_ref(&refs_); }
77
Mark D. Roth853fff82017-11-29 07:37:41 -080078 gpr_refcount refs_;
79};
80
Mark D. Rothbf816d32017-11-29 11:25:34 -080081// An alternative version of the RefCounted base class that
Mark D. Roth853fff82017-11-29 07:37:41 -080082// supports tracing. This is intended to be used in cases where the
83// object will be handled both by idiomatic C++ code using smart
84// pointers and legacy code that is manually calling Ref() and Unref().
85// Once all of our code is converted to idiomatic C++, we may be able to
86// eliminate this class.
Mark D. Roth08d9f3d2018-02-06 07:58:17 -080087template <typename Child>
Mark D. Rothbf816d32017-11-29 11:25:34 -080088class RefCountedWithTracing {
Mark D. Roth853fff82017-11-29 07:37:41 -080089 public:
Mark D. Roth08d9f3d2018-02-06 07:58:17 -080090 RefCountedPtr<Child> Ref() GRPC_MUST_USE_RESULT {
91 IncrementRefCount();
92 return RefCountedPtr<Child>(reinterpret_cast<Child*>(this));
93 }
Mark D. Roth853fff82017-11-29 07:37:41 -080094
Mark D. Roth08d9f3d2018-02-06 07:58:17 -080095 RefCountedPtr<Child> Ref(const DebugLocation& location,
96 const char* reason) GRPC_MUST_USE_RESULT {
Mark D. Rothb319f492017-11-28 14:50:07 -080097 if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) {
98 gpr_atm old_refs = gpr_atm_no_barrier_load(&refs_.count);
99 gpr_log(GPR_DEBUG, "%s:%p %s:%d ref %" PRIdPTR " -> %" PRIdPTR " %s",
100 trace_flag_->name(), this, location.file(), location.line(),
101 old_refs, old_refs + 1, reason);
102 }
Mark D. Roth08d9f3d2018-02-06 07:58:17 -0800103 return Ref();
Mark D. Rothb319f492017-11-28 14:50:07 -0800104 }
105
Mark D. Roth08d9f3d2018-02-06 07:58:17 -0800106 // TODO(roth): Once all of our code is converted to C++ and can use
107 // RefCountedPtr<> instead of manual ref-counting, make the Unref() methods
108 // private, since they will only be used by RefCountedPtr<>, which is a
109 // friend of this class.
110
Mark D. Rothb319f492017-11-28 14:50:07 -0800111 void Unref() {
112 if (gpr_unref(&refs_)) {
113 Delete(this);
114 }
115 }
116
117 void Unref(const DebugLocation& location, const char* reason) {
118 if (location.Log() && trace_flag_ != nullptr && trace_flag_->enabled()) {
119 gpr_atm old_refs = gpr_atm_no_barrier_load(&refs_.count);
120 gpr_log(GPR_DEBUG, "%s:%p %s:%d unref %" PRIdPTR " -> %" PRIdPTR " %s",
121 trace_flag_->name(), this, location.file(), location.line(),
122 old_refs, old_refs - 1, reason);
123 }
124 Unref();
125 }
Mark D. Roth70db6632017-11-27 14:53:26 -0800126
127 // Not copyable nor movable.
Mark D. Rothbf816d32017-11-29 11:25:34 -0800128 RefCountedWithTracing(const RefCountedWithTracing&) = delete;
129 RefCountedWithTracing& operator=(const RefCountedWithTracing&) = delete;
Mark D. Roth70db6632017-11-27 14:53:26 -0800130
Mark D. Roth324703d2018-01-11 07:41:31 -0800131 GRPC_ABSTRACT_BASE_CLASS
132
Mark D. Roth70db6632017-11-27 14:53:26 -0800133 protected:
Mark D. Rothabadc6c2017-11-28 08:24:10 -0800134 // Allow Delete() to access destructor.
Mark D. Rothd3984c32017-11-28 09:13:27 -0800135 template <typename T>
Mark D. Rothabadc6c2017-11-28 08:24:10 -0800136 friend void Delete(T*);
137
Mark D. Roth324703d2018-01-11 07:41:31 -0800138 RefCountedWithTracing()
139 : RefCountedWithTracing(static_cast<TraceFlag*>(nullptr)) {}
Mark D. Rothb319f492017-11-28 14:50:07 -0800140
Mark D. Rothbf816d32017-11-29 11:25:34 -0800141 explicit RefCountedWithTracing(TraceFlag* trace_flag)
Mark D. Roth853fff82017-11-29 07:37:41 -0800142 : trace_flag_(trace_flag) {
Mark D. Roth70db6632017-11-27 14:53:26 -0800143 gpr_ref_init(&refs_, 1);
144 }
145
Mark D. Roth324703d2018-01-11 07:41:31 -0800146#ifdef NDEBUG
147 explicit RefCountedWithTracing(DebugOnlyTraceFlag* trace_flag)
148 : RefCountedWithTracing() {}
149#endif
150
Mark D. Rothbf816d32017-11-29 11:25:34 -0800151 virtual ~RefCountedWithTracing() {}
Mark D. Roth70db6632017-11-27 14:53:26 -0800152
153 private:
Mark D. Roth08d9f3d2018-02-06 07:58:17 -0800154 // Allow RefCountedPtr<> to access IncrementRefCount().
155 friend class RefCountedPtr<Child>;
156
157 void IncrementRefCount() { gpr_ref(&refs_); }
158
Mark D. Rothb319f492017-11-28 14:50:07 -0800159 TraceFlag* trace_flag_ = nullptr;
Mark D. Roth70db6632017-11-27 14:53:26 -0800160 gpr_refcount refs_;
161};
162
163} // namespace grpc_core
164
Mark D. Roth4f2b0fd2018-01-19 12:12:23 -0800165#endif /* GRPC_CORE_LIB_GPRPP_REF_COUNTED_H */