diff --git a/CMakeLists.txt b/CMakeLists.txt index aa44af93f..041d17e07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,7 +116,7 @@ set( CMAKE_PREFIX_PATH #set( CMAKE_INTERPROCEDURAL_OPTIMIZATION_DEBUG OFF ) # Set the required C++ standard -set( CMAKE_CXX_STANDARD 14 ) +set( CMAKE_CXX_STANDARD 17 ) set( CMAKE_CXX_STANDARD_REQUIRED ON ) # Use ccache if available diff --git a/include/audacity/EffectAutomationParameters.h b/include/audacity/EffectAutomationParameters.h index 0577af21c..0a99b22f0 100644 --- a/include/audacity/EffectAutomationParameters.h +++ b/include/audacity/EffectAutomationParameters.h @@ -107,7 +107,8 @@ public: if (Read(key, &str)) { struct lconv *info = localeconv(); - wxString dec = info ? wxString::FromUTF8(info->decimal_point) : wxT("."); + wxString dec = + info ? wxString::FromUTF8(info->decimal_point) : wxString("."); str.Replace(wxT(","), dec); str.Replace(wxT("."), dec); diff --git a/libraries/lib-utility/MemoryX.cpp b/libraries/lib-utility/MemoryX.cpp index a3411bd7a..51888e6ab 100644 --- a/libraries/lib-utility/MemoryX.cpp +++ b/libraries/lib-utility/MemoryX.cpp @@ -1,5 +1,58 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + MemoryX.cpp + + Paul Licameli + + **********************************************************************/ + #include "MemoryX.h" // Make the symbol table non-empty UTILITY_API void lib_utility_dummy_symbol() {} + +#ifdef __APPLE__ + +constexpr auto sizeof_align_val = sizeof(std::align_val_t); + +void *NonInterferingBase::operator new(std::size_t count, std::align_val_t al) +{ + using namespace std; + // Get an allocation with sufficient extra space to remember the alignment + // (And to do that, adjust the alignment to be not less than the alignment of + // an alignment value!). + // Also increase the allocation by one entire alignment. + al = max( al, static_cast( alignof(align_val_t) ) ); + const auto al_as_size = static_cast(al); + auto ptr = static_cast( + ::operator new( count + sizeof_align_val + al_as_size ) ); + + // Adjust the pointer to a properly aligned one, with a space just before it + // to remember the adjustment + ptr += sizeof_align_val; + auto integer = reinterpret_cast(ptr); + const auto partial = integer % al_as_size; + auto adjustment = partial ? al_as_size - partial : 0; + integer += adjustment; + ptr = reinterpret_cast(integer); + + // Remember the adjustment + *(reinterpret_cast(ptr) - 1) = adjustment; + + return ptr; +} + +void NonInterferingBase::operator delete(void *ptr, std::align_val_t al) +{ + // Find the adjustment + auto adjustment = *(reinterpret_cast(ptr) - 1); + // Apply the adjustment + auto p = reinterpret_cast(ptr) - adjustment - sizeof_align_val; + // Call through to default operator + ::operator delete(p); +} + +#endif diff --git a/libraries/lib-utility/MemoryX.h b/libraries/lib-utility/MemoryX.h index da299a1bc..3fbc27c25 100644 --- a/libraries/lib-utility/MemoryX.h +++ b/libraries/lib-utility/MemoryX.h @@ -4,6 +4,7 @@ // C++ standard header with a few extensions #include #include +#include // align_val_t and hardware_destructive_interference_size #include // Needed for free. #ifndef safenew #define safenew new @@ -579,6 +580,36 @@ OutContainer transform_container( InContainer &inContainer, Function &&fn ) inContainer.begin(), inContainer.end(), fn ); } +//! Non-template helper for class template NonInterfering +/*! + If a structure contains any members with large alignment, this base class may also allow it to work in + macOS builds under current limitations of the C++17 standard implementation. + */ +struct UTILITY_API alignas( +#ifdef __WIN32__ + std::hardware_destructive_interference_size +#else + // That constant isn't defined for the other builds yet + 64 /* ? */ +#endif +) +NonInterferingBase { +#ifdef __APPLE__ + static void *operator new(std::size_t count, std::align_val_t al); + static void operator delete(void *ptr, std::align_val_t al); +#endif +}; + +/*! Given a structure type T, derive a structure with sufficient padding so that there is not false sharing of + cache lines between successive elements of an array of those structures. + */ +template< typename T > struct NonInterfering + : NonInterferingBase // Inherit operators; use empty base class optimization + , T +{ + using T::T; +}; + // These macros are used widely, so declared here. #define QUANTIZED_TIME(time, rate) (floor(((double)(time) * (rate)) + 0.5) / (rate)) // dB - linear amplitude conversions diff --git a/src/ActiveProjects.cpp b/src/ActiveProjects.cpp index 7e2967461..0f1a59d34 100644 --- a/src/ActiveProjects.cpp +++ b/src/ActiveProjects.cpp @@ -93,6 +93,6 @@ wxString ActiveProjects::Find(const FilePath &path) gPrefs->SetPath(configPath); - return found ? key : wxT(""); + return found ? key : wxString{}; } diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index 307d2f5b3..13754fb93 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -541,7 +541,7 @@ constexpr size_t TimeQueueGrainSize = 2000; #endif -struct AudioIoCallback::ScrubState +struct AudioIoCallback::ScrubState : NonInterferingBase { ScrubState(double t0, double rate, diff --git a/src/AudioIO.h b/src/AudioIO.h index 5651ac2c2..1a48912f7 100644 --- a/src/AudioIO.h +++ b/src/AudioIO.h @@ -132,12 +132,11 @@ int audacityAudioCallback( // Data must be default-constructible and either copyable or movable. template class MessageBuffer { - struct alignas( 64 - //std::hardware_destructive_interference_size // C++17 - ) UpdateSlot { + struct UpdateSlot { std::atomic mBusy{ false }; Data mData; - } mSlots[2]; + }; + NonInterfering mSlots[2]; std::atomic mLastWrittenSlot{ 0 }; @@ -566,10 +565,11 @@ protected: // These need not be updated atomically, because we rely on the atomics // in the playback ring buffers to supply the synchronization. Still, // align them to avoid false sharing. - alignas(64) struct Cursor { + struct Cursor { size_t mIndex {}; size_t mRemainder {}; - } mHead, mTail; + }; + NonInterfering mHead, mTail; void Producer( const PlaybackSchedule &schedule, double rate, double scrubSpeed, diff --git a/src/AudioIOBase.h b/src/AudioIOBase.h index 80734f52b..93d4173e1 100644 --- a/src/AudioIOBase.h +++ b/src/AudioIOBase.h @@ -16,10 +16,10 @@ Paul Licameli split from AudioIO.h #include #include -#include #include #include #include // member variable +#include "MemoryX.h" struct PaDeviceInfo; typedef void PaStream; @@ -117,6 +117,7 @@ struct AudioIOStartStreamOptions ///\brief A singleton object supporting queries of the state of any active /// audio streams, and audio device capabilities class AUDACITY_DLL_API AudioIOBase /* not final */ + : public NonInterferingBase { public: static AudioIOBase *Get(); diff --git a/src/LyricsWindow.cpp b/src/LyricsWindow.cpp index 4df43a270..68143da09 100644 --- a/src/LyricsWindow.cpp +++ b/src/LyricsWindow.cpp @@ -49,7 +49,7 @@ END_EVENT_TABLE() const wxSize gSize = wxSize(LYRICS_DEFAULT_WIDTH, LYRICS_DEFAULT_HEIGHT); LyricsWindow::LyricsWindow(AudacityProject *parent) - : wxFrame( &GetProjectFrame( *parent ), -1, {}, + : wxFrame( &GetProjectFrame( *parent ), -1, wxString{}, wxPoint(100, 300), gSize, //v Bug in wxFRAME_FLOAT_ON_PARENT: // If both the project frame and LyricsWindow are minimized and you restore LyricsWindow, diff --git a/src/MixerBoard.cpp b/src/MixerBoard.cpp index a0ade17d0..995242a39 100644 --- a/src/MixerBoard.cpp +++ b/src/MixerBoard.cpp @@ -1408,7 +1408,7 @@ const wxSize kDefaultSize = wxSize(MIXER_BOARD_MIN_WIDTH, MIXER_BOARD_MIN_HEIGHT); MixerBoardFrame::MixerBoardFrame(AudacityProject* parent) -: wxFrame( &GetProjectFrame( *parent ), -1, {}, +: wxFrame( &GetProjectFrame( *parent ), -1, wxString{}, wxDefaultPosition, kDefaultSize, wxDEFAULT_FRAME_STYLE | wxFRAME_FLOAT_ON_PARENT) , mProject(parent) diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp index e24fa6954..f9f0c254b 100644 --- a/src/PluginManager.cpp +++ b/src/PluginManager.cpp @@ -3032,7 +3032,7 @@ RegistryPath PluginManager::SettingsPath(const PluginID & ID, bool shared) wxT("_") + plug.GetVendor() + wxT("_") + - (shared ? wxT("") : plug.GetSymbol().Internal()); + (shared ? wxString{} : plug.GetSymbol().Internal()); return SETROOT + ConvertID(id) + diff --git a/src/ProjectFileIO.cpp b/src/ProjectFileIO.cpp index 07c15ee10..f9fec8a38 100644 --- a/src/ProjectFileIO.cpp +++ b/src/ProjectFileIO.cpp @@ -1140,7 +1140,7 @@ FilePath ProjectFileIO::SafetyFileName(const FilePath &src) int nn = 1; auto numberString = [](int num) -> wxString { - return num == 1 ? "" : wxString::Format(".%d", num); + return num == 1 ? wxString{} : wxString::Format(".%d", num); }; auto suffixes = AuxiliaryFileSuffixes(); diff --git a/src/RingBuffer.h b/src/RingBuffer.h index e92a0fc1e..f8e056879 100644 --- a/src/RingBuffer.h +++ b/src/RingBuffer.h @@ -14,7 +14,7 @@ #include "SampleFormat.h" #include -class RingBuffer { +class RingBuffer final : public NonInterferingBase { public: RingBuffer(sampleFormat format, size_t size); ~RingBuffer(); @@ -43,16 +43,8 @@ class RingBuffer { size_t Filled( size_t start, size_t end ); size_t Free( size_t start, size_t end ); - enum : size_t { CacheLine = 64 }; - /* - // We will do this in C++17 instead: - static constexpr size_t CacheLine = - std::hardware_destructive_interference_size; - */ - // Align the two atomics to avoid false sharing - alignas(CacheLine) std::atomic mStart { 0 }; - alignas(CacheLine) std::atomic mEnd{ 0 }; + NonInterfering< std::atomic > mStart { 0 }, mEnd{ 0 }; const size_t mBufferSize; diff --git a/src/commands/CommandTargets.cpp b/src/commands/CommandTargets.cpp index d296a8181..c84fbdef1 100644 --- a/src/commands/CommandTargets.cpp +++ b/src/commands/CommandTargets.cpp @@ -66,7 +66,7 @@ void CommandMessageTarget::EndStruct(){ void CommandMessageTarget::AddItem(const wxString &value, const wxString &name){ wxString Padding; Padding.Pad( mCounts.size() *2 -2); - Padding = (( value.length() < 15 ) || (mCounts.back()<=0)) ? "" : wxString("\n") + Padding; + Padding = (( value.length() < 15 ) || (mCounts.back()<=0)) ? wxString{} : wxString("\n") + Padding; if( name.empty() ) Update( wxString::Format( "%s%s\"%s\"", (mCounts.back()>0)?", ":"", Padding, Escaped(value))); else diff --git a/src/effects/nyquist/Nyquist.cpp b/src/effects/nyquist/Nyquist.cpp index 6e1ecf05b..345080b22 100644 --- a/src/effects/nyquist/Nyquist.cpp +++ b/src/effects/nyquist/Nyquist.cpp @@ -242,7 +242,7 @@ TranslatableString NyquistEffect::GetDescription() wxString NyquistEffect::ManualPage() { return mIsPrompt - ? wxT("Nyquist_Prompt") + ? wxString("Nyquist_Prompt") : mManPage; } @@ -2183,7 +2183,7 @@ bool NyquistEffect::Parse( ctrl.label = tokens[4]; // valStr may or may not be a quoted string - ctrl.valStr = len > 5 ? tokens[5] : wxT(""); + ctrl.valStr = len > 5 ? tokens[5] : wxString{}; ctrl.val = GetCtrlValue(ctrl.valStr); if (ctrl.valStr.length() > 0 && (ctrl.valStr[0] == wxT('(') || diff --git a/src/toolbars/DeviceToolBar.cpp b/src/toolbars/DeviceToolBar.cpp index 451bfd2fb..5ec590ec4 100644 --- a/src/toolbars/DeviceToolBar.cpp +++ b/src/toolbars/DeviceToolBar.cpp @@ -244,7 +244,7 @@ void DeviceToolBar::UpdatePrefs() int hostSelectionIndex = mHost->GetSelection(); wxString oldHost = hostSelectionIndex >= 0 ? mHost->GetString(hostSelectionIndex) : - wxT(""); + wxString{}; auto hostName = AudioIOHost.Read(); // if the prefs host name doesn't match the one displayed, it changed diff --git a/src/widgets/FileConfig.cpp b/src/widgets/FileConfig.cpp index 29f983667..19b8f1da2 100644 --- a/src/widgets/FileConfig.cpp +++ b/src/widgets/FileConfig.cpp @@ -23,6 +23,8 @@ #include "FileConfig.h" +#include // for ENOENT + #if !defined(F_OK) #define F_OK 0x00 #endif diff --git a/src/widgets/FileHistory.cpp b/src/widgets/FileHistory.cpp index 553f441c1..cd78e1c8b 100644 --- a/src/widgets/FileHistory.cpp +++ b/src/widgets/FileHistory.cpp @@ -115,7 +115,7 @@ void FileHistory::Load(wxConfigBase & config, const wxString & group) { mHistory.clear(); mGroup = group.empty() - ? wxT("RecentFiles") + ? wxString{ "RecentFiles" } : group; config.SetPath(mGroup); diff --git a/src/widgets/numformatter.cpp b/src/widgets/numformatter.cpp index 74832fc2e..56182839b 100644 --- a/src/widgets/numformatter.cpp +++ b/src/widgets/numformatter.cpp @@ -57,7 +57,7 @@ wxChar NumberFormatter::GetDecimalSeparator() { #if wxUSE_INTL struct lconv *info = localeconv(); - wxString s = info ? wxString::FromUTF8(info->decimal_point) : wxT("."); + wxString s = info ? wxString::FromUTF8(info->decimal_point) : wxString("."); if (s.empty()) { // We really must have something for decimal separator, so fall @@ -75,7 +75,7 @@ bool NumberFormatter::GetThousandsSeparatorIfUsed(wxChar *sep) { #if wxUSE_INTL struct lconv *info = localeconv(); - wxString s = info ? wxString::FromUTF8(info->thousands_sep) : wxT(""); + wxString s = info ? wxString::FromUTF8(info->thousands_sep) : wxString{}; if (s.empty()) {