Skip to content

Commit

Permalink
macOS: Pass JackMachSemaphore send right via mach_msg IPC (jackaudio#788
Browse files Browse the repository at this point in the history
)

* 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 jackaudio#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 jackaudio#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 jackaudio#784.

* Improve error handling

* Add myself to Authors
  • Loading branch information
p--b authored Aug 14, 2021
1 parent c742b43 commit 1ab3445
Show file tree
Hide file tree
Showing 7 changed files with 414 additions and 113 deletions.
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Nedko Arnaudov
Olaf Hering
Olivier Humbert
Paul Davis
Peter Bridgman
Peter L Jones
Pieter Palmers
Ricardo Crudo
Expand Down
1 change: 1 addition & 0 deletions common/wscript
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]
Expand Down
29 changes: 25 additions & 4 deletions macosx/JackMachSemaphore.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -38,21 +42,38 @@ 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:

void BuildName(const char* name, const char* server_name, char* res, int size);

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();
Expand Down
Loading

0 comments on commit 1ab3445

Please sign in to comment.