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 "refbase.h"
17 #include "utils_log.h"
18 #ifdef DEBUG_REFBASE
19 #include <unistd.h>
20 #endif
21
22 namespace OHOS {
23
WeakRefCounter(RefCounter * counter,void * cookie)24 WeakRefCounter::WeakRefCounter(RefCounter *counter, void *cookie)
25 : atomicWeak_(0), refCounter_(counter), cookie_(cookie)
26 {
27 if (refCounter_ != nullptr) {
28 refCounter_->IncRefCount();
29 }
30 }
31
~WeakRefCounter()32 WeakRefCounter::~WeakRefCounter()
33 {
34 if (refCounter_ != nullptr) {
35 refCounter_->DecRefCount();
36 }
37 }
38
GetWeakRefCount() const39 int WeakRefCounter::GetWeakRefCount() const
40 {
41 return atomicWeak_.load(std::memory_order_relaxed);
42 }
43
GetRefPtr()44 void* WeakRefCounter::GetRefPtr()
45 {
46 if ((cookie_ != nullptr) && (!refCounter_->IsRefPtrValid())) {
47 cookie_ = nullptr;
48 }
49 return cookie_;
50 }
51
IncWeakRefCount(const void * objectId)52 void WeakRefCounter::IncWeakRefCount(const void *objectId)
53 {
54 if (atomicWeak_.fetch_add(1, std::memory_order_relaxed) == 0) {
55 refCounter_->IncWeakRefCount(objectId);
56 }
57 }
58
DecWeakRefCount(const void * objectId)59 void WeakRefCounter::DecWeakRefCount(const void *objectId)
60 {
61 if (atomicWeak_.fetch_sub(1, std::memory_order_release) == 1) {
62 refCounter_->DecWeakRefCount(objectId);
63 delete this;
64 }
65 }
66
AttemptIncStrongRef(const void * objectId)67 bool WeakRefCounter::AttemptIncStrongRef(const void *objectId)
68 {
69 int unuse = 0;
70 return refCounter_->AttemptIncStrongRef(objectId, unuse);
71 }
72
73 #if ((defined DEBUG_REFBASE) && (!defined PRINT_TRACK_AT_ONCE))
74 // RefTracker is a debug tool, used to record the trace of RefBase.
75 // RefTracker will save the information about the count of RefBase,
76 // including the pointer of sptr/wptr(The pointer of itself, not the pointer
77 // it manages), the amount of strong/weak/refcout and the PID&TID.
78 // The Tracker can live with RefCounter.
79 // User should keep thread-safety of RefTracker.
80 class RefTracker {
81 public:
82 RefTracker(RefTracker* exTracker, const void* id, int strong, int weak, int ref, int pid, int tid);
83
84 void PrintTrace(const void* refCounterPtr);
85
86 RefTracker* PopTrace(const void* refCounterPtr);
87
88 private:
89 const void* ptrID;
90 int strongRefCnt;
91 int weakRefCnt;
92 int refCnt;
93 int PID;
94 int TID;
95 RefTracker* exTrace;
96 };
97
RefTracker(RefTracker * exTracker,const void * id,int strong,int weak,int ref,int pid,int tid)98 RefTracker::RefTracker(RefTracker* exTracker, const void* id, int strong, int weak, int ref, int pid, int tid)
99 : ptrID (id), strongRefCnt (strong), weakRefCnt (weak), refCnt (ref), PID (pid), TID (tid), exTrace (exTracker)
100 {
101 }
102
PrintTrace(const void * refCounterPtr)103 void RefTracker::PrintTrace(const void* refCounterPtr)
104 {
105 UTILS_LOGI("%{public}p call %{public}p. strong: %{public}d weak: %{public}d " \
106 "refcnt: %{public}d PID: %{public}d TID: %{public}d",
107 ptrID, refCounterPtr, strongRefCnt, weakRefCnt, refCnt, PID, TID);
108 }
109
PopTrace(const void * refCounterPtr)110 RefTracker* RefTracker::PopTrace(const void* refCounterPtr)
111 {
112 RefTracker* ref = exTrace;
113 PrintTrace(refCounterPtr);
114 delete this;
115 return ref;
116 }
117 #endif
118
119 #ifdef DEBUG_REFBASE
120 #ifdef PRINT_TRACK_AT_ONCE
PrintRefs(const void * objectId)121 void RefCounter::PrintRefs(const void* objectId)
122 {
123 std::lock_guard<std::mutex> lock(trackerMutex);
124 UTILS_LOGI("%{public}p call %{public}p. strong: %{public}d weak: %{public}d " \
125 "refcnt: %{public}d", objectId, this, atomicStrong_.load(std::memory_order_relaxed),
126 atomicWeak_.load(std::memory_order_relaxed), atomicRefCount_.load(std::memory_order_relaxed));
127 }
128 #else
GetNewTrace(const void * objectId)129 void RefCounter::GetNewTrace(const void* objectId)
130 {
131 std::lock_guard<std::mutex> lock(trackerMutex);
132 RefTracker* newTracker = new RefTracker(refTracker, objectId, atomicStrong_,
133 atomicWeak_, atomicRefCount_, getpid(), gettid());
134 refTracker = newTracker;
135 }
136
PrintTracker()137 void RefCounter::PrintTracker()
138 {
139 std::lock_guard<std::mutex> lock(trackerMutex);
140 if (refTracker) {
141 UTILS_LOGI("%{public}p start backtrace", this);
142 while (refTracker) {
143 refTracker = refTracker->PopTrace(this);
144 }
145 UTILS_LOGI("%{public}p end backtrace", this);
146 }
147 }
148 #endif
149
150 #ifndef TRACK_ALL
EnableTracker()151 void RefCounter::EnableTracker()
152 {
153 std::lock_guard<std::mutex> lock(trackerMutex);
154 #ifdef PRINT_TRACK_AT_ONCE
155 UTILS_LOGI("%{public}p start tracking", this);
156 #endif
157 enableTrack = true;
158 }
159 #endif
160 #endif
161
162 void RefCounter::DebugRefBase([[maybe_unused]]const void* objectId)
163 {
164 #ifdef DEBUG_REFBASE
165 if (enableTrack) {
166 #ifdef PRINT_TRACK_AT_ONCE
167 PrintRefs(objectId);
168 #else
169 GetNewTrace(objectId);
170 #endif
171 }
172 #endif
173 }
174
RefCounter()175 RefCounter::RefCounter()
176 : atomicStrong_(INITIAL_PRIMARY_VALUE), atomicWeak_(0), atomicRefCount_(0), atomicFlags_(0), atomicAttempt_(0)
177 {
178 }
179
GetRefCount()180 int RefCounter::GetRefCount()
181 {
182 return atomicRefCount_.load(std::memory_order_relaxed);
183 }
184
IncRefCount()185 void RefCounter::IncRefCount()
186 {
187 atomicRefCount_.fetch_add(1, std::memory_order_relaxed);
188 }
189
DecRefCount()190 void RefCounter::DecRefCount()
191 {
192 if (atomicRefCount_.load(std::memory_order_relaxed) > 0) {
193 if (atomicRefCount_.fetch_sub(1, std::memory_order_release) == 1) {
194 delete (this);
195 }
196 }
197 }
198
SetCallback(const RefPtrCallback & callback)199 void RefCounter::SetCallback(const RefPtrCallback& callback)
200 {
201 callback_ = callback;
202 }
203
RemoveCallback()204 void RefCounter::RemoveCallback()
205 {
206 callback_ = nullptr;
207 }
208
IsRefPtrValid()209 bool RefCounter::IsRefPtrValid()
210 {
211 return callback_ != nullptr;
212 }
213
214 #ifndef EMULATOR_PLATFORM
SetCanPromote(const CanPromote & canPromote)215 void RefCounter::SetCanPromote(const CanPromote &canPromote)
216 {
217 canPromote_ = canPromote;
218 }
219
RemoveCanPromote()220 void RefCounter::RemoveCanPromote()
221 {
222 canPromote_ = nullptr;
223 }
224
IsCanPromoteValid()225 bool RefCounter::IsCanPromoteValid()
226 {
227 return canPromote_ != nullptr;
228 }
229 #endif
230
~RefCounter()231 RefCounter::~RefCounter()
232 {
233 #ifdef DEBUG_REFBASE
234 if (enableTrack) {
235 #ifdef PRINT_TRACK_AT_ONCE
236 UTILS_LOGI("%{public}p end tracking", this);
237 #else
238 PrintTracker();
239 #endif
240 }
241 #endif
242 }
243
IncStrongRefCount(const void * objectId)244 int RefCounter::IncStrongRefCount(const void* objectId)
245 {
246 DebugRefBase(objectId);
247 int curCount = atomicStrong_.load(std::memory_order_relaxed);
248 if (curCount >= 0) {
249 curCount = atomicStrong_.fetch_add(1, std::memory_order_relaxed);
250 if (curCount == INITIAL_PRIMARY_VALUE) {
251 atomicStrong_.fetch_sub(INITIAL_PRIMARY_VALUE, std::memory_order_release);
252 }
253 }
254
255 return curCount;
256 }
257
DecStrongRefCount(const void * objectId)258 int RefCounter::DecStrongRefCount(const void* objectId)
259 {
260 DebugRefBase(objectId);
261 int curCount = GetStrongRefCount();
262 if (curCount == INITIAL_PRIMARY_VALUE) {
263 // unexpected case: there had never a strong reference.
264 UTILS_LOGD("decStrongRef when there is nerver a strong reference");
265 } else if (curCount > 0) {
266 // we should update the current count here.
267 // it may be changed after last operation.
268 curCount = atomicStrong_.fetch_sub(1, std::memory_order_release);
269 }
270
271 return curCount;
272 }
273
GetStrongRefCount()274 int RefCounter::GetStrongRefCount()
275 {
276 return atomicStrong_.load(std::memory_order_relaxed);
277 }
278
IncWeakRefCount(const void * objectId)279 int RefCounter::IncWeakRefCount(const void* objectId)
280 {
281 DebugRefBase(objectId);
282 return atomicWeak_.fetch_add(1, std::memory_order_relaxed);
283 }
284
DecWeakRefCount(const void * objectId)285 int RefCounter::DecWeakRefCount(const void* objectId)
286 {
287 DebugRefBase(objectId);
288 int curCount = GetWeakRefCount();
289 if (curCount > 0) {
290 curCount = atomicWeak_.fetch_sub(1, std::memory_order_release);
291 }
292
293 if (curCount != 1) {
294 return curCount;
295 }
296 std::atomic_thread_fence(std::memory_order_acquire);
297 if (IsLifeTimeExtended()) {
298 if (callback_) {
299 callback_();
300 }
301 } else {
302 // only weak ptr but never had a strong ref, we should do nothing here theoretically. But it may cause a leak.
303 if (GetStrongRefCount() == INITIAL_PRIMARY_VALUE) {
304 UTILS_LOGW("dec the last weakRef before it had a strong reference, delete refbase to avoid Memory Leak");
305 if (callback_) {
306 callback_();
307 }
308 } else {
309 // free RefCounter
310 DecRefCount();
311 }
312 }
313
314 return curCount;
315 }
316
GetWeakRefCount()317 int RefCounter::GetWeakRefCount()
318 {
319 return atomicWeak_.load(std::memory_order_relaxed);
320 }
321
GetAttemptAcquire()322 int RefCounter::GetAttemptAcquire()
323 {
324 return atomicAttempt_.load(std::memory_order_relaxed);
325 }
326
SetAttemptAcquire()327 void RefCounter::SetAttemptAcquire()
328 {
329 (void)atomicAttempt_.fetch_add(1, std::memory_order_relaxed);
330 }
331
IsAttemptAcquireSet()332 bool RefCounter::IsAttemptAcquireSet()
333 {
334 return static_cast<bool>(atomicAttempt_.load(std::memory_order_relaxed) > 0);
335 }
336
ClearAttemptAcquire()337 void RefCounter::ClearAttemptAcquire()
338 {
339 atomicAttempt_.fetch_sub(1, std::memory_order_relaxed);
340 }
341
ExtendObjectLifetime()342 void RefCounter::ExtendObjectLifetime()
343 {
344 atomicFlags_.fetch_or(FLAG_EXTEND_LIFE_TIME, std::memory_order_relaxed);
345 }
346
IsLifeTimeExtended()347 bool RefCounter::IsLifeTimeExtended()
348 {
349 return static_cast<bool>(atomicFlags_.load(std::memory_order_relaxed) & FLAG_EXTEND_LIFE_TIME);
350 }
351
AttemptIncStrongRef(const void * objectId,int & outCount)352 bool RefCounter::AttemptIncStrongRef(const void *objectId, int &outCount)
353 {
354 int curCount = GetStrongRefCount();
355 IncWeakRefCount(objectId);
356
357 // if the object already had strong references.just promoting it.
358 while ((curCount > 0) && (curCount != INITIAL_PRIMARY_VALUE)) {
359 if (atomicStrong_.compare_exchange_weak(curCount, curCount + 1, std::memory_order_relaxed)) {
360 goto ATTEMPT_SUCCESS;
361 }
362 // someone else changed the counter.re-acquire the counter value.
363 curCount = atomicStrong_.load(std::memory_order_relaxed);
364 }
365
366 if ((curCount == INITIAL_PRIMARY_VALUE) && !IsLifeTimeExtended()) {
367 // this object has a "normal" life-time,
368 while (curCount > 0) {
369 if (atomicStrong_.compare_exchange_weak(curCount, curCount + 1, std::memory_order_relaxed)) {
370 goto ATTEMPT_SUCCESS;
371 }
372 curCount = atomicStrong_.load(std::memory_order_relaxed);
373 }
374 }
375
376 if (IsLifeTimeExtended()) {
377 #ifndef EMULATOR_PLATFORM
378 if (!IsCanPromoteValid() || !canPromote_()) {
379 return false;
380 }
381 #endif
382 curCount = atomicStrong_.fetch_add(1, std::memory_order_relaxed);
383 }
384
385 ATTEMPT_SUCCESS:
386 if (curCount == INITIAL_PRIMARY_VALUE) {
387 outCount = curCount;
388 atomicStrong_.fetch_sub(INITIAL_PRIMARY_VALUE, std::memory_order_release);
389 return true;
390 }
391
392 if (curCount < 0 || (!IsLifeTimeExtended() && curCount == 0)) {
393 // the object destroyed on strong reference count reduce to zero.
394 DecWeakRefCount(objectId);
395 return false;
396 }
397
398 return true;
399 }
400
AttemptIncStrong(const void * objectId)401 bool RefCounter::AttemptIncStrong(const void *objectId)
402 {
403 IncWeakRefCount(objectId);
404 int curCount = GetStrongRefCount();
405 while (curCount > 0) {
406 if (atomicStrong_.compare_exchange_weak(curCount, curCount + 1, std::memory_order_relaxed)) {
407 break;
408 }
409 // curCount has been updated.
410 }
411 if (curCount <= 0) {
412 DecWeakRefCount(objectId);
413 }
414 return curCount > 0;
415 }
416
RefBase()417 RefBase::RefBase() : refs_(new RefCounter())
418 {
419 refs_->IncRefCount();
420 refs_->SetCallback([this] { this->RefPtrCallback(); });
421 #ifndef EMULATOR_PLATFORM
422 refs_->SetCanPromote([this] { return this->CanPromote(); });
423 #endif
424 }
425
RefBase(const RefBase &)426 RefBase::RefBase(const RefBase &)
427 {
428 refs_ = new (std::nothrow) RefCounter();
429 if (refs_ != nullptr) {
430 refs_->IncRefCount();
431 refs_->SetCallback([this] { this->RefPtrCallback(); });
432 #ifndef EMULATOR_PLATFORM
433 refs_->SetCanPromote([this] { return this->CanPromote(); });
434 #endif
435 }
436 }
437
438 #ifndef EMULATOR_PLATFORM
CanPromote()439 bool RefBase::CanPromote()
440 {
441 return true;
442 }
443 #endif
444
RefPtrCallback()445 void RefBase::RefPtrCallback()
446 {
447 delete this;
448 }
449
450 /*
451 * The two ends of the assignment are two independent and exclusive,
452 * and the application should not share the reference counter.
453 * RISK: If there is a reference count on the left of the equal sign,
454 * it may cause a reference count exception
455 */
operator =(const RefBase &)456 RefBase &RefBase::operator=(const RefBase &)
457 {
458 if (refs_ != nullptr) {
459 refs_->RemoveCallback();
460 refs_->DecRefCount();
461 }
462
463 refs_ = new (std::nothrow) RefCounter();
464 if (refs_ != nullptr) {
465 refs_->IncRefCount();
466 refs_->SetCallback([this] { this->RefPtrCallback(); });
467 #ifndef EMULATOR_PLATFORM
468 refs_->SetCanPromote([this] { return this->CanPromote(); });
469 #endif
470 }
471
472 return *this;
473 }
474
RefBase(RefBase && other)475 RefBase::RefBase(RefBase &&other) noexcept
476 {
477 refs_ = other.refs_;
478 other.refs_ = nullptr;
479 }
480
operator =(RefBase && other)481 RefBase &RefBase::operator=(RefBase &&other) noexcept
482 {
483 if (refs_ == other.refs_) {
484 return *this;
485 }
486
487 if (refs_ != nullptr) {
488 refs_->RemoveCallback();
489 refs_->DecRefCount();
490 }
491
492 refs_ = other.refs_;
493 other.refs_ = nullptr;
494 return *this;
495 }
496
~RefBase()497 RefBase::~RefBase()
498 {
499 if (refs_ != nullptr) {
500 refs_->RemoveCallback();
501 if ((refs_->IsLifeTimeExtended() && refs_->GetWeakRefCount() == 0) ||
502 refs_->GetStrongRefCount() == INITIAL_PRIMARY_VALUE) {
503 refs_->DecRefCount();
504 }
505 refs_ = nullptr;
506 }
507 }
508
ExtendObjectLifetime()509 void RefBase::ExtendObjectLifetime()
510 {
511 refs_->ExtendObjectLifetime();
512 }
513
IncStrongRef(const void * objectId)514 void RefBase::IncStrongRef(const void *objectId)
515 {
516 if (refs_ == nullptr) {
517 return;
518 }
519
520 IncWeakRef(objectId);
521 const int curCount = refs_->IncStrongRefCount(objectId);
522 if (!refs_->IsLifeTimeExtended() && curCount == 0) {
523 UTILS_LOGF("%{public}p still incStrongRef after last strong ref", this);
524 }
525 if (curCount == INITIAL_PRIMARY_VALUE) {
526 OnFirstStrongRef(objectId);
527 }
528 }
529
CheckIsAttemptAcquireSet(const void * objectId)530 void RefBase::CheckIsAttemptAcquireSet(const void *objectId)
531 {
532 if (refs_->IsAttemptAcquireSet()) {
533 refs_->ClearAttemptAcquire();
534 const int attemptCount = refs_->GetAttemptAcquire();
535 if (attemptCount < 0) {
536 UTILS_LOGF("Multi-threads trigger illegal decstrong from %{public}d due to AttemptIncStrong in ipc",
537 attemptCount);
538 }
539 refs_->DecStrongRefCount(objectId);
540 refs_->DecWeakRefCount(objectId);
541 }
542 }
543
DecStrongRef(const void * objectId)544 void RefBase::DecStrongRef(const void *objectId)
545 {
546 if (refs_ == nullptr) {
547 return;
548 }
549
550 RefCounter * const refs = refs_;
551 const int curCount = refs->DecStrongRefCount(objectId);
552 if (curCount <= 0) {
553 UTILS_LOGF("%{public}p call decStrongRef too many times", this);
554 }
555 if (curCount == 1) {
556 std::atomic_thread_fence(std::memory_order_acquire);
557 OnLastStrongRef(objectId);
558 if (!refs->IsLifeTimeExtended()) {
559 if (refs->callback_) {
560 refs->callback_();
561 }
562 }
563 }
564
565 refs->DecWeakRefCount(objectId);
566 }
567
GetSptrRefCount()568 int RefBase::GetSptrRefCount()
569 {
570 if (refs_ == nullptr) {
571 return 0;
572 }
573 return refs_->GetStrongRefCount();
574 }
575
CreateWeakRef(void * cookie)576 WeakRefCounter *RefBase::CreateWeakRef(void *cookie)
577 {
578 if (refs_ != nullptr) {
579 return new WeakRefCounter(refs_, cookie);
580 }
581 return nullptr;
582 }
583
IncWeakRef(const void * objectId)584 void RefBase::IncWeakRef(const void *objectId)
585 {
586 if (refs_ != nullptr) {
587 refs_->IncWeakRefCount(objectId);
588 }
589 }
590
GetRefCounter() const591 RefCounter *RefBase::GetRefCounter() const
592 {
593 return refs_;
594 }
595
DecWeakRef(const void * objectId)596 void RefBase::DecWeakRef(const void *objectId)
597 {
598 if (refs_ != nullptr) {
599 refs_->DecWeakRefCount(objectId);
600 }
601 }
602
GetWptrRefCount()603 int RefBase::GetWptrRefCount()
604 {
605 if (refs_ == nullptr) {
606 return 0;
607 }
608 return refs_->GetWeakRefCount();
609 }
610
AttemptAcquire(const void * objectId)611 bool RefBase::AttemptAcquire(const void *objectId)
612 {
613 if (refs_ == nullptr) {
614 return false;
615 }
616
617 int count = 0;
618 if (refs_->AttemptIncStrongRef(objectId, count)) {
619 refs_->SetAttemptAcquire();
620 if (count == INITIAL_PRIMARY_VALUE) {
621 OnFirstStrongRef(objectId);
622 }
623
624 return true;
625 }
626 return false;
627 }
628
AttemptIncStrongRef(const void * objectId)629 bool RefBase::AttemptIncStrongRef(const void *objectId)
630 {
631 if ((refs_ != nullptr) && (OnAttemptPromoted(objectId))) {
632 int count = 0;
633 bool ret = refs_->AttemptIncStrongRef(objectId, count);
634 if (count == INITIAL_PRIMARY_VALUE) {
635 OnFirstStrongRef(objectId);
636 }
637 return ret;
638 }
639
640 return false;
641 }
642
AttemptIncStrong(const void * objectId)643 bool RefBase::AttemptIncStrong(const void *objectId)
644 {
645 if (refs_ == nullptr) {
646 return false;
647 }
648 if (refs_->AttemptIncStrong(objectId)) {
649 refs_->SetAttemptAcquire();
650 return true;
651 }
652 return false;
653 }
654
IsAttemptAcquireSet()655 bool RefBase::IsAttemptAcquireSet()
656 {
657 if (refs_ == nullptr) {
658 return false;
659 }
660 return refs_->IsAttemptAcquireSet();
661 }
662
IsExtendLifeTimeSet()663 bool RefBase::IsExtendLifeTimeSet()
664 {
665 if (refs_ == nullptr) {
666 return false;
667 }
668 return refs_->IsLifeTimeExtended();
669 }
670
OnFirstStrongRef(const void *)671 void RefBase::OnFirstStrongRef(const void*)
672 {}
673
OnLastStrongRef(const void *)674 void RefBase::OnLastStrongRef(const void*)
675 {}
676
OnLastWeakRef(const void *)677 void RefBase::OnLastWeakRef(const void*)
678 {}
679
OnAttemptPromoted(const void *)680 bool RefBase::OnAttemptPromoted(const void*)
681 {
682 return true;
683 }
684
685 #if ((defined DEBUG_REFBASE) && (!defined TRACK_ALL))
EnableTracker()686 void RefBase::EnableTracker()
687 {
688 refs_->EnableTracker();
689 }
690 #else
EnableTracker()691 void RefBase::EnableTracker()
692 {
693 }
694 #endif
695
696 } // namespace OHOS
697