TaskExecutor and Swift 6 question

I have the following TaskExecutor code in Swift 6 and is getting the following error:

//Error Passing closure as a sending parameter risks causing data races between main actor-isolated code and concurrent execution of the closure.

May I know what is the best way to approach this?

This is the default code generated by Xcode when creating a Vision Pro App using Metal as the Immersive Renderer.

Renderer

@MainActor
static func startRenderLoop(_ layerRenderer: LayerRenderer, appModel: AppModel) {
    Task(executorPreference: RendererTaskExecutor.shared) { //Error
        
        let renderer = Renderer(layerRenderer, appModel: appModel)
        await renderer.startARSession()
        await renderer.renderLoop()
         
    }
}

final class RendererTaskExecutor: TaskExecutor { private let queue = DispatchQueue(label: "RenderThreadQueue", qos: .userInteractive)

func enqueue(_ job: UnownedJob) {
    queue.async {
      job.runSynchronously(on: self.asUnownedSerialExecutor())
    }
}

func asUnownedSerialExecutor() -> UnownedTaskExecutor {
    return UnownedTaskExecutor(ordinary: self)
}

static let shared: RendererTaskExecutor = RendererTaskExecutor()

}

I took your code and got it compiling in Xcode 16.1. See below.

If I change either LayerRenderer or AppModel from a struct to a class it produces the error you’re seeing. That makes sense to me, because you’re trying to pass non-sendable values between isolation domains.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

import Dispatch

struct LayerRenderer { }
struct AppModel { }

class Renderer {

    init(_ layerRenderer: LayerRenderer, appModel: AppModel) {
    }

    @MainActor
    static func startRenderLoop(_ layerRenderer: LayerRenderer, appModel: AppModel) {
        Task(executorPreference: RendererTaskExecutor.shared) {  //Error

            let renderer = Renderer(layerRenderer, appModel: appModel)
            await renderer.startARSession()
            await renderer.renderLoop()
        }
    }

    func startARSession() async {}
    func renderLoop() async {}
}

final class RendererTaskExecutor: TaskExecutor {

    private let queue = DispatchQueue(label: "RenderThreadQueue", qos: .userInteractive)
    func enqueue(_ job: UnownedJob) {
        queue.async {
            job.runSynchronously(on: self.asUnownedSerialExecutor())
        }
    }

    func asUnownedSerialExecutor() -> UnownedTaskExecutor {
        return UnownedTaskExecutor(ordinary: self)
    }

    static let shared: RendererTaskExecutor = RendererTaskExecutor()
}
TaskExecutor and Swift 6 question
 
 
Q