Screen Reader for macOS implemented with Swift Concurrency and Distributed Actors

Repurposing my questions that weren't a good fit for the group lab to see how that goes :)

I've been building a ScreenReader in Swift leveraging Structured Concurrency, actors, and recently distributed actors over XPC.

https://codeberg.org/SpeakUp

I have a number of questions I could ask (and would love to ask) but would start with asking for thoughts on my RunLoopExecutor project

https://codeberg.org/SpeakUp/RunLoopExecutor/

All of the macOS Accessibility APIs are C/CoreFoundation/CFRunLoop based and I wanted to build something where actors would feel idiomatic for an experienced Swift developer but under the hood we're making sure that we're not contending with ourselves with all the IPC we're doing to get Accessibility data.

I think so far it's been pretty successful as seen in the Controller types for the ScreenReader project: https://codeberg.org/SpeakUp/ScreenReader

I'm currently using pretty naive pool implementations, one that is fixed width and one that is dynamic with a maximum width. Would love to hear different approaches to growing and shrinking the thread pool and handling things like marking a given executor as likely in a bad state (usually meaning the app it's talking to over AX API is blocking it's main thread)

In the AccessibilityElement project https://codeberg.org/SpeakUp/AccessibilityElement for my HIServices Observer implementation I'm exposed to a race condition where axobserver doesn't flush it's notification queue on remove. I'm relying on pthread_specific currently to introduce thread local storage to work around this but it's quite clunky.

In an ideal world the HIServices API would emit a done event to allow cleanup but so far that hasn't happened.

I'll leave it there for now and do new posts with more requests for feedback if this one is well received.

There aren’t any question marks in your post, so it’s not clear what your actual question is. I suspect you’re looking for feedback on your RunLoopExecutor package.

If so, yeah, I’ve been down that path myself a few times and your code looks very like my first version. The biggest issue with it is that it relies on Thread being thread safe, which is not the case (much to my surprise). Hence your need for that nonisolated(unsafe).

In my quest to get around that I eventually created something much more complex. Sadly, it’s still requires unsafe stuff, but I bounded that to one thing that I consider to be safe (passing CFRunLoop references between threads) and two things that I consider to be Safe Enough™ (calling CFRunLoopSourceSignal and CFRunLoopWakeUp from another thread).

Needless to say I’m not really happy with that result, and that’s one of the reasons I’ve not shared my code more widely.

Share and Enjoy

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

Screen Reader for macOS implemented with Swift Concurrency and Distributed Actors
 
 
Q