1// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
2// Licensed under the MIT License:
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21
22#include "refcount.h"
23#include "debug.h"
24
25#if _MSC_VER
26// Annoyingly, MSVC only implements the C++ atomic libs, not the C libs, so the only useful
27// thing we can get from <atomic> seems to be atomic_thread_fence... but that one function is
28// indeed not implemented by the intrinsics, so...
29#include <atomic>
30#endif
31
32namespace kj {
33
34// =======================================================================================
35// Non-atomic (thread-unsafe) refcounting
36
37Refcounted::~Refcounted() noexcept(false) {
38 KJ_ASSERT(refcount == 0, "Refcounted object deleted with non-zero refcount.");
39}
40
41void Refcounted::disposeImpl(void* pointer) const {
42 if (--refcount == 0) {
43 delete this;
44 }
45}
46
47// =======================================================================================
48// Atomic (thread-safe) refcounting
49
50AtomicRefcounted::~AtomicRefcounted() noexcept(false) {
51 KJ_ASSERT(refcount == 0, "Refcounted object deleted with non-zero refcount.");
52}
53
54void AtomicRefcounted::disposeImpl(void* pointer) const {
55#if _MSC_VER
56 if (KJ_MSVC_INTERLOCKED(Decrement, rel)(&refcount) == 0) {
57 std::atomic_thread_fence(std::memory_order_acquire);
58 delete this;
59 }
60#else
61 if (__atomic_sub_fetch(&refcount, 1, __ATOMIC_RELEASE) == 0) {
62 __atomic_thread_fence(__ATOMIC_ACQUIRE);
63 delete this;
64 }
65#endif
66}
67
68bool AtomicRefcounted::addRefWeakInternal() const {
69#if _MSC_VER
70 long orig = refcount;
71
72 for (;;) {
73 if (orig == 0) {
74 // Refcount already hit zero. Destructor is already running so we can't revive the object.
75 return false;
76 }
77
78 unsigned long old = KJ_MSVC_INTERLOCKED(CompareExchange, nf)(&refcount, orig + 1, orig);
79 if (old == orig) {
80 return true;
81 }
82 orig = old;
83 }
84#else
85 uint orig = __atomic_load_n(&refcount, __ATOMIC_RELAXED);
86
87 for (;;) {
88 if (orig == 0) {
89 // Refcount already hit zero. Destructor is already running so we can't revive the object.
90 return false;
91 }
92
93 if (__atomic_compare_exchange_n(&refcount, &orig, orig + 1, true,
94 __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
95 // Successfully incremented refcount without letting it hit zero.
96 return true;
97 }
98 }
99#endif
100}
101
102} // namespace kj
103