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?
|
||||
}
|
||||
|
||||
/*
|
||||
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 {
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user