BackgroundMusic/BGMApp/BGMXPCHelper/main.m
Kyle Neideck 33b6b17115 Add an XPC helper. Sync BGMApp and BGMDriver with it while starting IO.
The BGMApp project now builds an XPC service bundle called BGMXPCHelper,
which vends a Mach service that BGMApp and BGMDriver can use to
communicate. This will hopefully be useful for some of the tasks HAL
notifications aren't suited to.

In this commit, BGMDriver uses the XPC helper when starting IO, to wait
until BGMApp is ready for playthrough. BGMApp can only start playthrough
when the output hardware is ready for IO. BGMDriver can now tell the HAL
when we're ready for IO, which means we don't have to keep the output
hardware running all the time (or drop frames or increase latency).

The end result is that playthrough doesn't waste CPU time while idle any
more. This also means that now playthrough won't prevent the system from
sleeping when idle.
2016-03-31 11:42:24 +11:00

81 lines
4 KiB
Objective-C

// 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 <http://www.gnu.org/licenses/>.
//
// 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
// <https://developer.apple.com/library/mac/technotes/tn2083/_index.html>. 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 <Foundation/Foundation.h>
#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