From 112b6bdf39be3afb0a3af9e2bbdd5bb94525466f Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 7 Mar 2017 15:16:50 +0100 Subject: [PATCH] Use Linux futex as JackSynchro Includes optimizations for internal clients, but not enabled yet. --- common/wscript | 2 +- linux/JackLinuxFutex.cpp | 243 ++++++++++++++++++++++++++++++++++++ linux/JackLinuxFutex.h | 85 +++++++++++++ linux/JackPlatformPlug_os.h | 4 +- 4 files changed, 331 insertions(+), 3 deletions(-) create mode 100644 linux/JackLinuxFutex.cpp create mode 100644 linux/JackLinuxFutex.h diff --git a/common/wscript b/common/wscript index b64d275f..2cff1b5d 100644 --- a/common/wscript +++ b/common/wscript @@ -83,10 +83,10 @@ def build(bld): 'JackDebugClient.cpp', 'timestamps.c', '../posix/JackPosixThread.cpp', - '../posix/JackPosixSemaphore.cpp', '../posix/JackPosixProcessSync.cpp', '../posix/JackPosixMutex.cpp', '../posix/JackSocket.cpp', + '../linux/JackLinuxFutex.cpp', '../linux/JackLinuxTime.c', ] includes = ['../linux', '../posix'] + includes diff --git a/linux/JackLinuxFutex.cpp b/linux/JackLinuxFutex.cpp new file mode 100644 index 00000000..6e897bc8 --- /dev/null +++ b/linux/JackLinuxFutex.cpp @@ -0,0 +1,243 @@ +/* +Copyright (C) 2004-2008 Grame +Copyright (C) 2016 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 "JackLinuxFutex.h" +#include "JackTools.h" +#include "JackConstants.h" +#include "JackError.h" +#include +#include +#include +#include +#include + +namespace Jack +{ + +void JackLinuxFutex::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); + if (getenv("JACK_PROMISCUOUS_SERVER")) { + snprintf(res, size, "jack_sem.%s_%s", server_name, ext_client_name); + } else { + snprintf(res, size, "jack_sem.%d_%s_%s", JackTools::GetUID(), server_name, ext_client_name); + } +} + +bool JackLinuxFutex::Signal() +{ + if (!fFutex) { + jack_error("JackLinuxFutex::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; + } + + ::syscall(__NR_futex, fFutex, fFutex->internal ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1, NULL, NULL, 0); + return true; +} + +bool JackLinuxFutex::SignalAll() +{ + return Signal(); +} + +bool JackLinuxFutex::Wait() +{ + if (!fFutex) { + jack_error("JackLinuxFutex::Wait name = %s already deallocated!!", fName); + return false; + } + + if (fFutex->needsChange) + { + fFutex->needsChange = false; + fFutex->internal = !fFutex->internal; + } + + for (;;) + { + if (__sync_bool_compare_and_swap(&fFutex->futex, 1, 0)) + return true; + + if (::syscall(__NR_futex, fFutex, fFutex->internal ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, 0, NULL, NULL, 0) != 0 && errno != EWOULDBLOCK) + return false; + } +} + +bool JackLinuxFutex::TimedWait(long usec) +{ + if (!fFutex) { + jack_error("JackLinuxFutex::TimedWait name = %s already deallocated!!", fName); + return false; + } + + if (fFutex->needsChange) + { + fFutex->needsChange = false; + fFutex->internal = !fFutex->internal; + } + + const uint secs = usec / 1000000; + const int nsecs = (usec % 1000000) * 1000; + + const timespec timeout = { static_cast(secs), nsecs }; + + for (;;) + { + if (__sync_bool_compare_and_swap(&fFutex->futex, 1, 0)) + return true; + + if (::syscall(__NR_futex, fFutex, fFutex->internal ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, 0, &timeout, NULL, 0) != 0 && errno != EWOULDBLOCK) + return false; + } +} + +// Server side : publish the futex in the global namespace +bool JackLinuxFutex::Allocate(const char* name, const char* server_name, int value, bool internal) +{ + BuildName(name, server_name, fName, sizeof(fName)); + jack_log("JackLinuxFutex::Allocate name = %s val = %ld", fName, value); + + 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; + } + + ftruncate(fSharedMem, sizeof(FutexData)); + + if ((fFutex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, fSharedMem, 0)) == NULL) { + 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; + } + + fPrivate = internal; + + fFutex->futex = value; + fFutex->internal = internal; + fFutex->wasInternal = internal; + fFutex->needsChange = false; + fFutex->externalCount = 0; + return true; +} + +// Client side : get the published futex from server +bool JackLinuxFutex::Connect(const char* name, const char* server_name) +{ + BuildName(name, server_name, fName, sizeof(fName)); + jack_log("JackLinuxFutex::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; + } + + if ((fFutex = (FutexData*)mmap(NULL, sizeof(FutexData), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, fSharedMem, 0)) == NULL) { + jack_error("Connect: can't connect named futex name = %s err = %s", fName, strerror(errno)); + close(fSharedMem); + fSharedMem = -1; + return false; + } + + if (! fPrivate && fFutex->wasInternal) + { + const char* externalSync = getenv("JACK_INTERNAL_CLIENT_SYNC"); + + if (externalSync != NULL && strstr(fName, externalSync) != NULL && ++fFutex->externalCount == 1) + { + jack_error("Note: client %s running as external client temporarily", fName); + fFutex->needsChange = true; + } + } + + return true; +} + +bool JackLinuxFutex::ConnectInput(const char* name, const char* server_name) +{ + return Connect(name, server_name); +} + +bool JackLinuxFutex::ConnectOutput(const char* name, const char* server_name) +{ + return Connect(name, server_name); +} + +bool JackLinuxFutex::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 JackLinuxFutex::Destroy() +{ + if (!fFutex) { + return; + } + + munmap(fFutex, sizeof(FutexData)); + fFutex = NULL; + + close(fSharedMem); + fSharedMem = -1; + + shm_unlink(fName); +} + +} // end of namespace + diff --git a/linux/JackLinuxFutex.h b/linux/JackLinuxFutex.h new file mode 100644 index 00000000..5c8a7006 --- /dev/null +++ b/linux/JackLinuxFutex.h @@ -0,0 +1,85 @@ +/* +Copyright (C) 2004-2008 Grame +Copyright (C) 2016 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 __JackLinuxFutex__ +#define __JackLinuxFutex__ + +#include "JackSynchro.h" +#include "JackCompilerDeps.h" +#include + +namespace Jack +{ + +/*! +\brief Inter process synchronization using Linux futex. + + Based on the JackPosixSemaphore class. + Adapted to work with linux futex to be as light as possible and also work in multiple architectures. + + Adds a new 'MakePrivate' function that makes the sync happen in the local process only, + making it even faster for internal clients. +*/ + +class SERVER_EXPORT JackLinuxFutex : 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; + + protected: + + void BuildName(const char* name, const char* server_name, char* res, int size); + + public: + + JackLinuxFutex():JackSynchro(), fSharedMem(-1), fFutex(NULL), fPrivate(false) + {} + + 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/linux/JackPlatformPlug_os.h b/linux/JackPlatformPlug_os.h index b480f453..60c9a585 100644 --- a/linux/JackPlatformPlug_os.h +++ b/linux/JackPlatformPlug_os.h @@ -54,8 +54,8 @@ namespace Jack { typedef JackPosixThread JackThread; } namespace Jack { typedef JackFifo JackSynchro; } */ -#include "JackPosixSemaphore.h" -namespace Jack { typedef JackPosixSemaphore JackSynchro; } +#include "JackLinuxFutex.h" +namespace Jack { typedef JackLinuxFutex JackSynchro; } /* __JackPlatformChannelTransaction__ */ /*