diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index ba6aac4e..c5f76361 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -42,7 +42,7 @@ jobs: cd build_linux cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 .. # Partially build so we have all required .h and .c/.cpp files - make -j$(nproc) build_mimalloc build_rade build_codec2 + make -j$(nproc) build_mimalloc build_rade build_codec2 build_rade_integ - name: Run clang-tidy on codebase working-directory: ${{github.workspace}}/build_linux @@ -340,6 +340,22 @@ jobs: native-audio: USE_NATIVE_AUDIO=0 - sanitizer: WITH_UBSAN=1 native-audio: USE_NATIVE_AUDIO=0 + - os: ubuntu-22.04 + sanitizer: WITH_RTSAN=1 + - os: ubuntu-22.04 + sanitizer: WITH_TSAN=1 + - os: ubuntu-22.04 + sanitizer: WITH_ASAN=1 + - os: ubuntu-22.04 + sanitizer: WITH_UBSAN=1 + - os: ubuntu-22.04-arm + sanitizer: WITH_RTSAN=1 + - os: ubuntu-22.04-arm + sanitizer: WITH_TSAN=1 + - os: ubuntu-22.04-arm + sanitizer: WITH_ASAN=1 + - os: ubuntu-22.04-arm + sanitizer: WITH_UBSAN=1 runs-on: ${{ matrix.os }} needs: lint diff --git a/.github/workflows/cmake-macos.yml b/.github/workflows/cmake-macos.yml index c5ad0f74..3996cff4 100644 --- a/.github/workflows/cmake-macos.yml +++ b/.github/workflows/cmake-macos.yml @@ -37,7 +37,7 @@ jobs: cd build_osx CC=clang CXX=clang++ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 .. # Partially build so we have all required .h and .c/.cpp files - make -j$(nproc) build_mimalloc build_rade build_codec2 + make -j$(nproc) build_mimalloc build_rade build_codec2 build_rade_integ - name: Run clang-tidy on codebase working-directory: ${{github.workspace}}/build_osx diff --git a/CMakeLists.txt b/CMakeLists.txt index c9d41e6a..9b501e3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -370,6 +370,7 @@ message(STATUS "Threads library flags: ${CMAKE_THREAD_LIBS_INIT}") # Build RADE. # (TBD - allow override with own build, similar to Codec2) include(cmake/BuildRADE.cmake) +include(cmake/BuildRADEForIntegrations.cmake) # # Build codec2 diff --git a/appimage/make-appimage.sh b/appimage/make-appimage.sh index bacd26a7..83ad4054 100755 --- a/appimage/make-appimage.sh +++ b/appimage/make-appimage.sh @@ -6,14 +6,17 @@ if [[ "${TARGET}" == "all" ]]; then export APPNAME="FreeDV" export APPEXEC=../build_linux/src/freedv export APPRUN="AppRun.sh" + export RADE_SRC="rade_src" elif [[ "${TARGET}" == "freedv-flex" ]]; then export APPNAME="FreeDV-FlexRadio" export APPEXEC=../build_linux/src/integrations/flex/freedv-flex export APPRUN="AppRun-FlexRadio.sh" + export RADE_SRC="rade_integ_src" elif [[ "${TARGET}" == "freedv-ka9q" ]]; then export APPNAME="FreeDV-KA9Q" export APPEXEC=../build_linux/src/integrations/ka9q/freedv-ka9q export APPRUN="AppRun-KA9Q.sh" + export RADE_SRC="rade_integ_src" fi DESKTOP_FILE="$APPNAME.desktop" @@ -81,9 +84,9 @@ cd - echo "Copying rade_src..." # ls freedv-rade/freedv-gui/build_linux/rade_src/model # model05/ model17/ model18/ model19/ model19_check3/ model_bbfm_01/ -cp -r "$BUILDDIR/build_linux/rade_src" "$APPDIR/." +cp -r "$BUILDDIR/build_linux/$RADE_SRC" "$APPDIR/." cd "$APPDIR/usr/bin" -ln -s "../../rade_src/model19_check3" "model19_check3" +ln -s "../../$RADE_SRC/model19_check3" "model19_check3" cd - # Create the output diff --git a/cmake/BuildRADE.cmake b/cmake/BuildRADE.cmake index c356ad66..0250b01f 100644 --- a/cmake/BuildRADE.cmake +++ b/cmake/BuildRADE.cmake @@ -15,7 +15,7 @@ ExternalProject_Add(build_rade SOURCE_DIR rade_src BINARY_DIR rade_build GIT_REPOSITORY https://github.com/drowe67/radae.git - GIT_TAG ms-disable-python-gc + GIT_TAG main CMAKE_ARGS ${RADE_CMAKE_ARGS} #CMAKE_CACHE_ARGS -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=${CMAKE_OSX_DEPLOYMENT_TARGET} INSTALL_COMMAND "" diff --git a/cmake/BuildRADEForIntegrations.cmake b/cmake/BuildRADEForIntegrations.cmake new file mode 100644 index 00000000..83138943 --- /dev/null +++ b/cmake/BuildRADEForIntegrations.cmake @@ -0,0 +1,71 @@ +if(CMAKE_CROSSCOMPILING) + set(RADE_CMAKE_ARGS ${RADE_CMAKE_ARGS} -DPython3_ROOT_DIR=${Python3_ROOT_DIR} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}) +elseif(APPLE) + set(RADE_CMAKE_ARGS ${RADE_CMAKE_ARGS} -DPython3_ROOT_DIR=${Python3_ROOT_DIR}) +endif() + +if(BUILD_OSX_UNIVERSAL) + set(RADE_CMAKE_ARGS ${RADE_CMAKE_ARGS} -DBUILD_OSX_UNIVERSAL=1) +endif(BUILD_OSX_UNIVERSAL) + +set(RADE_CMAKE_ARGS ${RADE_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DOPUS_URL=https://github.com/xiph/opus/archive/940d4e5af64351ca8ba8390df3f555484c567fbb.zip) + +include(ExternalProject) +ExternalProject_Add(build_rade_integ + SOURCE_DIR rade_integ_src + BINARY_DIR rade_integ_build + GIT_REPOSITORY https://github.com/drowe67/radae.git + GIT_TAG ms-disable-python-gc + CMAKE_ARGS ${RADE_CMAKE_ARGS} + #CMAKE_CACHE_ARGS -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=${CMAKE_OSX_DEPLOYMENT_TARGET} + INSTALL_COMMAND "" + EXCLUDE_FROM_ALL TRUE +) + +ExternalProject_Get_Property(build_rade_integ BINARY_DIR) +ExternalProject_Get_Property(build_rade_integ SOURCE_DIR) +add_library(rade_integ SHARED IMPORTED) +add_dependencies(rade_integ build_rade_integ) +include_directories(${SOURCE_DIR}/src) + +set_target_properties(rade_integ PROPERTIES + IMPORTED_LOCATION "${BINARY_DIR}/src/librade${CMAKE_SHARED_LIBRARY_SUFFIX}" + IMPORTED_IMPLIB "${BINARY_DIR}/src/librade${CMAKE_IMPORT_LIBRARY_SUFFIX}" +) +list(APPEND FREEDV_PACKAGE_SEARCH_PATHS ${BINARY_DIR}/src) +set(rade_integ_BINARY_DIR ${BINARY_DIR}) + +add_library(opus_integ STATIC IMPORTED) +add_dependencies(opus_integ build_rade_integ) +set(FARGAN_INTEG_ARM_CONFIG_H_FILE "${BINARY_DIR}/build_opus_arm-prefix/src/build_opus_arm/config.h") +set(FARGAN_INTEG_X86_CONFIG_H_FILE "${BINARY_DIR}/build_opus_x86-prefix/src/build_opus_x86/config.h") + +if(APPLE AND BUILD_OSX_UNIVERSAL) +include_directories( + ${BINARY_DIR}/build_opus_arm-prefix/src/build_opus_arm + ${BINARY_DIR}/build_opus_arm-prefix/src/build_opus_arm/dnn + ${BINARY_DIR}/build_opus_arm-prefix/src/build_opus_arm/celt + ${BINARY_DIR}/build_opus_arm-prefix/src/build_opus_arm/silk + ${BINARY_DIR}/build_opus_arm-prefix/src/build_opus_arm/include) +set_target_properties(opus_integ PROPERTIES + IMPORTED_LOCATION "${BINARY_DIR}/libopus${CMAKE_STATIC_LIBRARY_SUFFIX}" +) + +set(FARGAN_INTEG_CONFIG_H_FILE "${BINARY_DIR}/build_opus_arm-prefix/src/build_opus_arm/config.h") +else(APPLE AND BUILD_OSX_UNIVERSAL) +include_directories( + ${BINARY_DIR}/build_opus-prefix/src/build_opus + ${BINARY_DIR}/build_opus-prefix/src/build_opus/dnn + ${BINARY_DIR}/build_opus-prefix/src/build_opus/celt + ${BINARY_DIR}/build_opus-prefix/src/build_opus/silk + ${BINARY_DIR}/build_opus-prefix/src/build_opus/include) +set_target_properties(opus_integ PROPERTIES + IMPORTED_LOCATION "${BINARY_DIR}/build_opus-prefix/src/build_opus/.libs/libopus${CMAKE_STATIC_LIBRARY_SUFFIX}" +) +set(FARGAN_INTEG_CONFIG_H_FILE "${BINARY_DIR}/build_opus-prefix/src/build_opus/config.h") +set(FARGAN_INTEG_ARM_CONFIG_H_FILE "${FARGAN_CONFIG_H_FILE}") +set(FARGAN_INTEG_X86_CONFIG_H_FILE "${FARGAN_CONFIG_H_FILE}") +endif(APPLE AND BUILD_OSX_UNIVERSAL) + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/fargan_config_integ.h.in" "${CMAKE_CURRENT_BINARY_DIR}/fargan_config_integ.h") + diff --git a/fargan_config_integ.h.in b/fargan_config_integ.h.in new file mode 100644 index 00000000..df8ebb1e --- /dev/null +++ b/fargan_config_integ.h.in @@ -0,0 +1,15 @@ +#ifndef FARGAN_CONFIG_H +#define FARGAN_CONFIG_H + +// Include Opus config.h to suppress warnings +#ifdef __APPLE__ +#ifdef __aarch64__ +#include "@FARGAN_INTEG_ARM_CONFIG_H_FILE@" +#else +#include "@FARGAN_INTEG_X86_CONFIG_H_FILE@" +#endif // __aarch64__ +#else +#include "@FARGAN_INTEG_CONFIG_H_FILE@" +#endif // __APPLE__ + +#endif // FARGAN_CONFIG_H diff --git a/src/audio/MacAudioDevice.cpp b/src/audio/MacAudioDevice.cpp index 0c850807..5fab2355 100644 --- a/src/audio/MacAudioDevice.cpp +++ b/src/audio/MacAudioDevice.cpp @@ -114,7 +114,8 @@ static OSStatus SetCurrentIOBufferFrameSize(AudioObjectID inDeviceID, } MacAudioDevice::MacAudioDevice(MacAudioEngine* parent, std::string deviceName, int coreAudioId, IAudioEngine::AudioDirection direction, int numChannels, int sampleRate) - : coreAudioId_(coreAudioId) + : ThreadedObject("MacDevice") + , coreAudioId_(coreAudioId) , direction_(direction) , numChannels_(numChannels) , sampleRate_(sampleRate) diff --git a/src/audio/PortAudioInterface.h b/src/audio/PortAudioInterface.h index ed1b73f0..08578f0e 100644 --- a/src/audio/PortAudioInterface.h +++ b/src/audio/PortAudioInterface.h @@ -30,7 +30,8 @@ class PortAudioInterface : public ThreadedObject { public: - PortAudioInterface() = default; + PortAudioInterface() + : ThreadedObject("PortAudio") {} virtual ~PortAudioInterface() = default; std::future Initialize(); @@ -55,4 +56,4 @@ public: std::future GetStreamInfo(PaStream* stream); }; -#endif // PORT_AUDIO_INTERFACE_H \ No newline at end of file +#endif // PORT_AUDIO_INTERFACE_H diff --git a/src/audio/PulseAudioDevice.cpp b/src/audio/PulseAudioDevice.cpp index 743ba30e..a6c2cf43 100644 --- a/src/audio/PulseAudioDevice.cpp +++ b/src/audio/PulseAudioDevice.cpp @@ -365,16 +365,16 @@ void PulseAudioDevice::StreamReadCallback_(pa_stream *s, size_t length, void *us pa_stream_peek(s, &data, &length); if (!data || length == 0) { - return; //break; + return; } if (thisObj->onAudioDataFunction) { thisObj->onAudioDataFunction(*thisObj, const_cast(data), length / thisObj->getNumChannels() / sizeof(short), thisObj->onAudioDataState); } - sem_post(&thisObj->sem_); pa_stream_drop(s); } while (pa_stream_readable_size(s) > 0); + sem_post(&thisObj->sem_); } void PulseAudioDevice::StreamWriteCallback_(pa_stream *s, size_t length, void *userdata) diff --git a/src/audio/PulseAudioEngine.cpp b/src/audio/PulseAudioEngine.cpp index f3171c87..ff318074 100644 --- a/src/audio/PulseAudioEngine.cpp +++ b/src/audio/PulseAudioEngine.cpp @@ -26,6 +26,10 @@ #include #include "../util/logging/ulog.h" +#if defined(USE_RTKIT) +#include "rtkit.h" +#endif // defined(USE_RTKIT) + PulseAudioEngine::PulseAudioEngine() : initialized_(false) { @@ -73,6 +77,51 @@ void PulseAudioEngine::start() pa_context_set_state_callback(context_, [](pa_context*, void* mainloop) { pa_threaded_mainloop *threadedML = static_cast(mainloop); + +#if defined(USE_RTKIT) + if (pa_threaded_mainloop_in_thread(threadedML)) + { + DBusError error; + DBusConnection* bus = nullptr; + int result = 0; + + dbus_error_init(&error); + if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) + { + log_warn("Could not connect to system bus: %s", error.message); + } + else + { + int minNiceLevel = 0; + constexpr int ERROR_BUFFER_SIZE = 1024; + char tmpBuf[ERROR_BUFFER_SIZE]; + if ((result = rtkit_get_min_nice_level(bus, &minNiceLevel)) < 0) + { +#if (_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE + strerror_r(-result, tmpBuf, ERROR_BUFFER_SIZE); + log_warn("rtkit could not get minimum nice level: %s", tmpBuf); +#else + log_warn("rtkit could not get minimum nice level: %s", strerror_r(-result, tmpBuf, ERROR_BUFFER_SIZE)); +#endif // (_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE + } + else if ((result = rtkit_make_high_priority(bus, 0, minNiceLevel)) < 0) + { +#if (_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE + strerror_r(-result, tmpBuf, ERROR_BUFFER_SIZE); + log_warn("rtkit could not make high priority: %s", tmpBuf); +#else + log_warn("rtkit could not make high priority: %s", strerror_r(-result, tmpBuf, ERROR_BUFFER_SIZE)); +#endif // (_POSIX_C_SOURCE >= 200112L) && !_GNU_SOURCE + } + } + + if (bus != nullptr) + { + dbus_connection_unref(bus); + } + } +#endif // defined(USE_RTKIT) + pa_threaded_mainloop_signal(threadedML, 0); }, mainloop_); diff --git a/src/audio/WASAPIAudioDevice.cpp b/src/audio/WASAPIAudioDevice.cpp index 54902001..501679b6 100644 --- a/src/audio/WASAPIAudioDevice.cpp +++ b/src/audio/WASAPIAudioDevice.cpp @@ -37,7 +37,8 @@ thread_local HANDLE WASAPIAudioDevice::HelperTask_ = nullptr; WASAPIAudioDevice::WASAPIAudioDevice(ComPtr client, ComPtr device, IAudioEngine::AudioDirection direction, int sampleRate, int numChannels) - : client_(client) + : Win32COMObject("WASAPIDev") + , client_(client) , device_(device) , renderClient_(nullptr) , captureClient_(nullptr) diff --git a/src/audio/WASAPIAudioEngine.cpp b/src/audio/WASAPIAudioEngine.cpp index 7dca7d17..1ac8923b 100644 --- a/src/audio/WASAPIAudioEngine.cpp +++ b/src/audio/WASAPIAudioEngine.cpp @@ -34,7 +34,8 @@ #include "../util/logging/ulog.h" WASAPIAudioEngine::WASAPIAudioEngine() - : devEnumerator_(nullptr) + : Win32COMObject("WASAPIEng") + , devEnumerator_(nullptr) , inputDeviceList_(nullptr) , outputDeviceList_(nullptr) { diff --git a/src/integrations/common/CMakeLists.txt b/src/integrations/common/CMakeLists.txt index 6a3acc7d..095b16ba 100644 --- a/src/integrations/common/CMakeLists.txt +++ b/src/integrations/common/CMakeLists.txt @@ -1,7 +1,7 @@ set(PIPELINE_SUBSET ../../pipeline/AgcStep.cpp ../../pipeline/AudioPipeline.cpp - ../../pipeline/BandwidthExpandStep.cpp + #../../pipeline/BandwidthExpandStep.cpp ../../pipeline/IPipelineStep.cpp ../../pipeline/LevelAdjustStep.cpp ../../pipeline/RADEReceiveStep.cpp @@ -25,8 +25,8 @@ add_library(fdv_integ_audio_pipeline STATIC ${PIPELINE_SUBSET}) target_compile_options(fdv_integ_audio_pipeline PRIVATE ${WARNINGS_AS_ERRORS_FLAGS}) -target_compile_definitions(fdv_integ_audio_pipeline PRIVATE -DDISABLE_UNIT_TEST) -target_link_libraries(fdv_integ_audio_pipeline PRIVATE fdv_os_wrapper rade opus codec2 samplerate agc ebur128 ${SPEEXDSP_LIBRARY}) +target_compile_definitions(fdv_integ_audio_pipeline PRIVATE -DDISABLE_UNIT_TEST -DFREEDV_INTEGRATION) +target_link_libraries(fdv_integ_audio_pipeline PRIVATE fdv_os_wrapper rade_integ opus_integ codec2 samplerate agc ebur128 ${SPEEXDSP_LIBRARY}) if(STATIC_SPEEXDSP_BUILT) add_dependencies(fdv_integ_audio_pipeline speex) endif(STATIC_SPEEXDSP_BUILT) diff --git a/src/integrations/common/MinimalTxRxThread.h b/src/integrations/common/MinimalTxRxThread.h index 5b8e8597..6cdc0d3e 100644 --- a/src/integrations/common/MinimalTxRxThread.h +++ b/src/integrations/common/MinimalTxRxThread.h @@ -39,9 +39,13 @@ #include "../pipeline/RADETransmitStep.h" // TBD - need to wrap in "extern C" to avoid linker errors -extern "C" +extern "C" { +#if defined(FREEDV_INTEGRATION) + #include "fargan_config_integ.h" +#else #include "fargan_config.h" +#endif // defined(FREEDV_INTEGRATION) #include "fargan.h" #include "lpcnet.h" } diff --git a/src/integrations/common/ReportingController.cpp b/src/integrations/common/ReportingController.cpp index e34c962d..c29317b3 100644 --- a/src/integrations/common/ReportingController.cpp +++ b/src/integrations/common/ReportingController.cpp @@ -39,7 +39,8 @@ std::string ReportingController::getVersionString_() #define PSKREPORTER_REPORT_INTERVAL_MS (5*60*1000) ReportingController::ReportingController(std::string softwareName, bool rxOnly) - : softwareName_(std::move(softwareName)) + : ThreadedObject("Reporting") + , softwareName_(std::move(softwareName)) , pskReporterSendTimer_(PSKREPORTER_REPORT_INTERVAL_MS, [&](ThreadedTimer&) { enqueue_([&]() { if (pskReporterConnection_ != nullptr) diff --git a/src/integrations/flex/CMakeLists.txt b/src/integrations/flex/CMakeLists.txt index 42484a12..b7b787a1 100644 --- a/src/integrations/flex/CMakeLists.txt +++ b/src/integrations/flex/CMakeLists.txt @@ -13,4 +13,5 @@ add_executable(freedv-flex ${MIMALLOC_SOURCE_FILE}) target_compile_options(freedv-flex PRIVATE ${WARNINGS_AS_ERRORS_FLAGS} -Wno-missing-field-initializers) +target_compile_definitions(freedv-flex PRIVATE -DFREEDV_INTEGRATION) target_link_libraries(freedv-flex PRIVATE fdv_integ_audio_pipeline fdv_reporting fdv_util) diff --git a/src/integrations/flex/FlexVitaTask.cpp b/src/integrations/flex/FlexVitaTask.cpp index 4aa85d9c..42af6fb6 100644 --- a/src/integrations/flex/FlexVitaTask.cpp +++ b/src/integrations/flex/FlexVitaTask.cpp @@ -33,7 +33,8 @@ constexpr short FLOAT_TO_SHORT_MULTIPLIER = 32767; using namespace std::placeholders; FlexVitaTask::FlexVitaTask(std::shared_ptr helper, bool randomUdpPort) - : socket_(-1) + : ThreadedObject("FlexVita") + , socket_(-1) , rxStreamId_(0) , txStreamId_(0) , audioSeqNum_(0) diff --git a/src/integrations/flex/main.cpp b/src/integrations/flex/main.cpp index 15287dca..c7f196ee 100644 --- a/src/integrations/flex/main.cpp +++ b/src/integrations/flex/main.cpp @@ -47,7 +47,7 @@ extern "C" { - #include "fargan_config.h" + #include "fargan_config_integ.h" #include "fargan.h" #include "lpcnet.h" } diff --git a/src/integrations/ka9q/CMakeLists.txt b/src/integrations/ka9q/CMakeLists.txt index 496ca211..6ae9088f 100644 --- a/src/integrations/ka9q/CMakeLists.txt +++ b/src/integrations/ka9q/CMakeLists.txt @@ -9,4 +9,5 @@ add_executable(freedv-ka9q ${MIMALLOC_SOURCE_FILE}) target_compile_options(freedv-ka9q PRIVATE ${WARNINGS_AS_ERRORS_FLAGS}) +target_compile_definitions(freedv-ka9q PRIVATE -DFREEDV_INTEGRATION) target_link_libraries(freedv-ka9q PRIVATE git_version fdv_integ_audio_pipeline fdv_reporting fdv_util fdv_os_wrapper) diff --git a/src/integrations/ka9q/main.cpp b/src/integrations/ka9q/main.cpp index cf4c6aa7..16280fb8 100644 --- a/src/integrations/ka9q/main.cpp +++ b/src/integrations/ka9q/main.cpp @@ -59,7 +59,7 @@ extern "C" { - #include "fargan_config.h" + #include "fargan_config_integ.h" #include "fargan.h" #include "lpcnet.h" } diff --git a/src/main.cpp b/src/main.cpp index ce29aded..2c58dbb9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -259,6 +259,8 @@ void MainFrame::handleAudioDeviceChange_(std::string const& newDeviceName) void MainApp::UnitTest_() { + SetThreadName("UnitTest"); + // List audio devices auto engine = AudioEngineFactory::GetAudioEngine(); engine->start(); @@ -1009,9 +1011,7 @@ MainFrame::MainFrame(wxWindow *parent) : TopFrame(parent, wxID_ANY, _("FreeDV ") VAR_FMT("Var: %4.1f"), CLK_OFF_FMT("ClkOff: %+-d") { -#if defined(__linux__) - pthread_setname_np(pthread_self(), "FreeDV GUI"); -#endif // defined(__linux__) + SetThreadName("GUI"); terminating_ = false; realigned_ = false; @@ -2657,9 +2657,7 @@ void MainFrame::OnTogBtnOnOff(wxCommandEvent&) { std::thread onOffExec([this]() { -#if defined(__linux__) - pthread_setname_np(pthread_self(), "FreeDV TurningOn"); -#endif // defined(__linux__) + SetThreadName("TurningOn"); performFreeDVOn_(); @@ -2701,9 +2699,7 @@ void MainFrame::OnTogBtnOnOff(wxCommandEvent&) { std::thread onOffExec([this]() { -#if defined(__linux__) - pthread_setname_np(pthread_self(), "FreeDV TurningOff"); -#endif // defined(__linux__) + SetThreadName("TurningOff"); performFreeDVOff_(); @@ -3669,7 +3665,7 @@ void MainFrame::OnTxOutAudioData_(IAudioDevice& dev, void* data, size_t size, vo // If VOX tone is enabled, go back through and add the VOX tone // on the left channel. - if (cbData->leftChannelVoxTone) + if (cbData->leftChannelVoxTone.load(std::memory_order_acquire)) { cbData->voxTonePhase += 2.0*M_PI*VOX_TONE_FREQ/dev.getSampleRate(); cbData->voxTonePhase -= 2.0*M_PI*floor(cbData->voxTonePhase/(2.0*M_PI)); diff --git a/src/os/os_interface.h b/src/os/os_interface.h index c48f76ad..69f5697d 100644 --- a/src/os/os_interface.h +++ b/src/os/os_interface.h @@ -50,4 +50,7 @@ void StartLowLatencyActivity(); // Tells the operating system that we no longer need low latency. void StopLowLatencyActivity(); +// Sets the name of the current thread in the OS. +void SetThreadName(std::string const& name); + #endif // __OS_INTERFACE__ diff --git a/src/os/osx_interface.mm b/src/os/osx_interface.mm index 7a3eb71d..405772e3 100644 --- a/src/os/osx_interface.mm +++ b/src/os/osx_interface.mm @@ -23,6 +23,9 @@ #import #import #import + +#include + #include "os_interface.h" static id Activity = nil; @@ -117,3 +120,10 @@ std::string GetOperatingSystemString() { return "macos"; } + +void SetThreadName(std::string const& name) +{ + std::string fullName = "FDV "; + fullName += name; + pthread_setname_np(fullName.c_str()); +} diff --git a/src/os/osx_stubs.cpp b/src/os/osx_stubs.cpp index 8208e541..d0667161 100644 --- a/src/os/osx_stubs.cpp +++ b/src/os/osx_stubs.cpp @@ -50,3 +50,13 @@ std::string GetOperatingSystemString() return "other"; #endif // __linux__ } + +void SetThreadName(std::string const& name) +{ + std::string fullName = "FDV "; + fullName += name; +#ifdef __linux__ + pthread_setname_np(pthread_self(), fullName.c_str()); +#endif // __linux__ +} + diff --git a/src/os/windows_interface.cpp b/src/os/windows_interface.cpp index df69950f..1dc4da3f 100644 --- a/src/os/windows_interface.cpp +++ b/src/os/windows_interface.cpp @@ -63,3 +63,13 @@ std::string GetOperatingSystemString() { return "windows"; } + +void SetThreadName(std::string const& name) +{ + // XXX - assumes ASCII. This is probably fine, though, since + // this is debug code. + std::string fullName = "FDV "; + fullName += name; + std::wstring stemp = std::wstring(fullName.begin(), fullName.end()); + SetThreadDescription(GetCurrentThread(), stemp.c_str()); +} diff --git a/src/pipeline/CMakeLists.txt b/src/pipeline/CMakeLists.txt index 761e0df5..ba8f7689 100644 --- a/src/pipeline/CMakeLists.txt +++ b/src/pipeline/CMakeLists.txt @@ -26,7 +26,7 @@ add_library(fdv_audio_pipeline STATIC ) target_compile_options(fdv_audio_pipeline PRIVATE ${WARNINGS_AS_ERRORS_FLAGS}) -target_link_libraries(fdv_audio_pipeline PRIVATE ebur128 agc) +target_link_libraries(fdv_audio_pipeline PRIVATE ebur128 agc fdv_os_wrapper) target_include_directories(fdv_audio_pipeline PRIVATE ${CODEC2_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/..) add_dependencies(fdv_audio_pipeline rade opus codec2) diff --git a/src/pipeline/PlaybackStep.cpp b/src/pipeline/PlaybackStep.cpp index 3cc8287e..33eefc42 100644 --- a/src/pipeline/PlaybackStep.cpp +++ b/src/pipeline/PlaybackStep.cpp @@ -32,6 +32,8 @@ #include #endif // defined(__APPLE__) +#include "../os/os_interface.h" + extern wxMutex g_mutexProtectingCallbackData; using namespace std::chrono_literals; @@ -86,8 +88,11 @@ short* PlaybackStep::execute(short*, int numInputSamples, int* numOutputSamples) { outputFifo_.read(outputSamples_.get(), *numOutputSamples); } - - fileIoThreadSem_.signal(); + + if (outputFifo_.numUsed() < (outputFifo_.capacity() / 2)) + { + fileIoThreadSem_.signal(); + } return outputSamples_.get(); } @@ -101,6 +106,8 @@ void PlaybackStep::nonRtThreadEntry_() pthread_set_qos_class_self_np(QOS_CLASS_UTILITY, 0); #endif // defined(__APPLE__) + SetThreadName("PlayStep"); + while (!nonRtThreadEnding_) { g_mutexProtectingCallbackData.Lock(); diff --git a/src/pipeline/RADEReceiveStep.cpp b/src/pipeline/RADEReceiveStep.cpp index 62f06550..6e72a1f4 100644 --- a/src/pipeline/RADEReceiveStep.cpp +++ b/src/pipeline/RADEReceiveStep.cpp @@ -33,6 +33,8 @@ #include "pipeline_defines.h" +#include "../os/os_interface.h" + using namespace std::chrono_literals; #if !defined(DISABLE_UNIT_TEST) @@ -236,6 +238,8 @@ void RADEReceiveStep::utFeatureThreadEntry_() setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, IOPOL_THROTTLE); #endif // defined(__APPLE__) + SetThreadName("RADERx"); + float* featureBuf = new float[utFeatures_->capacity()]; assert(featureBuf != nullptr); diff --git a/src/pipeline/RADEReceiveStep.h b/src/pipeline/RADEReceiveStep.h index 0f4da005..6b0baa2c 100644 --- a/src/pipeline/RADEReceiveStep.h +++ b/src/pipeline/RADEReceiveStep.h @@ -29,7 +29,6 @@ #include #include "IPipelineStep.h" -#include "../freedv_interface.h" #include "rade_api.h" #include "rade_text.h" #include "../util/GenericFIFO.h" @@ -42,9 +41,15 @@ #define NUM_FEATURES_TO_STORE (256 * 1024) // TBD - need to wrap in "extern C" to avoid linker errors -extern "C" +extern "C" { +#if defined(FREEDV_INTEGRATION) + #include "fargan_config_integ.h" +#else + #include "fargan_config.h" +#endif // defined(FREEDV_INTEGRATION) #include "fargan.h" + #include "lpcnet.h" } class RADEReceiveStep : public IPipelineStep diff --git a/src/pipeline/RADETransmitStep.cpp b/src/pipeline/RADETransmitStep.cpp index 06339421..07226601 100644 --- a/src/pipeline/RADETransmitStep.cpp +++ b/src/pipeline/RADETransmitStep.cpp @@ -33,6 +33,8 @@ #include #endif // defined(__APPLE__) +#include "../os/os_interface.h" + using namespace std::chrono_literals; #define FEATURE_FIFO_SIZE ((RADE_SPEECH_SAMPLE_RATE / LPCNET_FRAME_SIZE) * rade_n_features_in_out(dv_)) @@ -246,6 +248,8 @@ void RADETransmitStep::utFeatureThreadEntry_() setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, IOPOL_THROTTLE); #endif // defined(__APPLE__) + SetThreadName("RADETx"); + float* featureBuf = new float[utFeatures_->capacity()]; assert(featureBuf != nullptr); diff --git a/src/pipeline/RADETransmitStep.h b/src/pipeline/RADETransmitStep.h index b2962235..9902535d 100644 --- a/src/pipeline/RADETransmitStep.h +++ b/src/pipeline/RADETransmitStep.h @@ -28,12 +28,22 @@ #include #include "IPipelineStep.h" -#include "../freedv_interface.h" #include "rade_api.h" -#include "lpcnet.h" #include "../util/GenericFIFO.h" #include "../util/Semaphore.h" +// TBD - need to wrap in "extern C" to avoid linker errors +extern "C" +{ +#if defined(FREEDV_INTEGRATION) + #include "fargan_config_integ.h" +#else + #include "fargan_config.h" +#endif // defined(FREEDV_INTEGRATION) + #include "fargan.h" + #include "lpcnet.h" +} + // Number of features to store. This is set to be close to the // typical size for RX/TX features for the rade_loss ctest to // avoid contention with normal RADE operation. diff --git a/src/pipeline/RecordStep.cpp b/src/pipeline/RecordStep.cpp index 84d8ae3d..cc6be3eb 100644 --- a/src/pipeline/RecordStep.cpp +++ b/src/pipeline/RecordStep.cpp @@ -29,6 +29,8 @@ #include #endif // defined(__APPLE__) +#include "../os/os_interface.h" + extern wxMutex g_mutexProtectingCallbackData; using namespace std::chrono_literals; @@ -97,6 +99,8 @@ void RecordStep::fileIoThreadEntry_() pthread_set_qos_class_self_np(QOS_CLASS_UTILITY, 0); #endif // defined(__APPLE__) + SetThreadName("RecordStep"); + while (!fileIoThreadEnding_) { g_mutexProtectingCallbackData.Lock(); diff --git a/src/pipeline/TapStep.cpp b/src/pipeline/TapStep.cpp index 32de89ba..ba49c9b7 100644 --- a/src/pipeline/TapStep.cpp +++ b/src/pipeline/TapStep.cpp @@ -30,6 +30,8 @@ #include #endif // defined(__APPLE__) +#include "../os/os_interface.h" + using namespace std::chrono_literals; TapStep::TapStep(int sampleRate, IPipelineStep* tapStep) @@ -43,6 +45,8 @@ TapStep::TapStep(int sampleRate, IPipelineStep* tapStep) short* fifoInput = new short[SAMPLE_RATE_AT_10MS]; assert(fifoInput != nullptr); + SetThreadName("TapStep"); + #if defined(__APPLE__) // Downgrade thread QoS to Utility to avoid thread contention issues. pthread_set_qos_class_self_np(QOS_CLASS_UTILITY, 0); diff --git a/src/pipeline/TxRxThread.cpp b/src/pipeline/TxRxThread.cpp index d8880671..7d38d4cf 100644 --- a/src/pipeline/TxRxThread.cpp +++ b/src/pipeline/TxRxThread.cpp @@ -531,12 +531,11 @@ void* TxRxThread::Entry() noexcept resetStats_(); #endif // defined(ENABLE_PROCESSING_STATS) -#if defined(__linux__) + // Set thread name for debugging const char* threadName = nullptr; - if (m_tx) threadName = "FreeDV txThread"; - else threadName = "FreeDV rxThread"; - pthread_setname_np(pthread_self(), threadName); -#endif // defined(__linux__) + if (m_tx) threadName = "txThread"; + else threadName = "rxThread"; + SetThreadName(threadName); // Make sure we don't start processing until // the main thread is ready. diff --git a/src/reporting/pskreporter.cpp b/src/reporting/pskreporter.cpp index e68eefd3..19469c14 100644 --- a/src/reporting/pskreporter.cpp +++ b/src/reporting/pskreporter.cpp @@ -49,6 +49,7 @@ #endif // defined(WIN32) || defined(__MINGW32__) #include "pskreporter.h" +#include "../os/os_interface.h" //#define PSK_REPORTER_TEST #if defined(PSK_REPORTER_TEST) @@ -270,6 +271,8 @@ bool PskReporter::reportCommon_() { std::unique_lock lock(recordListMutex_); + SetThreadName("PSKReport"); + // Header (2) + length (2) + time (4) + sequence # (4) + random identifier (4) + // RX format block + TX format block + RX data + TX data int dgSize = 16 + sizeof(rxFormatHeader) + sizeof(txFormatHeader) + getRxDataSize_() + getTxDataSize_(); diff --git a/src/rig_control/HamlibRigController.cpp b/src/rig_control/HamlibRigController.cpp index 9fcc52c6..6fafd2d8 100644 --- a/src/rig_control/HamlibRigController.cpp +++ b/src/rig_control/HamlibRigController.cpp @@ -69,7 +69,8 @@ bool HamlibRigController::RigCompare_(const struct rig_caps *rig1, const struct } HamlibRigController::HamlibRigController(std::string rigName, std::string serialPort, const int serialRate, const int civHex, const PttType pttType, std::string pttSerialPort, bool restoreFreqModeOnDisconnect, bool freqOnly) - : rigName_(std::move(rigName)) + : ThreadedObject("hamlib") + , rigName_(std::move(rigName)) , serialPort_(std::move(serialPort)) , serialRate_(serialRate) , civHex_(civHex) @@ -92,7 +93,8 @@ HamlibRigController::HamlibRigController(std::string rigName, std::string serial } HamlibRigController::HamlibRigController(int rigIndex, std::string serialPort, const int serialRate, const int civHex, const PttType pttType, std::string pttSerialPort, bool restoreFreqModeOnDisconnect, bool freqOnly) - : rigName_(RigIndexToName(rigIndex)) + : ThreadedObject("hamlib") + , rigName_(RigIndexToName(rigIndex)) , serialPort_(std::move(serialPort)) , serialRate_(serialRate) , civHex_(civHex) diff --git a/src/rig_control/SerialPortInRigController.cpp b/src/rig_control/SerialPortInRigController.cpp index 614ca304..cf011227 100644 --- a/src/rig_control/SerialPortInRigController.cpp +++ b/src/rig_control/SerialPortInRigController.cpp @@ -21,6 +21,7 @@ //========================================================================= #include "SerialPortInRigController.h" +#include "../os/os_interface.h" #define PTT_INPUT_MONITORING_TIME_MS 10 @@ -80,6 +81,8 @@ bool SerialPortInRigController::getCTS_() void SerialPortInRigController::pollThreadEntry_() { + SetThreadName("SerialIn"); + while (!threadExiting_) { enqueue_(std::bind(&SerialPortInRigController::pollSerialPort_, this)); diff --git a/src/rig_control/SerialPortRigController.cpp b/src/rig_control/SerialPortRigController.cpp index 5ddc9594..f7c327cf 100644 --- a/src/rig_control/SerialPortRigController.cpp +++ b/src/rig_control/SerialPortRigController.cpp @@ -27,7 +27,8 @@ #include "../util/logging/ulog.h" SerialPortRigController::SerialPortRigController(std::string serialPort) - : serialPortHandle_(COM_HANDLE_INVALID) + : ThreadedObject("SerialController") + , serialPortHandle_(COM_HANDLE_INVALID) , serialPort_(std::move(serialPort)) { // empty diff --git a/src/rig_control/omnirig/OmniRigController.cpp b/src/rig_control/omnirig/OmniRigController.cpp index abfd8882..62f9dd88 100644 --- a/src/rig_control/omnirig/OmniRigController.cpp +++ b/src/rig_control/omnirig/OmniRigController.cpp @@ -34,7 +34,8 @@ using namespace std::chrono_literals; #define OMNI_RIG_WAIT_TIME (200ms) OmniRigController::OmniRigController(int rigId, bool restoreOnDisconnect, bool freqOnly) - : rigId_(rigId) + : ThreadedObject("OmniRig") + , rigId_(rigId) , omniRig_(nullptr) , rig_(nullptr) , origFreq_(0) diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 4586e3d3..0def92b2 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -24,7 +24,7 @@ add_library(fdv_util STATIC ) target_compile_options(fdv_util PRIVATE ${WARNINGS_AS_ERRORS_FLAGS}) -target_link_libraries(fdv_util yyjson) +target_link_libraries(fdv_util yyjson fdv_os_wrapper) if(UNITTEST) macro(DefineUnitTest utName) diff --git a/src/util/TcpConnectionHandler.cpp b/src/util/TcpConnectionHandler.cpp index 4f8cb0a8..207f71f1 100644 --- a/src/util/TcpConnectionHandler.cpp +++ b/src/util/TcpConnectionHandler.cpp @@ -47,6 +47,7 @@ #include "TcpConnectionHandler.h" #include "logging/ulog.h" +#include "../os/os_interface.h" using namespace std::chrono_literals; @@ -59,7 +60,8 @@ using namespace std::chrono_literals; #endif // INVALID_SOCKET TcpConnectionHandler::TcpConnectionHandler() - : enableReconnect_(false) + : ThreadedObject("TcpConn") + , enableReconnect_(false) , reconnectTimer_(RECONNECT_INTERVAL_MS, [&](ThreadedTimer&) { enqueue_(std::bind(&TcpConnectionHandler::connectImpl_, this)); }, false) @@ -178,6 +180,8 @@ void TcpConnectionHandler::connectImpl_() std::string portStr = portStream.str(); std::thread ipv6ResolveThread([&, portStr, ipv6ResultPromise]() { + SetThreadName("ipv6"); + struct addrinfo *result = nullptr; resolveAddresses_(AF_INET6, host_.c_str(), portStr.c_str(), &result); ipv6ResultPromise->set_value(result); @@ -185,6 +189,8 @@ void TcpConnectionHandler::connectImpl_() }); std::thread ipv4ResolveThread([&, portStr, ipv4ResultPromise]() { + SetThreadName("ipv4"); + struct addrinfo *result = nullptr; resolveAddresses_(AF_INET, host_.c_str(), portStr.c_str(), &result); ipv4ResultPromise->set_value(result); @@ -579,9 +585,10 @@ void TcpConnectionHandler::sendImpl_(const char* buf, int length) void TcpConnectionHandler::receiveImpl_() { constexpr int READ_SIZE_BYTES = 1024; - char buf[READ_SIZE_BYTES]; + SetThreadName("TCPRx"); + while (socket_.load(std::memory_order_acquire) != INVALID_SOCKET) { struct timeval tv = {0, 250000}; // 250ms diff --git a/src/util/ThreadedObject.cpp b/src/util/ThreadedObject.cpp index cb44b39d..a2ec0761 100644 --- a/src/util/ThreadedObject.cpp +++ b/src/util/ThreadedObject.cpp @@ -21,12 +21,14 @@ //========================================================================= #include +#include "../os/os_interface.h" #include "ThreadedObject.h" using namespace std::chrono_literals; -ThreadedObject::ThreadedObject(ThreadedObject* parent) +ThreadedObject::ThreadedObject(std::string name, ThreadedObject* parent) : parent_(parent) + , name_(std::move(name)) , isDestroying_(false) { // Instantiate thread here rather than the initializer since otherwise @@ -107,6 +109,8 @@ void ThreadedObject::eventLoop_() pthread_set_qos_class_self_np(QOS_CLASS_UTILITY,0); #endif // defined(__APPLE__) + SetThreadName(name_); + while (!isDestroying_.load(std::memory_order_acquire)) { std::function fn; diff --git a/src/util/ThreadedObject.h b/src/util/ThreadedObject.h index c9ba1c4e..d0797d35 100644 --- a/src/util/ThreadedObject.h +++ b/src/util/ThreadedObject.h @@ -33,6 +33,7 @@ #include #include #include +#include class ThreadedObject { @@ -40,7 +41,7 @@ public: virtual ~ThreadedObject(); protected: - ThreadedObject(ThreadedObject* parent = nullptr); + ThreadedObject(std::string name, ThreadedObject* parent = nullptr); // Enqueues some code to run on a different thread. // @param timeoutMilliseconds Timeout to wait for lock. Note: if we can't get a lock within the timeout, the function doesn't run! @@ -48,7 +49,8 @@ protected: private: ThreadedObject* parent_; - + std::string name_; + #if defined(__APPLE__) dispatch_queue_t queue_; #else diff --git a/src/util/ThreadedObject.mm b/src/util/ThreadedObject.mm index 784d243a..035d591d 100644 --- a/src/util/ThreadedObject.mm +++ b/src/util/ThreadedObject.mm @@ -24,8 +24,9 @@ #include "ThreadedObject.h" -ThreadedObject::ThreadedObject(ThreadedObject* parent) +ThreadedObject::ThreadedObject(std::string name, ThreadedObject* parent) : parent_(parent) + , name_(std::move(name)) { dispatch_queue_t parentQueue; diff --git a/src/util/ThreadedTimer.cpp b/src/util/ThreadedTimer.cpp index 43d76d06..0cdb26e8 100644 --- a/src/util/ThreadedTimer.cpp +++ b/src/util/ThreadedTimer.cpp @@ -21,6 +21,7 @@ //========================================================================= #include "ThreadedTimer.h" +#include "../os/os_interface.h" #include @@ -85,6 +86,8 @@ void ThreadedTimer::TimerServer::unregisterTimer(ThreadedTimer* timer) void ThreadedTimer::TimerServer::eventLoop_() { + SetThreadName("Timer"); + std::unique_lock lk(mutex_); std::chrono::time_point nextFireTime; while (!isDestroying_.load(std::memory_order_acquire)) diff --git a/src/util/Win32COMObject.cpp b/src/util/Win32COMObject.cpp index a4efd1d5..730983bc 100644 --- a/src/util/Win32COMObject.cpp +++ b/src/util/Win32COMObject.cpp @@ -27,7 +27,8 @@ #include -Win32COMObject::Win32COMObject() +Win32COMObject::Win32COMObject(std::string name) + : ThreadedObject(name) { enqueue_([&]() { HRESULT res = CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); diff --git a/src/util/Win32COMObject.h b/src/util/Win32COMObject.h index 74dd6757..c2f668b8 100644 --- a/src/util/Win32COMObject.h +++ b/src/util/Win32COMObject.h @@ -32,7 +32,7 @@ public: virtual ~Win32COMObject(); protected: - Win32COMObject(); + Win32COMObject(std::string name); private: };