Realtime threads with Swift

We have tests for this and it was really really hard to get right :smiley:

3 Likes

Fortunately, the relaxed compare-exchange in the snippet above is actually only used in the case where the refcount can be kept inline, and the out-of-line case (including the initial transition) takes a different path.

3 Likes

[/quote]
Hold up. Retain and release are not β€œoutlawed”.
[/quote]

I knew I saw it somewhere years ago.

Although as was rightfully pointed out by @ksluder, @David_Smith and others unless side table is used (which is highly unlikely – you need to be very creative to get into that situation on purpose) –"retain / release are fine in realtime" (obviously if the object is not getting deallocated there, and not considering the complications of NSObject or its subclasses objects).

I remember the notion of immortal objects, is this not public API yet, and secondly, once you mark something immortal (e.g. to exclude all further ARC traffic on it) can you later unmark it, to finally get rid of it when the time comes?

There's nothing specifically preventing making immortality a two way switch, except that I don't know how to do it safely. Every scenario I can think of where you can reasonably say "ok, NOW it's ok to start refcounting again", if you have that much control over who is using the object, you could probably have made it move-only instead.

However, when I added immortality I didn't have any use-cases for removing it in mind, so I didn't spend a lot of time thinking about it. The big question you'd have to answer is "when something starts having a refcount again, what is its refcount?" The obvious values I can think of are 1 ("things can only become mortal again if uniquely referenced") or 0 (basically equivalent to just calling free()).

3 Likes

IIUC saturating arithmetic would result into overreleases when the object is released the corresponding number of times as it was retained.

Either of these sounds reasonable. The main use case would be "how do I delete this object". e.g.:

makeImmortal(a)
foo(a) // pass to some external `foo` which is unaware of `a` immorality
// foo can `retain` / `release` `a` - but those would be ignored
deleteImmortal(a)

oh, yes, I meant saturate-and-pin i.e. leak anything that maxes out its refcount

Have you considered terminating the process in this case? I know it is gross..

Attacker-controlled process termination can also be a problem, but yeah, that's a valid approach in many cases

I wrote a quick test to retain an object 2^30 times (plus a few), this is what I'm getting:

Fatal error: Object was retained too many times

If I am not mistaken this is the corresponding code:

template <typename RefCountBits>
HeapObject *RefCounts<RefCountBits>::incrementSlow(RefCountBits oldbits,
                                                   uint32_t n) {
  if (oldbits.isImmortal(false)) {
    return getHeapObject();
  }
  else if (oldbits.hasSideTable()) {
    // Out-of-line slow path.
    auto side = oldbits.getSideTable();
    side->incrementStrong(n);
  }
  else {
    // Retain count overflow.
    swift::swift_abortRetainOverflow();
  }
  return getHeapObject();
}

No side table creation on retain? ATM I am using obj-c file (with -fno-objc-arc set on it) to call retain and up until it overflows it works ok (the change is observed with retainCount).

1 Like

Huh. Sounds like the comment near the top of RefCount.h is incorrect then! Good find.

Just came across this discussion as we try to figure out how to handle real-time audio correctly... .

@audulus : I saw your implementation of Synchronized AudioBuffer List on AudioKit v6 using Atomics. I was wondering with the time past, whether this is a valid solution?! Let's say the producer being a simple MIDI message filling buffers here.

Thanks for sharing.