1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ref_object.h"
17 #include "log_print.h"
18
19 namespace DistributedDB {
20 constexpr static int MAX_REF_COUNT = 1024;
21
AutoLock(const RefObject * obj,bool unlocked)22 RefObject::AutoLock::AutoLock(const RefObject *obj, bool unlocked)
23 : refObj_(obj),
24 isLocked_(false)
25 {
26 if (refObj_ != nullptr) {
27 if (unlocked) {
28 refObj_->LockObj();
29 }
30 isLocked_ = true;
31 }
32 }
33
Lock()34 void RefObject::AutoLock::Lock()
35 {
36 if (refObj_ != nullptr) {
37 if (!isLocked_) {
38 refObj_->LockObj();
39 isLocked_ = true;
40 } else {
41 LOGE("RefObject-AutoLock: obj' acquires lock more than once.");
42 }
43 }
44 }
45
Unlock()46 void RefObject::AutoLock::Unlock()
47 {
48 if (refObj_ != nullptr) {
49 if (isLocked_) {
50 refObj_->UnlockObj();
51 isLocked_ = false;
52 } else {
53 LOGE("RefObject-AutoLock: obj releases lock more than once.");
54 }
55 }
56 }
57
~AutoLock()58 RefObject::AutoLock::~AutoLock()
59 {
60 if (refObj_ != nullptr) {
61 if (isLocked_) {
62 refObj_->UnlockObj();
63 isLocked_ = false;
64 }
65 refObj_ = nullptr;
66 }
67 }
68
RefObject()69 RefObject::RefObject()
70 : refCount_(1),
71 isKilled_(false)
72 {}
73
~RefObject()74 RefObject::~RefObject()
75 {
76 int refCount = refCount_.load(std::memory_order_seq_cst);
77 if (refCount > 0) {
78 LOGF("object is destructed with ref-count > 0., refCount = %d", refCount);
79 }
80 }
81
OnLastRef(const std::function<void (void)> & callback) const82 void RefObject::OnLastRef(const std::function<void(void)> &callback) const
83 {
84 if (onLast_) {
85 std::string tag = GetObjectTag();
86 LOGW("%s object set 'OnLastRef()' callback twice.", tag.c_str());
87 return;
88 }
89 onLast_ = callback;
90 }
91
OnKill(const std::function<void (void)> & callback)92 void RefObject::OnKill(const std::function<void(void)> &callback)
93 {
94 if (onKill_) {
95 std::string tag = GetObjectTag();
96 LOGW("%s object set 'OnKill()' callback twice.", tag.c_str());
97 return;
98 }
99 onKill_ = callback;
100 }
101
IsKilled() const102 bool RefObject::IsKilled() const
103 {
104 return isKilled_;
105 }
106
KillObj()107 void RefObject::KillObj()
108 {
109 std::lock_guard<std::mutex> lockGuard(objLock_);
110 if (!IsKilled()) {
111 isKilled_ = true;
112 if (onKill_) {
113 onKill_();
114 }
115 }
116 }
117
LockObj() const118 void RefObject::LockObj() const
119 {
120 objLock_.lock();
121 }
122
UnlockObj() const123 void RefObject::UnlockObj() const
124 {
125 objLock_.unlock();
126 }
127
WaitLockedUntil(std::condition_variable & cv,const std::function<bool (void)> & condition,int seconds)128 bool RefObject::WaitLockedUntil(std::condition_variable &cv,
129 const std::function<bool(void)> &condition, int seconds)
130 {
131 // Enter with lock held.
132 if (!condition) {
133 return false;
134 }
135
136 bool waitOk = true;
137 {
138 std::unique_lock<std::mutex> lock(objLock_, std::adopt_lock_t());
139 while (!condition()) {
140 if (seconds > 0) {
141 cv.wait_for(lock, std::chrono::seconds(seconds));
142 waitOk = condition();
143 break;
144 } else {
145 cv.wait(lock);
146 }
147 }
148 }
149
150 // Lock has just been dropped in unique_lock::~unique_lock(),
151 // so we lock it again.
152 LockObj();
153 return waitOk;
154 }
155
IncObjRef(const RefObject * obj)156 void RefObject::IncObjRef(const RefObject *obj)
157 {
158 if (obj == nullptr) {
159 return;
160 }
161 int refCount = obj->refCount_.fetch_add(1, std::memory_order_seq_cst);
162 if ((refCount <= 0) || (refCount >= MAX_REF_COUNT)) {
163 std::string tag = obj->GetObjectTag();
164 LOGF("%s object is refed with ref-count=%d.", tag.c_str(), refCount);
165 }
166 }
167
DecObjRef(const RefObject * obj)168 void RefObject::DecObjRef(const RefObject *obj)
169 {
170 if (obj == nullptr) {
171 return;
172 }
173 int refCount = obj->refCount_.fetch_sub(1, std::memory_order_seq_cst);
174 if (refCount <= 0) {
175 std::string tag = obj->GetObjectTag();
176 LOGF("%s object is unrefed with ref-count(%d) <= 0.", tag.c_str(), refCount);
177 } else {
178 if (refCount == 1) {
179 if (obj->onLast_) {
180 obj->onLast_();
181 }
182 delete obj;
183 }
184 }
185 }
186
KillAndDecObjRef(RefObject * obj)187 void RefObject::KillAndDecObjRef(RefObject *obj)
188 {
189 if (obj == nullptr) {
190 return;
191 }
192 obj->KillObj();
193 obj->DecObjRef(obj);
194 obj = nullptr;
195 }
196
197 DEFINE_OBJECT_TAG_FACILITIES(RefObject)
198 } // namespace DistributedDB
199