diff --git a/AUTHORS.rst b/AUTHORS.rst index 7266fd003..7b7185ec0 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -72,6 +72,7 @@ Nedko Arnaudov Olaf Hering Olivier Humbert Paul Davis +Peter Bridgman Peter L Jones Pieter Palmers Ricardo Crudo diff --git a/common/wscript b/common/wscript index 114eb0a2b..24f024ec2 100644 --- a/common/wscript +++ b/common/wscript @@ -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', ] diff --git a/macosx/JackMachSemaphore.h b/macosx/JackMachSemaphore.h index 10406847a..20465a096 100644 --- a/macosx/JackMachSemaphore.h +++ b/macosx/JackMachSemaphore.h @@ -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 #include #include @@ -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; + + /*! \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; - bool recursiveBootstrapRegister(int counter); + /*! \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(); diff --git a/macosx/JackMachSemaphore.mm b/macosx/JackMachSemaphore.mm index 7b7f4e2e2..1905e04cb 100644 --- a/macosx/JackMachSemaphore.mm +++ b/macosx/JackMachSemaphore.mm @@ -18,12 +18,18 @@ */ #include "JackMachSemaphore.h" +#include "JackMachUtils.h" #include "JackConstants.h" #include "JackTools.h" #include "JackError.h" -#include -#include -#include + +#include + +#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 @@ 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::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::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::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 @@ 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)); + if ((res = semaphore_create(task, &fSemaphore, SYNC_POLICY_FIFO, value)) != KERN_SUCCESS) { + jack_mach_error(res, "failed to create semaphore"); 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; - } + 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; } - char* const sharedName = (char*)mmap(NULL, SYNC_MAX_NAME_SIZE+1, PROT_READ|PROT_WRITE, MAP_SHARED, fSharedMem, 0); + 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(); - 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 = 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(); - 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)); return false; } - jack_log("JackMachSemaphore::Allocate name = %s", fName); - return recursiveBootstrapRegister(1); + 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 + ); + + /* 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 + } - jack_log("JackMachSemaphore::Connect name = %s ", fName); - return true; + 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::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)); + if (fSemaphore == MACH_PORT_NULL) { + jack_error("JackMachSemaphore::Destroy semaphore is MACH_PORT_NULL; already destroyed?"); + return; + } + + if (fThreadSemServer) { + if (fThreadSemServer->Kill() < 0) { + jack_error("JackMachSemaphore::Destroy failed to kill semaphore server thread..."); + // Oh dear. How sad. Never mind. } - fSemaphore = 0; - } else { - jack_error("JackMachSemaphore::Destroy semaphore < 0"); + + JackMachThread* thread = fThreadSemServer; + fThreadSemServer = NULL; + delete thread; } - if (!fSharedName) { - return; + if (fSemServer) { + JackMachSemaphoreServer* server = fSemServer; + fSemServer = NULL; + delete server; } - munmap(fSharedName, SYNC_MAX_NAME_SIZE+1); - fSharedName = NULL; + if ((res = mach_port_destroy(task, fServicePort)) != KERN_SUCCESS) { + jack_mach_error(res, "failed to destroy IPC port"); + } else { + fServicePort = MACH_PORT_NULL; + } - close(fSharedMem); - fSharedMem = -1; + if ((res = semaphore_destroy(mach_task_self(), fSemaphore)) != KERN_SUCCESS) { + jack_mach_error(res, "failed to destroy semaphore"); + } else { + fSemaphore = MACH_PORT_NULL; + } - shm_unlink(fName); + jack_log("JackMachSemaphore::Destroy: OK, name = %s", fName); } } // end of namespace diff --git a/macosx/JackMachSemaphoreServer.h b/macosx/JackMachSemaphoreServer.h new file mode 100644 index 000000000..85f3522fb --- /dev/null +++ b/macosx/JackMachSemaphoreServer.h @@ -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 +#include + +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 diff --git a/macosx/JackMachSemaphoreServer.mm b/macosx/JackMachSemaphoreServer.mm new file mode 100644 index 000000000..21f159b06 --- /dev/null +++ b/macosx/JackMachSemaphoreServer.mm @@ -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 + +#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 diff --git a/macosx/JackMachUtils.h b/macosx/JackMachUtils.h new file mode 100644 index 000000000..b9cbf59ff --- /dev/null +++ b/macosx/JackMachUtils.h @@ -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