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