I am embedding a dynamic programming language inside my Swift application. Said programming language's runtime runs in a background thread and my app normally communicates with it asynchronously via pipes. Now, I have a need for the programs written in that language to be able to occasionally execute Swift callbacks. So, I have the following mechanism for installing these callbacks:
public func installCallback(id: CallbackID, proc: @escaping (InputPort) -> Void) {
callbacks[id] = proc
let addr = Int(bitPattern: unsafeBitCast(callbackHandler, to: Optional<UnsafeRawPointer>.self)!)
registerWithOtherLang(id, Int64(addr))
}
fileprivate typealias CallbackID = UInt64
fileprivate var callbacks = [CallbackID: (InputPort) -> Void]()
fileprivate let callbackHandler: @convention(c) (CallbackID, Int, UnsafePointer<CChar>) -> Void = ...
Where the callbackHandler
procedure looks up a callback by its id when called from the other side and applies it to the data it receives (handler args 2 and 3 are a size and data, respectively). The registerWithOtherLang
procedure instructs the other language's runtime to keep track of a foreign procedure (from its perspective) with the address of the callback handler.
This works reliably, but I'm new to Swift and I'm concerned I don't know what I don't know. Specifically, I wonder if callbackHandler
's address could change at runtime or if there are any other problems with this approach.