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
17#include "refbase.h"
18
19namespace OHOS {
20WeakRefCounter::WeakRefCounter(RefCounter *counter, void *cookie)
21    : atomicWeak_(0), refCounter_(counter), cookie_(cookie)
22{
23    if (refCounter_ != nullptr) {
24        refCounter_->IncRefCount();
25    }
26}
27
28WeakRefCounter::~WeakRefCounter()
29{
30    if (refCounter_ != nullptr) {
31        refCounter_->DecRefCount();
32    }
33}
34
35void* WeakRefCounter::GetRefPtr()
36{
37    if ((cookie_ != nullptr) && (!refCounter_->IsRefPtrValid())) {
38        cookie_ = nullptr;
39    }
40    return cookie_;
41}
42
43void WeakRefCounter::IncWeakRefCount(const void *objectId)
44{
45    if (atomicWeak_.fetch_add(1, std::memory_order_relaxed) == 0) {
46        refCounter_->IncWeakRefCount(objectId);
47    }
48}
49
50void WeakRefCounter::DecWeakRefCount(const void *objectId)
51{
52    if (atomicWeak_.fetch_sub(1, std::memory_order_release) == 1) {
53        refCounter_->DecWeakRefCount(objectId);
54        delete this;
55    }
56}
57
58bool WeakRefCounter::AttemptIncStrongRef(const void *objectId)
59{
60    int unuse = 0;
61    return refCounter_->AttemptIncStrongRef(objectId, unuse);
62}
63
64RefCounter::RefCounter()
65    : atomicStrong_(INITIAL_PRIMARY_VALUE), atomicWeak_(0), atomicRefCount_(0), atomicFlags_(0), atomicAttempt_(0)
66{
67}
68int RefCounter::GetRefCount()
69{
70    return atomicRefCount_.load(std::memory_order_relaxed);
71}
72
73void RefCounter::IncRefCount()
74{
75    atomicRefCount_.fetch_add(1, std::memory_order_relaxed);
76}
77
78void RefCounter::DecRefCount()
79{
80    if (atomicRefCount_.load(std::memory_order_relaxed) > 0) {
81        if (atomicRefCount_.fetch_sub(1, std::memory_order_release) == 1) {
82            delete (this);
83        }
84    }
85}
86
87void RefCounter::SetCallback(const RefPtrCallback& callback)
88{
89    callback_ = callback;
90}
91
92void RefCounter::RemoveCallback()
93{
94    callback_ = nullptr;
95}
96
97bool RefCounter::IsRefPtrValid()
98{
99    return callback_ != nullptr;
100}
101
102RefCounter::~RefCounter()
103{
104}
105
106int RefCounter::IncStrongRefCount(const void * /*objectId*/)
107{
108    int curCount = atomicStrong_.load(std::memory_order_relaxed);
109    if (curCount >= 0) {
110        curCount = atomicStrong_.fetch_add(1, std::memory_order_relaxed);
111        if (curCount == INITIAL_PRIMARY_VALUE) {
112            atomicStrong_.fetch_sub(INITIAL_PRIMARY_VALUE, std::memory_order_release);
113        }
114    }
115    return curCount;
116}
117
118int RefCounter::DecStrongRefCount(const void * /*objectId*/)
119{
120    int curCount = GetStrongRefCount();
121    if (curCount == INITIAL_PRIMARY_VALUE) {
122        // unexpected case: there had never a strong reference.
123    } else if (curCount > 0) {
124        // we should update the current count here.
125        // it may be changed after last operation.
126        curCount = atomicStrong_.fetch_sub(1, std::memory_order_release);
127    }
128    return curCount;
129}
130
131int RefCounter::GetStrongRefCount()
132{
133    return atomicStrong_.load(std::memory_order_relaxed);
134}
135
136int RefCounter::IncWeakRefCount(const void * /*objectId*/)
137{
138    return atomicWeak_.fetch_add(1, std::memory_order_relaxed);
139}
140
141int RefCounter::DecWeakRefCount(const void * /*objectId*/)
142{
143    int curCount = GetWeakRefCount();
144    if (curCount > 0) {
145        curCount = atomicWeak_.fetch_sub(1, std::memory_order_release);
146    }
147    int strongRefCount = GetStrongRefCount();
148    if ((curCount == 1) || (strongRefCount == 0 && !IsLifeTimeExtended())) {
149        if (callback_) {
150            callback_();
151        }
152    }
153    return curCount;
154}
155
156int RefCounter::GetWeakRefCount()
157{
158    return atomicWeak_.load(std::memory_order_relaxed);
159}
160
161void RefCounter::SetAttemptAcquire()
162{
163    (void)atomicAttempt_.fetch_add(1, std::memory_order_relaxed);
164}
165
166bool RefCounter::IsAttemptAcquireSet()
167{
168    return static_cast<bool>(atomicAttempt_.load(std::memory_order_relaxed) > 0);
169}
170
171void RefCounter::ClearAttemptAcquire()
172{
173    atomicAttempt_.fetch_sub(1, std::memory_order_relaxed);
174}
175
176void RefCounter::ExtendObjectLifetime()
177{
178    atomicFlags_.fetch_or(FLAG_EXTEND_LIFE_TIME, std::memory_order_relaxed);
179}
180
181bool RefCounter::IsLifeTimeExtended()
182{
183    return static_cast<bool>(atomicFlags_.load(std::memory_order_relaxed) & FLAG_EXTEND_LIFE_TIME);
184}
185
186bool RefCounter::AttemptIncStrongRef(const void *objectId, int &outCount)
187{
188    int curCount = GetStrongRefCount();
189    IncWeakRefCount(objectId);
190
191    // if the object already had strong references.just promoting it.
192    while ((curCount > 0) && (curCount != INITIAL_PRIMARY_VALUE)) {
193        if (atomicStrong_.compare_exchange_weak(curCount, curCount + 1, std::memory_order_relaxed)) {
194            goto attempt_success;
195        }
196
197        // someone else changed the counter.re-acquire the counter value.
198        curCount = atomicStrong_.load(std::memory_order_relaxed);
199    }
200
201    if ((curCount == INITIAL_PRIMARY_VALUE) && !IsLifeTimeExtended()) {
202        // this object has a "normal" life-time,
203        while (curCount > 0) {
204            if (atomicStrong_.compare_exchange_weak(curCount, curCount + 1, std::memory_order_relaxed)) {
205                goto attempt_success;
206            }
207
208            curCount = atomicStrong_.load(std::memory_order_relaxed);
209        }
210    }
211
212    if (IsLifeTimeExtended()) {
213        curCount = atomicStrong_.fetch_add(1, std::memory_order_relaxed);
214    }
215
216attempt_success:
217    if (curCount >= INITIAL_PRIMARY_VALUE) {
218        outCount = curCount;
219        atomicStrong_.fetch_sub(INITIAL_PRIMARY_VALUE, std::memory_order_release);
220        return true;
221    }
222
223    if (curCount < 0 || (!IsLifeTimeExtended() && curCount == 0)) {
224        // the object destroyed on strong reference count reduce to zero.
225        DecWeakRefCount(objectId);
226        return false;
227    }
228    return true;
229}
230
231RefBase::RefBase() : refs_(new RefCounter())
232{
233    refs_->IncRefCount();
234    refs_->SetCallback([this] {
235        this->RefPtrCallback();
236    });
237}
238
239RefBase::RefBase(const RefBase& /*other*/)
240{
241    refs_ = new RefCounter();
242    if (refs_ != nullptr) {
243        refs_->IncRefCount();
244        refs_->SetCallback([this] {
245            this->RefPtrCallback();
246        });
247    }
248}
249
250void RefBase::RefPtrCallback()
251{
252    delete this;
253}
254
255/*
256 * The two ends of the assignment are two independent and exclusive,
257 * and the application should not share the reference counter.
258 * RISK: If there is a reference count on the left of the equal sign,
259 * it may cause a reference count exception
260 */
261RefBase &RefBase::operator=(const RefBase& /*other*/)
262{
263    if (refs_ != nullptr) {
264        refs_->RemoveCallback();
265        refs_->DecRefCount();
266    }
267    refs_ = new RefCounter();
268    if (refs_ != nullptr) {
269        refs_->IncRefCount();
270        refs_->SetCallback([this] {
271            this->RefPtrCallback();
272        });
273    }
274    return *this;
275}
276
277RefBase::RefBase(RefBase &&other) noexcept
278{
279    refs_ = other.refs_;
280    other.refs_ = nullptr;
281}
282
283RefBase &RefBase::operator=(RefBase &&other)  noexcept
284{
285    if (refs_ == other.refs_) {
286        return *this;
287    }
288    if (refs_ != nullptr) {
289        refs_->RemoveCallback();
290        refs_->DecRefCount();
291    }
292    refs_ = other.refs_;
293    other.refs_ = nullptr;
294    return *this;
295}
296
297RefBase::~RefBase()
298{
299    if (refs_ != nullptr) {
300        refs_->RemoveCallback();
301        refs_->DecRefCount();
302        refs_ = nullptr;
303    }
304}
305
306void RefBase::ExtendObjectLifetime()
307{
308    refs_->ExtendObjectLifetime();
309}
310
311void RefBase::IncStrongRef(const void *objectId)
312{
313    if (refs_ == nullptr) {
314        return;
315    }
316    const int curCount = refs_->IncStrongRefCount(objectId);
317    IncWeakRef(objectId);
318    if (curCount == INITIAL_PRIMARY_VALUE) {
319        OnFirstStrongRef(objectId);
320    }
321    if (refs_->IsAttemptAcquireSet()) {
322        refs_->ClearAttemptAcquire();
323        refs_->DecStrongRefCount(objectId);
324    }
325}
326
327void RefBase::DecStrongRef(const void *objectId)
328{
329    if (refs_ == nullptr) {
330        return;
331    }
332    const int curCount = refs_->DecStrongRefCount(objectId);
333    if (curCount == 1) {
334        OnLastStrongRef(objectId);
335    }
336    DecWeakRef(objectId);
337}
338
339int RefBase::GetSptrRefCount()
340{
341    if (refs_ != nullptr) {
342        return refs_->GetStrongRefCount();
343    } else {
344        return 0;
345    }
346}
347
348WeakRefCounter *RefBase::CreateWeakRef(void *cookie)
349{
350    if (refs_ != nullptr) {
351        return new WeakRefCounter(refs_, cookie);
352    }
353
354    return nullptr;
355}
356
357void RefBase::IncWeakRef(const void *objectId)
358{
359    if (refs_ != nullptr) {
360        refs_->IncWeakRefCount(objectId);
361    }
362}
363
364void RefBase::DecWeakRef(const void *objectId)
365{
366    if (refs_ != nullptr) {
367        refs_->DecWeakRefCount(objectId);
368    }
369}
370
371int RefBase::GetWptrRefCount()
372{
373    if (refs_ != nullptr) {
374        return refs_->GetWeakRefCount();
375    } else {
376        return 0;
377    }
378}
379
380bool RefBase::AttemptAcquire(const void *objectId)
381{
382    if (refs_ != nullptr) {
383        int count = 0;
384        if (refs_->AttemptIncStrongRef(objectId, count)) {
385            if (count == INITIAL_PRIMARY_VALUE) {
386                OnFirstStrongRef(objectId);
387            }
388            refs_->SetAttemptAcquire();
389            return true;
390        }
391    }
392    return false;
393}
394
395bool RefBase::AttemptIncStrongRef(const void *objectId)
396{
397    if ((refs_ != nullptr) && (OnAttemptPromoted(objectId))) {
398        int count = 0;
399        bool ret = refs_->AttemptIncStrongRef(objectId, count);
400        if (count == INITIAL_PRIMARY_VALUE) {
401            OnFirstStrongRef(objectId);
402        }
403        return ret;
404    }
405    return false;
406}
407
408bool RefBase::IsAttemptAcquireSet()
409{
410    if (refs_ != nullptr) {
411        return refs_->IsAttemptAcquireSet();
412    }
413    return false;
414}
415
416bool RefBase::IsExtendLifeTimeSet()
417{
418    if (refs_ != nullptr) {
419        return refs_->IsLifeTimeExtended();
420    }
421    return false;
422}
423
424void RefBase::OnFirstStrongRef(const void *)
425{}
426
427void RefBase::OnLastStrongRef(const void *)
428{}
429
430void RefBase::OnLastWeakRef(const void *)
431{}
432
433bool RefBase::OnAttemptPromoted(const void *)
434{
435    return true;
436}
437} // namespace OHOS
438