From d2a9dc56093e7b7e8890566b533d2e38c460da19 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 24 Mar 2024 16:32:20 +0100 Subject: [PATCH] Use macos internal API for semaphore instead of bootstrap stuff Signed-off-by: falkTX --- common/wscript | 3 +- linux/JackLinuxFutex.cpp | 10 ++ macosx/JackMachFutex.cpp | 306 +++++++++++++++++++++++++++++++++++ macosx/JackMachFutex.h | 82 ++++++++++ macosx/JackPlatformPlug_os.h | 10 +- 5 files changed, 404 insertions(+), 7 deletions(-) create mode 100644 macosx/JackMachFutex.cpp create mode 100644 macosx/JackMachFutex.h diff --git a/common/wscript b/common/wscript index 178d4c1de..e483f4051 100644 --- a/common/wscript +++ b/common/wscript @@ -135,9 +135,8 @@ def build(bld): '../posix/JackPosixProcessSync.cpp', '../posix/JackPosixThread.cpp', '../posix/JackPosixMutex.cpp', + '../macosx/JackMachFutex.cpp', '../macosx/JackMachThread.mm', - '../macosx/JackMachSemaphore.mm', - '../macosx/JackMachSemaphoreServer.mm', '../posix/JackSocket.cpp', '../macosx/JackMachTime.c', ] diff --git a/linux/JackLinuxFutex.cpp b/linux/JackLinuxFutex.cpp index 29b13901c..71c2151ff 100644 --- a/linux/JackLinuxFutex.cpp +++ b/linux/JackLinuxFutex.cpp @@ -165,6 +165,11 @@ bool JackLinuxFutex::Allocate(const char* name, const char* server_name, int val FutexData* futex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, fSharedMem, 0); + // try without MAP_LOCKED + if (futex == NULL || futex == MAP_FAILED) { + futex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED, fSharedMem, 0); + } + if (futex == NULL || futex == MAP_FAILED) { jack_error("Allocate: can't check in named futex name = %s err = %s", fName, strerror(errno)); close(fSharedMem); @@ -203,6 +208,11 @@ bool JackLinuxFutex::Connect(const char* name, const char* server_name) FutexData* futex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, fSharedMem, 0); + // try without MAP_LOCKED + if (futex == NULL || futex == MAP_FAILED) { + futex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED, fSharedMem, 0); + } + if (futex == NULL || futex == MAP_FAILED) { jack_error("Connect: can't connect named futex name = %s err = %s", fName, strerror(errno)); close(fSharedMem); diff --git a/macosx/JackMachFutex.cpp b/macosx/JackMachFutex.cpp new file mode 100644 index 000000000..95bd8fccc --- /dev/null +++ b/macosx/JackMachFutex.cpp @@ -0,0 +1,306 @@ +/* +Copyright (C) 2004-2008 Grame +Copyright (C) 2016-2024 Filipe Coelho + +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 "JackMachFutex.h" +#include "JackTools.h" +#include "JackConstants.h" +#include "JackError.h" +#include "promiscuous.h" +#include +#include +#include +#include + +#define UL_COMPARE_AND_WAIT 1 +#define UL_COMPARE_AND_WAIT_SHARED 3 +#define ULF_WAKE_ALL 0x00000100 +#define ULF_NO_ERRNO 0x01000000 + +extern "C" { +int __ulock_wait(uint32_t operation, void* addr, uint64_t value, uint32_t timeout_us); +int __ulock_wake(uint32_t operation, void* addr, uint64_t value); +} + +namespace Jack +{ + +JackMachFutex::JackMachFutex() : JackSynchro(), fSharedMem(-1), fFutex(NULL), fPrivate(false) +{ + const char* promiscuous = getenv("JACK_PROMISCUOUS_SERVER"); + fPromiscuous = (promiscuous != NULL); + fPromiscuousGid = jack_group2gid(promiscuous); +} + +void JackMachFutex::BuildName(const char* client_name, const char* server_name, char* res, int size) +{ + char ext_client_name[SYNC_MAX_NAME_SIZE + 1]; + JackTools::RewriteName(client_name, ext_client_name); + + // make the name as small as possible, as macos has issues with long semaphore names + if (strcmp(server_name, "default") == 0) + server_name = ""; + else if (strcmp(server_name, "mod-desktop") == 0) + server_name = "mdsk."; + + if (fPromiscuous) { + snprintf(res, std::min(size, 32), "js.%s%s", server_name, ext_client_name); + } else { + snprintf(res, std::min(size, 32), "js%d.%s%s", JackTools::GetUID(), server_name, ext_client_name); + } +} + +bool JackMachFutex::Signal() +{ + if (!fFutex) { + jack_error("JackMachFutex::Signal name = %s already deallocated!!", fName); + return false; + } + + if (fFlush) { + return true; + } + + if (! __sync_bool_compare_and_swap(&fFutex->futex, 0, 1)) + { + // already unlocked, do not wake futex + if (! fFutex->internal) return true; + } + + const uint32_t operation = ULF_NO_ERRNO | (fFutex->internal ? UL_COMPARE_AND_WAIT : UL_COMPARE_AND_WAIT_SHARED); + __ulock_wake(operation, fFutex, 0); + return true; +} + +bool JackMachFutex::SignalAll() +{ + if (!fFutex) { + jack_error("JackMachFutex::SignalAll name = %s already deallocated!!", fName); + return false; + } + + if (fFlush) { + return true; + } + + const uint32_t operation = ULF_NO_ERRNO | ULF_WAKE_ALL | (fFutex->internal ? UL_COMPARE_AND_WAIT : UL_COMPARE_AND_WAIT_SHARED); + __ulock_wake(operation, fFutex, 0); + return true; +} + +bool JackMachFutex::Wait() +{ + if (!fFutex) { + jack_error("JackMachFutex::Wait name = %s already deallocated!!", fName); + return false; + } + + if (fFutex->needsChange) + { + fFutex->needsChange = false; + fFutex->internal = !fFutex->internal; + } + + const uint32_t operation = fFutex->internal ? UL_COMPARE_AND_WAIT : UL_COMPARE_AND_WAIT_SHARED; + + for (;;) + { + if (__sync_bool_compare_and_swap(&fFutex->futex, 1, 0)) + return true; + + if (__ulock_wait(operation, fFutex, 0, UINT32_MAX) != 0) + if (errno != EAGAIN && errno != EINTR) + return false; + } +} + +bool JackMachFutex::TimedWait(long usec) +{ + if (usec == LONG_MAX) + return Wait(); + + if (!fFutex) { + jack_error("JackMachFutex::TimedWait name = %s already deallocated!!", fName); + return false; + } + + if (fFutex->needsChange) + { + fFutex->needsChange = false; + fFutex->internal = !fFutex->internal; + } + + const uint32_t operation = fFutex->internal ? UL_COMPARE_AND_WAIT : UL_COMPARE_AND_WAIT_SHARED; + + for (;;) + { + if (__sync_bool_compare_and_swap(&fFutex->futex, 1, 0)) + return true; + + if (__ulock_wait(operation, fFutex, 0, usec) != 0) + if (errno != EAGAIN && errno != EINTR) + return false; + } +} + +// Server side : publish the futex in the global namespace +bool JackMachFutex::Allocate(const char* name, const char* server_name, int value, bool internal) +{ + BuildName(name, server_name, fName, sizeof(fName)); + jack_log("JackMachFutex::Allocate name = %s val = %ld", fName, value); + + // FIXME + shm_unlink(fName); + + if ((fSharedMem = shm_open(fName, O_CREAT | O_RDWR, 0777)) < 0) { + jack_error("Allocate: can't check in named futex name = %s err = %s", fName, strerror(errno)); + return false; + } + + if (ftruncate(fSharedMem, sizeof(FutexData)) != 0) { + jack_error("Allocate: can't set shared memory size in named futex name = %s err = %s", fName, strerror(errno)); + return false; + } + + if (fPromiscuous && (jack_promiscuous_perms(fSharedMem, fName, fPromiscuousGid) < 0)) { + close(fSharedMem); + fSharedMem = -1; + shm_unlink(fName); + return false; + } + + FutexData* futex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED, fSharedMem, 0); + + if (futex == NULL || futex == MAP_FAILED) { + jack_error("Allocate: can't check in named futex name = %s err = %s", fName, strerror(errno)); + close(fSharedMem); + fSharedMem = -1; + shm_unlink(fName); + return false; + } + + mlock(futex, sizeof(FutexData)); + + fPrivate = internal; + + futex->futex = value; + futex->internal = internal; + futex->wasInternal = internal; + futex->needsChange = false; + futex->externalCount = 0; + fFutex = futex; + return true; +} + +// Client side : get the published futex from server +bool JackMachFutex::Connect(const char* name, const char* server_name) +{ + BuildName(name, server_name, fName, sizeof(fName)); + jack_log("JackMachFutex::Connect name = %s", fName); + + // Temporary... + if (fFutex) { + jack_log("Already connected name = %s", name); + return true; + } + + if ((fSharedMem = shm_open(fName, O_RDWR, 0)) < 0) { + jack_error("Connect: can't connect named futex name = %s err = %s", fName, strerror(errno)); + return false; + } + + FutexData* futex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED, fSharedMem, 0); + + if (futex == NULL || futex == MAP_FAILED) { + jack_error("Connect: can't connect named futex name = %s err = %s", fName, strerror(errno)); + close(fSharedMem); + fSharedMem = -1; + return false; + } + + mlock(futex, sizeof(FutexData)); + + if (! fPrivate && futex->wasInternal) + { + const char* externalSync = getenv("JACK_INTERNAL_CLIENT_SYNC"); + + if (externalSync != NULL && strstr(fName, externalSync) != NULL && ++futex->externalCount == 1) + { + jack_error("Note: client %s running as external client temporarily", fName); + futex->needsChange = true; + } + } + + fFutex = futex; + return true; +} + +bool JackMachFutex::ConnectInput(const char* name, const char* server_name) +{ + return Connect(name, server_name); +} + +bool JackMachFutex::ConnectOutput(const char* name, const char* server_name) +{ + return Connect(name, server_name); +} + +bool JackMachFutex::Disconnect() +{ + if (!fFutex) { + return true; + } + + if (! fPrivate && fFutex->wasInternal) + { + const char* externalSync = getenv("JACK_INTERNAL_CLIENT_SYNC"); + + if (externalSync != NULL && strstr(fName, externalSync) != NULL && --fFutex->externalCount == 0) + { + jack_error("Note: client %s now running as internal client again", fName); + fFutex->needsChange = true; + } + } + + munmap(fFutex, sizeof(FutexData)); + fFutex = NULL; + + close(fSharedMem); + fSharedMem = -1; + return true; +} + +// Server side : destroy the futex +void JackMachFutex::Destroy() +{ + if (!fFutex) { + return; + } + + munmap(fFutex, sizeof(FutexData)); + fFutex = NULL; + + close(fSharedMem); + fSharedMem = -1; + + shm_unlink(fName); +} + +} // end of namespace + diff --git a/macosx/JackMachFutex.h b/macosx/JackMachFutex.h new file mode 100644 index 000000000..865c1d65e --- /dev/null +++ b/macosx/JackMachFutex.h @@ -0,0 +1,82 @@ +/* +Copyright (C) 2004-2008 Grame +Copyright (C) 2016-2024 Filipe Coelho + +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 __JackMachFutex__ +#define __JackMachFutex__ + +#include "JackSynchro.h" +#include "JackCompilerDeps.h" +#include + +namespace Jack +{ + +/*! +\brief Inter process synchronization using macOS futexes (through an internal macOS API). + + Heavily based on the JackLinuxFutex class.. +*/ + +class SERVER_EXPORT JackMachFutex : public detail::JackSynchro +{ + + private: + struct FutexData { + int futex; // futex, needs to be 1st member + bool internal; // current internal state + bool wasInternal; // initial internal state, only changes in allocate + bool needsChange; // change state on next wait call + int externalCount; // how many external clients have connected + }; + + int fSharedMem; + FutexData* fFutex; + bool fPrivate; + bool fPromiscuous; + int fPromiscuousGid; + + protected: + + void BuildName(const char* name, const char* server_name, char* res, int size); + + public: + + JackMachFutex(); + + bool Signal(); + bool SignalAll(); + bool Wait(); + bool TimedWait(long usec); + + bool Allocate(const char* name, const char* server_name, int value, bool internal = false); + bool Connect(const char* name, const char* server_name); + bool ConnectInput(const char* name, const char* server_name); + bool ConnectOutput(const char* name, const char* server_name); + bool Disconnect(); + void Destroy(); + + void MakePrivate(bool priv); +}; + +} // end of namespace + + +#endif + diff --git a/macosx/JackPlatformPlug_os.h b/macosx/JackPlatformPlug_os.h index 2ecde203f..85db84890 100644 --- a/macosx/JackPlatformPlug_os.h +++ b/macosx/JackPlatformPlug_os.h @@ -31,9 +31,9 @@ namespace Jack struct JackRequest; struct JackResult; - class JackPosixMutex; - class JackMachThread; - class JackMachSemaphore; + class JackPosixMutex; + class JackMachThread; + class JackMachFutex; class JackSocketServerChannel; class JackSocketClientChannel; @@ -52,8 +52,8 @@ namespace Jack { typedef JackPosixMutex JackMutex; } namespace Jack { typedef JackMachThread JackThread; } /* __JackPlatformSynchro__ client activation */ -#include "JackMachSemaphore.h" -namespace Jack { typedef JackMachSemaphore JackSynchro; } +#include "JackMachFutex.h" +namespace Jack { typedef JackMachFutex JackSynchro; } /* __JackPlatformProcessSync__ */ #include "JackPosixProcessSync.h"