Does an actor protect static properties and functions?

I've created this experiment. while ActorCounter is often more consistent than ClassCounter it is often not correct. Are static properties protected or not by actors?

import Foundation

let max = 1_000

actor ActorCounter {
    static var count: Int = 0
    static func increment() async {
        count += 1
    }
}

class ClassCounter {
    static var count: Int = 0
    static func increment() {
        count += 1
    }
}

// Increment concurrently using a Task Group
await withTaskGroup(of: Void.self, body: { group in
    for _ in 0..<max {
        group.addTask {
            await ActorCounter.increment()
        }
    }
})

// Increment concurrently using Dispatch
DispatchQueue.concurrentPerform(iterations: max) { _ in
    ClassCounter.increment()
}

print("Concurrent Perform is done")

var count = 0
for _ in 0..<max {
    count += 1
}

// make sure the work is really completed
let nanoseconds = UInt64(0.25 * Double(NSEC_PER_SEC))
try await Task.sleep(nanoseconds: nanoseconds)

print("Actor:", ActorCounter.count)
print("Class:", ClassCounter.count)
print("Non-Async:", count)

1 Like

Static methods are not part of any actor instance; they're global state just like static properties on other types. I wouldn't expect any reliable difference between the ActorCounter or ClassCounter here. If you want an actor guard that isn't tied to any particular instance, you can define a global actor, like MainActor, and decorate the methods you want to be guarded by it with the attribute for the global actor.

2 Likes

Thanks @Joe_Groff

Instead of implementing a Global Actor, which requires Xcode 13, I have made it into a singleton since we still need to allow Xcode 12.4 to be used.

Using @MainActor on that shared property should work, right?

@MainActor
    static let shared = MyType()

@MainActor is also Xcode 13+. If you need to support Xcode 12 / Swift <= 5.4, you have conditionalize all of your concurrency usage behind a check.

Edit: It may look like @MainActor is available but that's a compiler bug that's fixed on main but not yet in a release. Narrowly avoided that one myself.

2 Likes

What I've read here says custom global actors were possible with Xcode 13. I will have to try this out with Xcode 12.4, but I believe we have used @MainActor before. We have not created a custom global actor yet.

@MainActor is a global actor.

It is a special case which was supported earlier than the custom global actors, if what I am reading is accurate.

@MainActor was available slightly before the ability to define your own global actors, but both were generally part of Xcode 13 / Swift 5.5.

Wait, nevermind. All these versions are very confusing. I am using Xcode 14 and we support back to Xcode 13.4.