Does String copy storage between actors?

Hey all,

I’m migrating a library to Swift 6.

I use String as an identifier for file resource cache lookups.

I’m mildly aware that string is not just a struct, but also references a storage container; like array.

Are there performance hits for transferring Array or String between different actors?

For example does the entire storage get copied every time the array and string are sent? Or does the copy-on-write functionality still hold up across actors?

I’m trying to decide if creating a lookup table actor would be worth my time and energy to reduce copying, if copying would occur.

In CoW containers, isKnownUniquelyReferenced is used under the hood to check an atomic reference count on modification universally, including concurrent access, which is why it is atomic in the first place.

Since you can only directly send immutable values, you can access the same immutable string from any isolation.

var mutable = "foo"
let immutable = mutable // no copy, ref count +1

Task {
  // stringConsumer(mutable) <- error, can't send `mutable`
  stringConsumer(immutable) // no copies here

  var newMutable = immutable // no copy, ref count +1

  // creates a copy, `isKnownUniquelyReferenced` is `false` here
  newMutable.append("baz")
}

// creates a copy, `isKnownUniquelyReferenced` is `false` here
mutable.append("bar")
5 Likes

Correct! It's thread-safe by virtue of the reference counting being synchronized already. It's just doing a single check, and doesn't also modify the ref count, so there's no TOC/TOU concerns.

By the time you're able to call isKnownUniquelyReferenced(x), you've got a strong reference to x. At that point, it can check if the reference count is >1, indicating that some other reference must exist.

Here's an enlightening thread on this matter: isKnownUniquelyReferenced thread safety

3 Likes

Excellent. I needed that extra confidence before getting locked into passing lots of immutable strings around

Thanks guys!

1 Like