mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[embedded] Document the details of the Embedded Swift's refcounting scheme
This commit is contained in:
@@ -28,6 +28,67 @@ public struct ClassMetadata {
|
|||||||
var ivarDestroyer: UnsafeRawPointer?
|
var ivarDestroyer: UnsafeRawPointer?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Embedded Swift Refcounting Scheme
|
||||||
|
=================================
|
||||||
|
|
||||||
|
The scheme for storing and maintaining a refcount on heap objects is very simple in Embedded Swift, and is much
|
||||||
|
simpler than regular Swift's. This is mainly due to the fact that we currently only maintain the regular ("strong")
|
||||||
|
refcount and we don't allow weak references, unowned references and we don't track refcount during deinit of the
|
||||||
|
object.
|
||||||
|
|
||||||
|
The refcount is always stored directly inline in the heap object, in the `refcount` field (see HeapObject struct
|
||||||
|
below). This field has the following structure (on 32-bit, and similar on other bitwidths):
|
||||||
|
|
||||||
|
┌──────────────┬──────────────────────────────────────────────┐
|
||||||
|
│ b31 │ b30:b0 │
|
||||||
|
├──────────────┼──────────────────────────────────────────────┤
|
||||||
|
│ doNotFreeBit │ actual number of references │
|
||||||
|
└──────────────┴──────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
If the highest bit (doNotFreeBit) is set, the behavior of dropping the last reference (release operation where
|
||||||
|
refcount ends up being 0) is altered to avoid calling free() on the object (deinit is still run). This is crutial for
|
||||||
|
class instances that are promoted by the compiler from being heap-allocated to instead be located on the stack
|
||||||
|
(see swift_initStackObject).
|
||||||
|
|
||||||
|
To retrieve the actual number of references from the `refcount` field, refcountMask needs to be applied, which masks
|
||||||
|
off the doNotFreeBit.
|
||||||
|
|
||||||
|
The actual number of references has one possible value that has a special meaning, immortalRefCount (all bits set,
|
||||||
|
i.e. 0x7fff_ffff on 32-bit systems). When used, retain and release operations do nothing, references are not counted,
|
||||||
|
and the object can never be deinit'd / free'd. This is used for class instances that are promoted by the compiler to
|
||||||
|
be allocated statically in global memory (see swift_initStaticObject). Note that there are two different scenarios for
|
||||||
|
this currently:
|
||||||
|
|
||||||
|
- In most cases, a class instance that is promoted to a global, is still dynamically initialized with a runtime call
|
||||||
|
to swift_initStaticObject. This function will set the refcount field to immortalRefCount | doNotFreeBit.
|
||||||
|
- As a special case to allow arrays be fully statically initialized without runtime overhead, instances of
|
||||||
|
_ContiguousArrayStorage can be promoted to __StaticArrayStorage with the HeapObject header emitted directly by the
|
||||||
|
compiler and refcount field directly set to immortalRefCount | doNotFreeBit (see irgen::emitConstantObject).
|
||||||
|
|
||||||
|
Tne immortalRefCount is additionally also used as a placeholder value for objects (heap-allocated or stack-allocated)
|
||||||
|
when they're currently inside their deinit(). This is done to prevent further retains and releases inside deinit from
|
||||||
|
triggering deinitialization again, without the need to reserve another bit for this purpose. Retains and releases in
|
||||||
|
deinit() are allowed, as long as they are balanced at the end, i.e. the object is not escaped (user's responsibility)
|
||||||
|
and not over-released (this can only be caused by unsafe code).
|
||||||
|
|
||||||
|
The following table summarizes the meaning of the possible combinations of doNotFreeBit and have immortal refcount
|
||||||
|
value:
|
||||||
|
|
||||||
|
┌───────────╥──────────╥─────────────────────────────────────────────────┐
|
||||||
|
│ doNotFree ║ immortal ║ │
|
||||||
|
╞═══════════╬══════════╬═════════════════════════════════════════════════╡
|
||||||
|
│ 0 ║ no ║ regular class instance │
|
||||||
|
├───────────╫──────────╫─────────────────────────────────────────────────┤
|
||||||
|
│ 0 ║ yes ║ regular class instance during deinit() │
|
||||||
|
├───────────╫──────────╫─────────────────────────────────────────────────┤
|
||||||
|
│ 1 ║ no ║ stack-allocated │
|
||||||
|
├───────────╫──────────╫─────────────────────────────────────────────────┤
|
||||||
|
│ 1 ║ yes ║ global-allocated, no need to track references, │
|
||||||
|
│ ║ ║ or stack-allocated instance during deinit() │
|
||||||
|
└───────────╨──────────╨─────────────────────────────────────────────────┘
|
||||||
|
*/
|
||||||
|
|
||||||
public struct HeapObject {
|
public struct HeapObject {
|
||||||
// There is no way to express the custom ptrauth signature on the metadata
|
// There is no way to express the custom ptrauth signature on the metadata
|
||||||
// field, so let's use UnsafeRawPointer and a helper function in C instead
|
// field, so let's use UnsafeRawPointer and a helper function in C instead
|
||||||
|
|||||||
Reference in New Issue
Block a user