Skip to content

Commit 8605da8

Browse files
authored
Merge pull request #68886 from kubamracek/embedded-atomic-refcount
[embedded] Use atomic load/store/rmw in embedded runtime refcounting implementation
2 parents d2eaa84 + a3f2984 commit 8605da8

File tree

1 file changed

+78
-18
lines changed

1 file changed

+78
-18
lines changed

stdlib/public/core/EmbeddedRuntime.swift

Lines changed: 78 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
import SwiftShims
1414

15+
/// Class object and class metadata structures
16+
1517
public struct ClassMetadata {
1618
var superclassMetadata: UnsafeMutablePointer<ClassMetadata>?
1719

@@ -39,6 +41,10 @@ public struct HeapObject {
3941
static let immortalRefCount = -1
4042
}
4143

44+
45+
46+
/// Allocations
47+
4248
func alignedAlloc(size: Int, alignment: Int) -> UnsafeMutableRawPointer? {
4349
let alignment = max(alignment, MemoryLayout<UnsafeRawPointer>.size)
4450
var r: UnsafeMutableRawPointer? = nil
@@ -94,52 +100,98 @@ public func swift_initStackObject(metadata: UnsafeMutablePointer<ClassMetadata>,
94100
return object
95101
}
96102

103+
104+
105+
/// Refcounting
106+
97107
@_silgen_name("swift_setDeallocating")
98108
public func swift_setDeallocating(object: UnsafeMutablePointer<HeapObject>) {
99109
}
100110

101-
// TODO/FIXME: Refcounting and swift_once is not thread-safe, the following only works in single-threaded environments.
102-
103111
@_silgen_name("swift_isUniquelyReferenced_nonNull_native")
104112
public func swift_isUniquelyReferenced_nonNull_native(object: UnsafeMutablePointer<HeapObject>) -> Bool {
105-
// TODO/FIXME: Refcounting is not thread-safe, the following only works in single-threaded environments.
106-
return object.pointee.refcount == 1
113+
let refcount = refcountPointer(for: object)
114+
return loadAcquire(refcount) == 1
107115
}
108116

109117
@_silgen_name("swift_retain")
110118
public func swift_retain(object: Builtin.RawPointer) -> Builtin.RawPointer {
111119
return swift_retain_n(object: object, n: 1)
112120
}
113121

122+
// Cannot use UnsafeMutablePointer<HeapObject>? directly in the function argument or return value as it causes IRGen crashes
114123
@_silgen_name("swift_retain_n")
115124
public func swift_retain_n(object: Builtin.RawPointer, n: UInt32) -> Builtin.RawPointer {
116125
if Int(Builtin.ptrtoint_Word(object)) == 0 { return object }
117126
let o = UnsafeMutablePointer<HeapObject>(object)
118-
// TODO/FIXME: Refcounting is not thread-safe, the following only works in single-threaded environments.
119-
if o.pointee.refcount == HeapObject.immortalRefCount { return o._rawValue }
120-
o.pointee.refcount += Int(n)
121-
return o._rawValue
127+
return swift_retain_n_(object: o, n: n)._rawValue
128+
}
129+
130+
func swift_retain_n_(object: UnsafeMutablePointer<HeapObject>, n: UInt32) -> UnsafeMutablePointer<HeapObject> {
131+
let refcount = refcountPointer(for: object)
132+
if loadRelaxed(refcount) == HeapObject.immortalRefCount {
133+
return object
134+
}
135+
136+
addRelaxed(refcount, n: Int(n))
137+
138+
return object
122139
}
123140

124141
@_silgen_name("swift_release")
125-
public func swift_release(object: Builtin.RawPointer) {
142+
public func swift_release(object: UnsafeMutablePointer<HeapObject>?) {
126143
swift_release_n(object: object, n: 1)
127144
}
128145

129146
@_silgen_name("swift_release_n")
130-
public func swift_release_n(object: Builtin.RawPointer, n: UInt32) {
131-
if Int(Builtin.ptrtoint_Word(object)) == 0 { return }
132-
let o = UnsafeMutablePointer<HeapObject>(object)
133-
// TODO/FIXME: Refcounting is not thread-safe, the following only works in single-threaded environments.
134-
if o.pointee.refcount == HeapObject.immortalRefCount { return }
135-
o.pointee.refcount -= Int(n)
136-
if (o.pointee.refcount & HeapObject.refcountMask) == 0 {
137-
_swift_embedded_invoke_heap_object_destroy(o)
138-
} else if (o.pointee.refcount & HeapObject.refcountMask) < 0 {
147+
public func swift_release_n(object: UnsafeMutablePointer<HeapObject>?, n: UInt32) {
148+
guard let object else {
149+
return
150+
}
151+
152+
let refcount = refcountPointer(for: object)
153+
if loadRelaxed(refcount) == HeapObject.immortalRefCount {
154+
return
155+
}
156+
157+
let resultingRefcount = subFetchAcquireRelease(refcount, n: Int(n)) & HeapObject.refcountMask
158+
if resultingRefcount == 0 {
159+
_swift_embedded_invoke_heap_object_destroy(object)
160+
} else if resultingRefcount < 0 {
139161
fatalError("negative refcount")
140162
}
141163
}
142164

165+
166+
167+
/// Refcount helpers
168+
169+
fileprivate func refcountPointer(for object: UnsafeMutablePointer<HeapObject>) -> UnsafeMutablePointer<Int> {
170+
// TODO: This should use MemoryLayout<HeapObject>.offset(to: \.refcount) but we don't have KeyPaths yet
171+
return UnsafeMutablePointer<Int>(UnsafeRawPointer(object).advanced(by: MemoryLayout<Int>.size)._rawValue)
172+
}
173+
174+
fileprivate func loadRelaxed(_ refcount: UnsafeMutablePointer<Int>) -> Int {
175+
Int(Builtin.atomicload_monotonic_Word(refcount._rawValue))
176+
}
177+
178+
fileprivate func loadAcquire(_ refcount: UnsafeMutablePointer<Int>) -> Int {
179+
Int(Builtin.atomicload_acquire_Word(refcount._rawValue))
180+
}
181+
182+
fileprivate func subFetchAcquireRelease(_ refcount: UnsafeMutablePointer<Int>, n: Int) -> Int {
183+
let oldValue = Int(Builtin.atomicrmw_sub_acqrel_Word(refcount._rawValue, n._builtinWordValue))
184+
return oldValue - n
185+
}
186+
187+
fileprivate func addRelaxed(_ refcount: UnsafeMutablePointer<Int>, n: Int) {
188+
_ = Builtin.atomicrmw_add_monotonic_Word(refcount._rawValue, n._builtinWordValue)
189+
}
190+
191+
192+
193+
/// Exclusivity checking
194+
143195
@_silgen_name("swift_beginAccess")
144196
public func swift_beginAccess(pointer: UnsafeMutableRawPointer, buffer: UnsafeMutableRawPointer, flags: UInt, pc: UnsafeMutableRawPointer) {
145197
// TODO: Add actual exclusivity checking.
@@ -150,6 +202,10 @@ public func swift_endAccess(buffer: UnsafeMutableRawPointer) {
150202
// TODO: Add actual exclusivity checking.
151203
}
152204

205+
206+
207+
// Once
208+
153209
@_silgen_name("swift_once")
154210
public func swift_once(predicate: UnsafeMutablePointer<Int>, fn: (@convention(c) (UnsafeMutableRawPointer)->()), context: UnsafeMutableRawPointer) {
155211
// TODO/FIXME: The following only works in single-threaded environments.
@@ -160,6 +216,10 @@ public func swift_once(predicate: UnsafeMutablePointer<Int>, fn: (@convention(c)
160216
}
161217
}
162218

219+
220+
221+
// Misc
222+
163223
@_silgen_name("swift_deletedMethodError")
164224
public func swift_deletedMethodError() -> Never {
165225
Builtin.int_trap()

0 commit comments

Comments
 (0)