// This file is part of Background Music. // // Background Music is free software: you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation, either version 2 of the // License, or (at your option) any later version. // // Background Music is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Background Music. If not, see . // // main.m // BGMXPCHelper // // Copyright © 2016 Kyle Neideck // // BGMXPCHelper passes XPC messages between BGMDriver and BGMApp. So far it's only used for synchronization while starting IO. // // BGMApp and BGMDriver usually communicate by changing device properties and listening for notifications about those changes, which the // HAL sends. We use XPC, or plan to use it, for the few cases that notifications don't suit. For example, shared memory regions can be // sent via XPC. So we might be able to share a ring buffer between BGMDriver and BGMApp and avoid the latency and CPU overhead of // sending the audio data through an input stream. XPC should also let us safely send messages from real-time functions. // // Notifications would have probably worked fine for synchronizing BGMDriver and BGMApp while starting IO. But the Core Audio headers // don't specify which threads notifications are sent/received on, what (if anything) they can block on, or what can be blocked by their // handler callbacks. So using XPC instead makes it a bit easier to reason about the process and avoid potential deadlocks. (That said, // I ended up adding a huge amount of error handling and boilerplate code for XPC, so this way is probably more confusing overall.) // // CFMessagePort would be much simpler than XPC to use, but as far as I could tell there's no way to create a remote CFMessagePort for // a Mach port without looking the Mach port up using the bootstrap service. And because BGMXPCHelper and BGMApp have to use different // bootstrap namespaces, BGMXPCHelper wouldn't be able to see services vended by BGMApp. So BGMXPCHelper would be able to receive // messages from BGMApp and reply to them, but not send them. // // BGMXPCHelper has to run as a launchd daemon so the Mach service it vends will use the global bootstrap namespace. See Table 2 in // . This is because the coreaudiod process, where BGMDriver runs, // uses the global bootstrap namespace, which means it can only see services in that namespace. In practical terms, this just means we // have to run BGMXPCHelper by creating a launchd.plist file for it in /Library/LaunchDaemons and loading it with a command like // launchctl bootstrap system /Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist // // Local Includes #import "BGMXPCProtocols.h" #import "BGMXPCListenerDelegate.h" // PublicUtility Includes #undef CoreAudio_ThreadStampMessages #define CoreAudio_ThreadStampMessages 0 // Requires C++ #include "CADebugMacros.h" // System Includes #import #pragma clang assume_nonnull begin int main(int argc, const char *argv[]) { #pragma unused (argc, argv) DebugMsg("BGMXPCHelper::main: Service starting up"); // Set up the one NSXPCListener for this service. It will handle all incoming connections. This checks our service in with // the bootstrap service. NSXPCListener *listener = [[NSXPCListener alloc] initWithMachServiceName:kBGMXPCHelperMachServiceName]; BGMXPCListenerDelegate* delegate = [BGMXPCListenerDelegate new]; listener.delegate = delegate; // Start receiving requests. [listener resume]; [[NSRunLoop currentRunLoop] run]; return 0; } #pragma clang assume_nonnull end