macOS: Pass JackMachSemaphore send right via mach_msg IPC (#788)

* macOS: Pass JackMachSemaphore send right via mach_msg IPC

Previously, JackMachSemaphore would communicate the send right for the
semaphore object from the server to a client via a named service
registered via `bootstrap_register`. However, to do this, it would
register the semaphore's port as the service port directly.

In theory this ought to be fine, however in practice, macOS `launchd`,
which provides the `bootstrap_register` interface, does not correctly
detect when such a port becomes dead, and incorrectly believes that the
service that it provides is forever alive, even past the end of the
`jackd` process' (and therefore the semaphore's) existence. This seems
to be *specific* to semaphore ports, as `launchd` is expecting a
standard IPC port, owned by the task, not the kernel. This prevents
`jackd` from later registering another service with the same name, as
`launchd` rejects the registration as conflicting with an active service.

To get around this, `jackd` previously added a counter to the end of the
named service registrations, allowing old services to remain in the
system until the end of the session. To prevent things getting out of
hand, this was capped at 98 service registrations for a given semaphore
name. This led to #784, in which running a client for the 99th time
resulted in the semaphore creation failing and the client failing to
connect.

As `launchd` outlives multiple runs of `jackd`, this situation persisted
across restarts of `jackd`, requiring a restart of the user's session
(i.e. a reboot) to fix.

An initial attempt at fixing this (see #785) tried passing the port
rights directly via shared memory, however mach is too clever for us and
foils that plan by having port names be looked up in a per-task table
(sensible when you think about it).

In this commit, we use mach IPC messages to transfer the send right for
the semaphore from the server to the client. By registering a standard
IPC port with the bootstrap server, the service registrations are
correctly torn down when the ports are destroyed.

It works something like this:

* Server creates IPC port and registers it globally via `bootstrap_register`
* Server listens on IPC port for messages
* Client looks up IPC port via `bootstrap_look_up`
* Client sends it a message
* Server replies with a message containing a send right to the
semaphore's port
* Client is then free to use the semaphore port as before.

This resolves #784.

* Improve error handling

* Add myself to Authors
This commit is contained in:
Peter Bridgman 2021-08-14 16:24:41 +01:00 committed by falkTX
parent 4ba2806038
commit 7e599badec
7 changed files with 425 additions and 124 deletions

View File

@ -72,6 +72,7 @@ Nedko Arnaudov
Olaf Hering
Olivier Humbert
Paul Davis
Peter Bridgman
Peter L Jones
Pieter Palmers
Ricardo Crudo

View File

@ -117,6 +117,7 @@ def build(bld):
'../posix/JackPosixMutex.cpp',
'../macosx/JackMachThread.mm',
'../macosx/JackMachSemaphore.mm',
'../macosx/JackMachSemaphoreServer.mm',
'../posix/JackSocket.cpp',
'../macosx/JackMachTime.c',
]

View File

@ -22,6 +22,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "JackCompilerDeps.h"
#include "JackSynchro.h"
#include "JackMachThread.h"
#include "JackMachSemaphoreServer.h"
#include <mach/mach.h>
#include <servers/bootstrap.h>
#include <mach/semaphore.h>
@ -38,13 +42,24 @@ class SERVER_EXPORT JackMachSemaphore : public detail::JackSynchro
private:
/*! \brief A mach send right to the mach semaphore, or MACH_PORT_NULL if not yet Allocate()d
* (server) or Connect()ed (client). */
semaphore_t fSemaphore;
/*! \brief The bootstrap port for this task, or MACH_PORT_NULL if not yet obtained. */
mach_port_t fBootPort;
int fSharedMem;
char* fSharedName;
/*! \brief The IPC port used to pass the semaphore port from the server to the client, and
* for the client to request that this occurs. MACH_PORT_NULL if not yet created (server) or
* looked up (client). */
mach_port_t fServicePort;
bool recursiveBootstrapRegister(int counter);
/*! \brief On the server, if allocated, a runnable semaphore server which listens for IPC
* messages and replies with a send right for a semaphore port. */
JackMachSemaphoreServer* fSemServer;
/*! \brief On the server, if allocated, a thread that runs \ref fSemServer. */
JackMachThread* fThreadSemServer;
protected:
@ -52,7 +67,13 @@ class SERVER_EXPORT JackMachSemaphore : public detail::JackSynchro
public:
JackMachSemaphore():JackSynchro(), fSemaphore(0), fBootPort(0), fSharedMem(0), fSharedName(NULL)
JackMachSemaphore():
JackSynchro(),
fSemaphore(MACH_PORT_NULL),
fBootPort(MACH_PORT_NULL),
fServicePort(MACH_PORT_NULL),
fSemServer(NULL),
fThreadSemServer(NULL)
{}
bool Signal();

View File

@ -18,12 +18,18 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "JackMachSemaphore.h"
#include "JackMachUtils.h"
#include "JackConstants.h"
#include "JackTools.h"
#include "JackError.h"
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <mach/message.h>
#define jack_mach_error(kern_result, message) \
jack_mach_error_uncurried("JackMachSemaphore", kern_result, message)
#define jack_mach_bootstrap_err(kern_result, message, name) \
jack_mach_bootstrap_err_uncurried("JackMachSemaphore", kern_result, message, name)
namespace Jack
{
@ -42,7 +48,7 @@ void JackMachSemaphore::BuildName(const char* client_name, const char* server_na
bool JackMachSemaphore::Signal()
{
if (!fSemaphore) {
if (fSemaphore == MACH_PORT_NULL) {
jack_error("JackMachSemaphore::Signal name = %s already deallocated!!", fName);
return false;
}
@ -60,7 +66,7 @@ bool JackMachSemaphore::Signal()
bool JackMachSemaphore::SignalAll()
{
if (!fSemaphore) {
if (fSemaphore == MACH_PORT_NULL) {
jack_error("JackMachSemaphore::SignalAll name = %s already deallocated!!", fName);
return false;
}
@ -79,7 +85,7 @@ bool JackMachSemaphore::SignalAll()
bool JackMachSemaphore::Wait()
{
if (!fSemaphore) {
if (fSemaphore == MACH_PORT_NULL) {
jack_error("JackMachSemaphore::Wait name = %s already deallocated!!", fName);
return false;
}
@ -93,7 +99,7 @@ bool JackMachSemaphore::Wait()
bool JackMachSemaphore::TimedWait(long usec)
{
if (!fSemaphore) {
if (fSemaphore == MACH_PORT_NULL) {
jack_error("JackMachSemaphore::TimedWait name = %s already deallocated!!", fName);
return false;
}
@ -109,132 +115,199 @@ bool JackMachSemaphore::TimedWait(long usec)
return (res == KERN_SUCCESS);
}
bool JackMachSemaphore::recursiveBootstrapRegister(int counter)
/*! \brief Server side: create semaphore and publish IPC primitives to make it accessible.
*
* This method;
* - Allocates a mach semaphore
* - Allocates a new mach IPC port and obtains a send right for it
* - Publishes IPC port send right to the bootstrap server
* - Starts a new JackMachSemaphoreServer thread, which listens for messages on the IPC port and
* replies with a send right to the mach semaphore.
*
* \returns false if any of the above steps fails, or true otherwise.
*/
bool JackMachSemaphore::Allocate(const char* client_name, const char* server_name, int value)
{
if (counter == 99)
return false;
kern_return_t res;
if ((res = bootstrap_register(fBootPort, fSharedName, fSemaphore)) != KERN_SUCCESS) {
switch (res) {
case BOOTSTRAP_SUCCESS :
break;
case BOOTSTRAP_NOT_PRIVILEGED :
case BOOTSTRAP_NAME_IN_USE :
case BOOTSTRAP_UNKNOWN_SERVICE :
case BOOTSTRAP_SERVICE_ACTIVE :
// try again with next suffix
snprintf(fSharedName, sizeof(fName), "%s-%d", fName, ++counter);
return recursiveBootstrapRegister(counter);
break;
default :
jack_log("bootstrap_register() err = %i:%s", res, bootstrap_strerror(res));
break;
}
jack_error("Allocate: can't check in mach semaphore name = %s err = %i:%s", fName, res, bootstrap_strerror(res));
if (fSemaphore != MACH_PORT_NULL) {
jack_error("JackMachSemaphore::Allocate: Semaphore already allocated; called twice? [%s]", fName);
return false;
}
return true;
}
BuildName(client_name, server_name, fName, sizeof(fName));
// Server side : publish the semaphore in the global namespace
bool JackMachSemaphore::Allocate(const char* name, const char* server_name, int value)
{
BuildName(name, server_name, fName, sizeof(fName));
mach_port_t task = mach_task_self();
kern_return_t res;
if (fBootPort == 0) {
if (fBootPort == MACH_PORT_NULL) {
if ((res = task_get_bootstrap_port(task, &fBootPort)) != KERN_SUCCESS) {
jack_error("Allocate: Can't find bootstrap mach port err = %s", mach_error_string(res));
jack_mach_error(res, "can't find bootstrap mach port");
return false;
}
}
if ((fSharedMem = shm_open(fName, O_CREAT | O_RDWR, 0777)) < 0) {
jack_error("Allocate: can't check in mach shared name = %s err = %s", fName, strerror(errno));
return false;
}
struct stat st;
if (fstat(fSharedMem, &st) != -1 && st.st_size == 0) {
if (ftruncate(fSharedMem, SYNC_MAX_NAME_SIZE+1) != 0) {
jack_error("Allocate: can't set shared memory size in mach shared name = %s err = %s", fName, strerror(errno));
return false;
}
}
char* const sharedName = (char*)mmap(NULL, SYNC_MAX_NAME_SIZE+1, PROT_READ|PROT_WRITE, MAP_SHARED, fSharedMem, 0);
if (sharedName == NULL || sharedName == MAP_FAILED) {
jack_error("Allocate: can't check in mach shared name = %s err = %s", fName, strerror(errno));
close(fSharedMem);
fSharedMem = -1;
shm_unlink(fName);
return false;
}
fSharedName = sharedName;
strcpy(fSharedName, fName);
if ((res = semaphore_create(task, &fSemaphore, SYNC_POLICY_FIFO, value)) != KERN_SUCCESS) {
jack_error("Allocate: can create semaphore err = %i:%s", res, mach_error_string(res));
jack_mach_error(res, "failed to create semaphore");
return false;
}
jack_log("JackMachSemaphore::Allocate name = %s", fName);
return recursiveBootstrapRegister(1);
if ((res = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &fServicePort)) != KERN_SUCCESS) {
jack_mach_error(res, "failed to allocate IPC port");
// Cleanup created semaphore
this->Destroy();
return false;
}
if ((res = mach_port_insert_right(mach_task_self(), fServicePort, fServicePort, MACH_MSG_TYPE_MAKE_SEND)) != KERN_SUCCESS) {
jack_mach_error(res, "failed to obtain send right for IPC port");
// Cleanup created semaphore & mach port
this->Destroy();
return false;
}
if ((res = bootstrap_register(fBootPort, fName, fServicePort)) != KERN_SUCCESS) {
jack_mach_bootstrap_err(res, "can't register IPC port with bootstrap server", fName);
// Cleanup created semaphore & mach port
this->Destroy();
return false;
}
fSemServer = new JackMachSemaphoreServer(fSemaphore, fServicePort, fName);
fThreadSemServer = new JackMachThread(fSemServer);
if (fThreadSemServer->Start() < 0) {
jack_error("JackMachSemaphore::Allocate: failed to start semaphore IPC server thread [%s]", fName);
// Cleanup created semaphore, mach port (incl. service registration), and server
this->Destroy();
return false;
}
jack_log("JackMachSemaphore::Allocate: OK, name = %s", fName);
return true;
}
// Client side : get the published semaphore from server
bool JackMachSemaphore::ConnectInput(const char* name, const char* server_name)
/*! \brief Client side: Obtain semaphore from server via published IPC port.
*
* This method;
* - Looks up the service port for the jackd semaphore server for this client by name
* - Sends a message to that server asking for a semaphore port send right
* - Receives a semaphore send right in return and stores it locally
*
* \returns False if any of the above steps fails, or true otherwise.
*/
bool JackMachSemaphore::ConnectInput(const char* client_name, const char* server_name)
{
BuildName(name, server_name, fName, sizeof(fName));
BuildName(client_name, server_name, fName, sizeof(fName));
mach_port_t task = mach_task_self();
kern_return_t res;
// Temporary...
if (fSharedName) {
jack_log("Already connected name = %s", name);
if (fSemaphore != MACH_PORT_NULL) {
jack_log("JackMachSemaphore::Connect: Already connected name = %s", fName);
return true;
}
if (fBootPort == 0) {
if ((res = task_get_bootstrap_port(mach_task_self(), &fBootPort)) != KERN_SUCCESS) {
jack_error("Connect: can't find bootstrap port err = %s", mach_error_string(res));
if (fBootPort == MACH_PORT_NULL) {
if ((res = task_get_bootstrap_port(task, &fBootPort)) != KERN_SUCCESS) {
jack_mach_error(res, "can't find bootstrap port");
return false;
}
}
if ((fSharedMem = shm_open(fName, O_RDWR, 0)) < 0) {
jack_error("Connect: can't connect mach shared name = %s err = %s", fName, strerror(errno));
if ((res = bootstrap_look_up(fBootPort, fName, &fServicePort)) != KERN_SUCCESS) {
jack_mach_bootstrap_err(res, "can't find IPC service port to request semaphore", fName);
return false;
}
char* const sharedName = (char*)mmap(NULL, SYNC_MAX_NAME_SIZE+1, PROT_READ|PROT_WRITE, MAP_SHARED, fSharedMem, 0);
mach_port_t semaphore_req_port;
if ((res = mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &semaphore_req_port)) != KERN_SUCCESS) {
jack_mach_error(res, "failed to allocate request port");
if ((res = mach_port_deallocate(task, fServicePort)) != KERN_SUCCESS) {
jack_mach_error(res, "failed to deallocate IPC service port during cleanup");
} else {
fServicePort = MACH_PORT_NULL;
}
if (sharedName == NULL || sharedName == MAP_FAILED) {
jack_error("Connect: can't connect mach shared name = %s err = %s", fName, strerror(errno));
close(fSharedMem);
fSharedMem = -1;
return false;
}
if ((res = bootstrap_look_up(fBootPort, sharedName, &fSemaphore)) != KERN_SUCCESS) {
jack_error("Connect: can't find mach semaphore name = %s, sname = %s, err = %s", fName, sharedName, bootstrap_strerror(res));
close(fSharedMem);
fSharedMem = -1;
// Prepare a message buffer on the stack. We'll use it for both sending and receiving a message.
struct {
mach_msg_header_t hdr;
mach_msg_trailer_t trailer;
} msg;
/*
* Configure the message to consume the destination port we give it (_MOVE_SEND), and to
* transmute the local port receive right we give it into a send_once right at the destination.
* The server will use that send_once right to reply to us.
*/
msg.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
msg.hdr.msgh_local_port = semaphore_req_port;
msg.hdr.msgh_remote_port = fServicePort;
mach_msg_return_t send_err = mach_msg(
&msg.hdr,
MACH_SEND_MSG,
sizeof(msg.hdr), // no trailer on send
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (send_err != MACH_MSG_SUCCESS) {
jack_mach_error(send_err, "failed to send semaphore port request IPC");
if ((res = mach_port_deallocate(task, fServicePort)) != KERN_SUCCESS) {
jack_mach_error(res, "failed to deallocate IPC service port during cleanup");
} else {
fServicePort = MACH_PORT_NULL;
}
if ((res = mach_port_destroy(task, semaphore_req_port)) != KERN_SUCCESS) {
jack_mach_error(res, "failed to destroy IPC request port during cleanup");
}
return false;
} else {
fServicePort = MACH_PORT_NULL; // We moved it into the message and away to the destination
}
fSharedName = sharedName;
mach_msg_return_t recv_err = mach_msg(
&msg.hdr,
MACH_RCV_MSG,
0,
sizeof(msg),
semaphore_req_port,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL
);
jack_log("JackMachSemaphore::Connect name = %s ", fName);
return true;
/* Don't leak ports: irrespective of if we succeeded to read or not, destroy the port we created
* to send/receive the request as we have no further use for it either way. */
if ((res = mach_port_destroy(task, semaphore_req_port)) != KERN_SUCCESS) {
jack_mach_error(res, "failed to destroy semaphore_req_port");
// This isn't good, but doesn't actually stop the semaphore from working... don't bail
}
if (recv_err != MACH_MSG_SUCCESS) {
jack_mach_error(recv_err, "failed to receive semaphore port");
return false;
} else {
fSemaphore = msg.hdr.msgh_remote_port;
jack_log("JackMachSemaphore::Connect: OK, name = %s ", fName);
return true;
}
}
bool JackMachSemaphore::Connect(const char* name, const char* server_name)
@ -249,49 +322,75 @@ bool JackMachSemaphore::ConnectOutput(const char* name, const char* server_name)
bool JackMachSemaphore::Disconnect()
{
if (fSemaphore > 0) {
jack_log("JackMachSemaphore::Disconnect name = %s", fName);
fSemaphore = 0;
}
if (!fSharedName) {
if (fSemaphore == MACH_PORT_NULL) {
return true;
}
munmap(fSharedName, SYNC_MAX_NAME_SIZE+1);
fSharedName = NULL;
mach_port_t task = mach_task_self();
kern_return_t res;
close(fSharedMem);
fSharedMem = -1;
return true;
jack_log("JackMachSemaphore::Disconnect name = %s", fName);
if (fServicePort != MACH_PORT_NULL) {
// If we're still holding onto a service port send right for some reason, deallocate it
if ((res = mach_port_deallocate(task, fServicePort)) != KERN_SUCCESS) {
jack_mach_error(res, "failed to deallocate stray service port");
// Continue cleanup even if this fails; don't bail
} else {
fServicePort = MACH_PORT_NULL;
}
}
if ((res = mach_port_deallocate(task, fSemaphore)) != KERN_SUCCESS) {
jack_mach_error(res, "failed to deallocate semaphore port");
return false;
} else {
fSemaphore = MACH_PORT_NULL;
return true;
}
}
// Server side : destroy the JackGlobals
void JackMachSemaphore::Destroy()
{
kern_return_t res;
mach_port_t task = mach_task_self();
if (fSemaphore > 0) {
jack_log("JackMachSemaphore::Destroy name = %s", fName);
if ((res = semaphore_destroy(mach_task_self(), fSemaphore)) != KERN_SUCCESS) {
jack_error("JackMachSemaphore::Destroy can't destroy semaphore err = %s", mach_error_string(res));
}
fSemaphore = 0;
} else {
jack_error("JackMachSemaphore::Destroy semaphore < 0");
}
if (!fSharedName) {
if (fSemaphore == MACH_PORT_NULL) {
jack_error("JackMachSemaphore::Destroy semaphore is MACH_PORT_NULL; already destroyed?");
return;
}
munmap(fSharedName, SYNC_MAX_NAME_SIZE+1);
fSharedName = NULL;
if (fThreadSemServer) {
if (fThreadSemServer->Kill() < 0) {
jack_error("JackMachSemaphore::Destroy failed to kill semaphore server thread...");
// Oh dear. How sad. Never mind.
}
close(fSharedMem);
fSharedMem = -1;
JackMachThread* thread = fThreadSemServer;
fThreadSemServer = NULL;
delete thread;
}
shm_unlink(fName);
if (fSemServer) {
JackMachSemaphoreServer* server = fSemServer;
fSemServer = NULL;
delete server;
}
if ((res = mach_port_destroy(task, fServicePort)) != KERN_SUCCESS) {
jack_mach_error(res, "failed to destroy IPC port");
} else {
fServicePort = MACH_PORT_NULL;
}
if ((res = semaphore_destroy(mach_task_self(), fSemaphore)) != KERN_SUCCESS) {
jack_mach_error(res, "failed to destroy semaphore");
} else {
fSemaphore = MACH_PORT_NULL;
}
jack_log("JackMachSemaphore::Destroy: OK, name = %s", fName);
}
} // end of namespace

View File

@ -0,0 +1,56 @@
/*
Copyright (C) 2021 Peter Bridgman
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __JackMachSemaphoreServer__
#define __JackMachSemaphoreServer__
#include "JackCompilerDeps.h"
#include "JackMachThread.h"
#include <mach/mach.h>
#include <mach/semaphore.h>
namespace Jack
{
/*! \brief A runnable thread which listens for IPC messages and replies with a semaphore send right. */
class SERVER_EXPORT JackMachSemaphoreServer : public JackRunnableInterface
{
private:
/*! \brief The semaphore send right that will be dispatched to clients. */
semaphore_t fSemaphore;
/*! \brief The port on which we will listen for IPC messages. */
mach_port_t fServerReceive;
/*! \brief A pointer to a null-terminated string buffer that will be read to obtain the
* server name for reporting purposes. Not managed at all by this type. */
char* fName;
public:
JackMachSemaphoreServer(semaphore_t semaphore, mach_port_t server_recv, char* name):
fSemaphore(semaphore), fServerReceive(server_recv), fName(name)
{}
bool Execute() override;
};
} // end of namespace
#endif

View File

@ -0,0 +1,87 @@
/*
Copyright (C) 2021 Peter Bridgman
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "JackMachSemaphoreServer.h"
#include "JackMachUtils.h"
#include "JackConstants.h"
#include "JackTools.h"
#include "JackError.h"
#include <mach/message.h>
#define jack_mach_error(kern_result, message) \
jack_mach_error_uncurried("JackMachSemaphoreServer", kern_result, message)
namespace Jack
{
bool JackMachSemaphoreServer::Execute() {
jack_log("JackMachSemaphoreServer::Execute: %s", fName);
/* Setup a message struct in our local stack frame which we can receive messages into and send
* messages from. */
struct {
mach_msg_header_t hdr;
mach_msg_trailer_t trailer;
} msg;
// Block until we receive a message on the fServerReceive port.
mach_msg_return_t recv_err = mach_msg(
&msg.hdr,
MACH_RCV_MSG,
0,
sizeof(msg),
fServerReceive,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL
);
if (recv_err != MACH_MSG_SUCCESS) {
jack_mach_error(recv_err, "receive error");
return true; // Continue processing more connections
}
/* We're going to reuse the message struct that we received the message into to send a reply.
* Setup the semaphore send port that we want to give to the client as the local port... */
msg.hdr.msgh_local_port = fSemaphore;
/*
* ... to be returned by copy (_COPY_SEND), to a destination that is _SEND_ONCE that we no
* longer require. That destination will have been set by the client as their local_port, so
* will now already be the remote_port in the message we received (nifty, eh?).
*/
msg.hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, MACH_MSG_TYPE_COPY_SEND);
mach_msg_return_t send_err = mach_msg(
&msg.hdr,
MACH_SEND_MSG,
sizeof(msg.hdr), // no trailer on send
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (send_err != MACH_MSG_SUCCESS) {
jack_mach_error(send_err, "send error");
}
return true;
}
} // end of namespace

36
macosx/JackMachUtils.h Normal file
View File

@ -0,0 +1,36 @@
/*
Copyright (C) 2021 Peter Bridgman
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __JackMachUtils__
#define __JackMachUtils__
#define jack_mach_error_uncurried(type_name, kern_return, message) \
jack_error(type_name "::%s: " message " - %i:%s", \
__FUNCTION__, \
kern_return, \
mach_error_string(kern_return))
#define jack_mach_bootstrap_err_uncurried(type_name, kern_return, message, service_name) \
jack_error(type_name "::%s: " message " [%s] - %i:%s", \
__FUNCTION__, \
service_name, \
kern_return, \
bootstrap_strerror(kern_return))
#endif