xref: /commonlibrary/c_utils/base/src/refbase.cpp (revision 3f4cbf05)
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
22namespace OHOS {
23
24WeakRefCounter::WeakRefCounter(RefCounter *counter, void *cookie)
25    : atomicWeak_(0), refCounter_(counter), cookie_(cookie)
26{
27    if (refCounter_ != nullptr) {
28        refCounter_->IncRefCount();
29    }
30}
31
32WeakRefCounter::~WeakRefCounter()
33{
34    if (refCounter_ != nullptr) {
35        refCounter_->DecRefCount();
36    }
37}
38
39int WeakRefCounter::GetWeakRefCount() const
40{
41    return atomicWeak_.load(std::memory_order_relaxed);
42}
43
44void* WeakRefCounter::GetRefPtr()
45{
46    if ((cookie_ != nullptr) && (!refCounter_->IsRefPtrValid())) {
47        cookie_ = nullptr;
48    }
49    return cookie_;
50}
51
52void WeakRefCounter::IncWeakRefCount(const void *objectId)
53{
54    if (atomicWeak_.fetch_add(1, std::memory_order_relaxed) == 0) {
55        refCounter_->IncWeakRefCount(objectId);
56    }
57}
58
59void 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
67bool 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.
80class RefTracker {
81public:
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
88private:
89    const void* ptrID;
90    int strongRefCnt;
91    int weakRefCnt;
92    int refCnt;
93    int PID;
94    int TID;
95    RefTracker* exTrace;
96};
97
98RefTracker::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
103void 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
110RefTracker* 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
121void 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
129void 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
137void 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
151void 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
162void 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
175RefCounter::RefCounter()
176    : atomicStrong_(INITIAL_PRIMARY_VALUE), atomicWeak_(0), atomicRefCount_(0), atomicFlags_(0), atomicAttempt_(0)
177{
178}
179
180int RefCounter::GetRefCount()
181{
182    return atomicRefCount_.load(std::memory_order_relaxed);
183}
184
185void RefCounter::IncRefCount()
186{
187    atomicRefCount_.fetch_add(1, std::memory_order_relaxed);
188}
189
190void 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
199void RefCounter::SetCallback(const RefPtrCallback& callback)
200{
201    callback_ = callback;
202}
203
204void RefCounter::RemoveCallback()
205{
206    callback_ = nullptr;
207}
208
209bool RefCounter::IsRefPtrValid()
210{
211    return callback_ != nullptr;
212}
213
214#ifndef EMULATOR_PLATFORM
215void RefCounter::SetCanPromote(const CanPromote &canPromote)
216{
217    canPromote_ = canPromote;
218}
219
220void RefCounter::RemoveCanPromote()
221{
222    canPromote_ = nullptr;
223}
224
225bool RefCounter::IsCanPromoteValid()
226{
227    return canPromote_ != nullptr;
228}
229#endif
230
231RefCounter::~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
244int 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
258int 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
274int RefCounter::GetStrongRefCount()
275{
276    return atomicStrong_.load(std::memory_order_relaxed);
277}
278
279int RefCounter::IncWeakRefCount(const void* objectId)
280{
281    DebugRefBase(objectId);
282    return atomicWeak_.fetch_add(1, std::memory_order_relaxed);
283}
284
285int 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
317int RefCounter::GetWeakRefCount()
318{
319    return atomicWeak_.load(std::memory_order_relaxed);
320}
321
322int RefCounter::GetAttemptAcquire()
323{
324    return atomicAttempt_.load(std::memory_order_relaxed);
325}
326
327void RefCounter::SetAttemptAcquire()
328{
329    (void)atomicAttempt_.fetch_add(1, std::memory_order_relaxed);
330}
331
332bool RefCounter::IsAttemptAcquireSet()
333{
334    return static_cast<bool>(atomicAttempt_.load(std::memory_order_relaxed) > 0);
335}
336
337void RefCounter::ClearAttemptAcquire()
338{
339    atomicAttempt_.fetch_sub(1, std::memory_order_relaxed);
340}
341
342void RefCounter::ExtendObjectLifetime()
343{
344    atomicFlags_.fetch_or(FLAG_EXTEND_LIFE_TIME, std::memory_order_relaxed);
345}
346
347bool RefCounter::IsLifeTimeExtended()
348{
349    return static_cast<bool>(atomicFlags_.load(std::memory_order_relaxed) & FLAG_EXTEND_LIFE_TIME);
350}
351
352bool 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
385ATTEMPT_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
401bool 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
417RefBase::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
426RefBase::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
439bool RefBase::CanPromote()
440{
441    return true;
442}
443#endif
444
445void 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 */
456RefBase &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
475RefBase::RefBase(RefBase &&other) noexcept
476{
477    refs_ = other.refs_;
478    other.refs_ = nullptr;
479}
480
481RefBase &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
497RefBase::~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
509void RefBase::ExtendObjectLifetime()
510{
511    refs_->ExtendObjectLifetime();
512}
513
514void 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
530void 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
544void 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
568int RefBase::GetSptrRefCount()
569{
570    if (refs_ == nullptr) {
571        return 0;
572    }
573    return refs_->GetStrongRefCount();
574}
575
576WeakRefCounter *RefBase::CreateWeakRef(void *cookie)
577{
578    if (refs_ != nullptr) {
579        return new WeakRefCounter(refs_, cookie);
580    }
581    return nullptr;
582}
583
584void RefBase::IncWeakRef(const void *objectId)
585{
586    if (refs_ != nullptr) {
587        refs_->IncWeakRefCount(objectId);
588    }
589}
590
591RefCounter *RefBase::GetRefCounter() const
592{
593    return refs_;
594}
595
596void RefBase::DecWeakRef(const void *objectId)
597{
598    if (refs_ != nullptr) {
599        refs_->DecWeakRefCount(objectId);
600    }
601}
602
603int RefBase::GetWptrRefCount()
604{
605    if (refs_ == nullptr) {
606        return 0;
607    }
608    return refs_->GetWeakRefCount();
609}
610
611bool 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
629bool 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
643bool 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
655bool RefBase::IsAttemptAcquireSet()
656{
657    if (refs_ == nullptr) {
658        return false;
659    }
660    return refs_->IsAttemptAcquireSet();
661}
662
663bool RefBase::IsExtendLifeTimeSet()
664{
665    if (refs_ == nullptr) {
666        return false;
667    }
668    return refs_->IsLifeTimeExtended();
669}
670
671void RefBase::OnFirstStrongRef(const void*)
672{}
673
674void RefBase::OnLastStrongRef(const void*)
675{}
676
677void RefBase::OnLastWeakRef(const void*)
678{}
679
680bool RefBase::OnAttemptPromoted(const void*)
681{
682    return true;
683}
684
685#if ((defined DEBUG_REFBASE) && (!defined TRACK_ALL))
686void RefBase::EnableTracker()
687{
688    refs_->EnableTracker();
689}
690#else
691void RefBase::EnableTracker()
692{
693}
694#endif
695
696}  // namespace OHOS
697