修复退出时taskflow线程安全导致的崩溃

移除Tracy, 因为其导致线程安全问题崩溃
This commit is contained in:
Nanako 2024-05-26 00:17:38 +08:00
parent dc57b52be1
commit 6a7e99a3f8
13 changed files with 105 additions and 86 deletions

View File

@ -2,26 +2,15 @@ cmake_minimum_required(VERSION 3.5)
project(core)
set(CMAKE_CXX_STANDARD 23)
include(FetchContent)
FetchContent_Declare(
tracy
GIT_REPOSITORY https://github.com/wolfpld/tracy.git
GIT_TAG master
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(tracy)
set(ALL_FILES "")
retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} ALL_FILES)
add_library(${PROJECT_NAME} SHARED ${ALL_FILES})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${PROJECT_NAME} PUBLIC rtaudio spdlog mempool Taskflow glfw Tracy::TracyClient)
target_link_libraries(${PROJECT_NAME} PUBLIC rtaudio spdlog mempool Taskflow glfw)
add_definitions(-Dcore_EXPORTS)
@ -42,8 +31,3 @@ elseif(APPLE)
elseif(UNIX AND NOT APPLE)
target_compile_definitions(${PROJECT_NAME} PUBLIC PLATFORM_WINDOWS=0 PLATFORM_MACOS=0 PLATFORM_LINUX=1 GLFW_EXPOSE_NATIVE_X11)
endif()
# is debug
if (CMAKE_BUILD_TYPE MATCHES Debug)
add_compile_definitions(-DTRACY_ENABLE=1)
endif ()

View File

@ -6,6 +6,7 @@
#include "audio/plugin_host/plugin_host_manager.h"
#include "spdlog/spdlog.h"
#include "thread_message/thread_message_hubs.h"
#include "misc/platform_thread.h"
IMPL_SINGLETON_INSTANCE(audio_device_manager)
@ -32,9 +33,9 @@ void audio_device_manager::init(singleton_initliazer& initliazer) {
void audio_device_manager::release(singleton_release_guard& release_guard) {
singleton_t<audio_device_manager>::release(release_guard);
stop_render_thread();
release_guard.require_release<mixer>();
audio_->stopStream();
stop_render_thread();
delete audio_;
}
@ -116,18 +117,27 @@ void audio_device_manager::log_current_device_info() {
}
void audio_device_manager::start_render_thread() {
render_thread_should_stop_ = false;
render_thread_ = std::thread(&audio_device_manager::render_thread, this);
render_thread_.detach();
spdlog::info("port_audio render thread started");
}
void audio_device_manager::stop_render_thread() {
render_thread_should_stop_ = true;
if (render_thread_.joinable())
render_thread_.join();
// wait for render thread to stop
while (render_thread_running_) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
spdlog::info("port_audio render thread stopped");
}
void audio_device_manager::render_thread() {
set_thread_name("audio_render");
render_thread_running_ = true;
dummy_track* master = g_mixer.get_master();
spdlog::info("port_audio render thread started");
render_buffers_.resize(get_output_channel_count());
@ -137,7 +147,7 @@ void audio_device_manager::render_thread() {
for (auto& render_buffer : render_buffers_)
render_buffer.SetCapacity(frames * 3);
while (audio_->isStreamRunning()) {
while (!render_thread_should_stop_) {
// const float milliseconds = 1.f / (rate / (frames / 4)) * 1e4;
g_audio_thread_hub.process_messages();
@ -157,5 +167,5 @@ void audio_device_manager::render_thread() {
render_buffers_[i].Push(element, frames);
}
}
spdlog::info("port_audio render thread stopped");
render_thread_running_ = false;
}

View File

@ -31,6 +31,7 @@ protected:
std::thread render_thread_;
std::vector<circular_audio_buffer<float>> render_buffers_;
std::atomic_bool render_thread_running_ = false;
std::atomic_bool render_thread_should_stop_ = false;
#pragma endregion
private:
RtAudio::StreamParameters input_params_ = {};

View File

@ -1,10 +1,8 @@
#include "audio_buffer.h"
#include <cstring>
#include <tracy/tracy.hpp>
void audio_buffer::resize(uint32_t channel_num, uint32_t block_size) {
ZoneScoped;
std::scoped_lock lock(lock_);
buffer_.resize(channel_num);
headers_.resize(channel_num);
@ -15,7 +13,6 @@ void audio_buffer::resize(uint32_t channel_num, uint32_t block_size) {
}
void audio_buffer::clear() {
ZoneScoped;
std::scoped_lock lock(lock_);
for (auto& channel : buffer_) {
std::memset(channel.data(), 0, channel.size() * sizeof(float));
@ -23,7 +20,6 @@ void audio_buffer::clear() {
}
void audio_buffer::mix(audio_buffer& in_buffer, float percent) {
ZoneScoped;
std::scoped_lock lock(lock_);
// will be optimized by compiler
for (uint32_t channel_index = 0; channel_index < buffer_.size(); channel_index++) {
@ -36,7 +32,6 @@ void audio_buffer::mix(audio_buffer& in_buffer, float percent) {
}
void audio_buffer::multiple(float percent) {
ZoneScoped;
std::scoped_lock lock(lock_);
// will be optimized by compiler
for (auto& channel : buffer_) {
@ -47,7 +42,6 @@ void audio_buffer::multiple(float percent) {
}
std::vector<float> audio_buffer::get_interleaved_buffer() {
ZoneScoped;
std::scoped_lock lock(lock_);
std::vector<float> result;
result.reserve(buffer_[0].size() * buffer_.size());

View File

@ -44,7 +44,7 @@ class null_channel_node : public channel_node {
public:
null_channel_node() : channel_node(nullptr, channel_node_type::null) {}
static void init() { instance = new null_channel_node(); }
static void destroy() { delete instance; }
static void destroy() { delete instance; instance = nullptr; }
static null_channel_node* get() { return instance; }
std::string get_name() override { return "NullChannelNode"; }

View File

@ -1,7 +1,5 @@
#include "mixer.h"
#include <tracy/tracy.hpp>
#include "channel_interface.h"
#include "channel_node.h"
#include "mixer_track.h"
@ -14,7 +12,6 @@
IMPL_SINGLETON_INSTANCE(mixer)
void build_effect_channel_interface(mixer_track* track, const channel_interface* in_interface, std::map<mixer_track*, int32_t>& processed_tracks) {
ZoneScoped;
int32_t& track_current_layer = processed_tracks[track];
auto& input_channel_nodes = in_interface->input_channel_nodes;
auto& output_channel_nodes = in_interface->output_channel_nodes;
@ -42,7 +39,6 @@ void build_effect_channel_interface(mixer_track* track, const channel_interface*
}
int32_t build_process_node_internal(mixer_track* track, std::map<mixer_track*, int32_t>& processed_tracks, int32_t layer) {
ZoneScoped;
int32_t& track_current_layer = processed_tracks[track];
track_current_layer = std::max(track_current_layer, layer);
for (const mixer_track_link& child_link: track->children) {
@ -62,10 +58,9 @@ void build_instrument_process_node(const plugin_host* host, std::map<mixer_track
}
void mixer::init(singleton_initliazer& initliazer) {
ZoneScoped;
singleton_t<mixer>::init(initliazer);
null_channel_node::init();
executor_ = new tf::Executor(std::thread::hardware_concurrency());
executor_.exchange(new tf::Executor(std::thread::hardware_concurrency()));
auto device_manager = initliazer.require<audio_device_manager>();
zero_track = new dummy_track();
@ -79,19 +74,18 @@ void mixer::init(singleton_initliazer& initliazer) {
}
void mixer::release(singleton_release_guard& release_guard) {
ZoneScoped;
singleton_t<mixer>::release(release_guard);
release_guard.require_release<audio_device_manager>();
null_channel_node::destroy();
for (const mixer_track* track : tracks_) {
delete track;
}
tracks_.clear();
delete zero_track;
delete executor_;
zero_track = nullptr;
}
dummy_track* mixer::create_dummy_track(const std::string& in_name) {
ZoneScoped;
auto* track = new dummy_track();
track->rename(in_name);
track->init();
@ -108,7 +102,6 @@ dummy_track* mixer::create_dummy_track(const std::string& in_name) {
}
instrument_track* mixer::create_instrument_track(plugin_host* in_instrument) {
ZoneScoped;
auto* track = new instrument_track(in_instrument);
track->rename(in_instrument->name);
track->init();
@ -131,21 +124,10 @@ void mixer::remove_track(mixer_track* track) {
}
void mixer::process(uint32_t in_frames) {
ZoneScoped;
tf::Taskflow taskflow;
tf::Task previous_task = taskflow.emplace([] {});
for (const auto& order: layer_order_) {
for (const auto& layer = layer_tracks_[order]; const auto& track: layer) {
taskflow.emplace([track, in_frames] {
track->process(in_frames);
}).succeed(previous_task);
}
tf::Task new_layer = taskflow.emplace([] {});
new_layer.succeed(previous_task);
previous_task = new_layer;
}
executor_->run(taskflow).wait();
auto e = executor_.load();
if (!e)
return;
e->run(taskflow_).wait();
post_process(in_frames);
dummy_track* master = get_master();
master->buffer.multiple(master->volume);
@ -158,7 +140,6 @@ void mixer::reset() {
}
void mixer::build_process_node() {
ZoneScoped;
if (tracks_.empty())
return;
std::map<mixer_track*, int32_t> processed_tracks;
@ -183,6 +164,7 @@ void mixer::build_process_node() {
void mixer::request_build_process_node() {
g_audio_thread_hub.push_message([this] {
build_process_node();
update_taskflow(g_audio_device_manager.get_buffer_size());
});
}
@ -199,7 +181,6 @@ void post_process_internal(mixer_track* track, uint32_t in_frames, std::vector<m
}
void mixer::post_process(uint32_t in_frames) const {
ZoneScoped;
const auto master = get_master();
std::vector<mixer_track*> processed_tracks;
post_process_internal(master, in_frames, processed_tracks);
@ -208,6 +189,7 @@ void mixer::post_process(uint32_t in_frames) const {
void mixer::thread_register_track(mixer_track* track) {
tracks_.push_back(track);
build_process_node();
update_taskflow(g_audio_device_manager.get_buffer_size());
on_add_track.broadcast(track);
g_main_thread_hub.push_message([track, this] {
on_add_track_main_thread.broadcast(track);
@ -220,6 +202,7 @@ void mixer::thread_remove_track(mixer_track* track) {
}
get_master()->remove_child(track);
build_process_node();
update_taskflow(g_audio_device_manager.get_buffer_size());
on_remove_track.broadcast(track);
g_main_thread_hub.push_message([track, this]() {
@ -227,3 +210,26 @@ void mixer::thread_remove_track(mixer_track* track) {
delete track;
});
}
void mixer::update_taskflow(uint32_t in_frames) {
tf::Taskflow taskflow;
tf::Task previous_task = taskflow.emplace([] {});
for (const auto& order: layer_order_) {
for (const auto& layer = layer_tracks_[order]; const auto& track: layer) {
taskflow.emplace([track, in_frames] {
track->process(in_frames);
}).succeed(previous_task);
}
tf::Task new_layer = taskflow.emplace([] {});
new_layer.succeed(previous_task);
previous_task = new_layer;
}
taskflow_ = std::move(taskflow);
}
void mixer::begin_release(singleton_release_guard &release_guard) {
singleton::begin_release(release_guard);
auto e = executor_.exchange(nullptr);
e->wait_for_all();
delete e;
}

View File

@ -17,6 +17,7 @@ class mixer : public singleton_t<mixer> {
public:
void init(singleton_initliazer& initliazer) override;
void begin_release(singleton_release_guard &release_guard) override;
void release(singleton_release_guard& release_guard) override;
const char* get_name() override { return "mixer"; }
@ -43,10 +44,13 @@ private:
void thread_register_track(mixer_track* track);
void thread_remove_track(mixer_track* track);
void update_taskflow(uint32_t in_frames);
std::vector<mixer_track*> tracks_;
std::map<int32_t, std::vector<mixer_track*>> layer_tracks_;
std::vector<int32_t> layer_order_;
tf::Executor* executor_;
std::atomic<tf::Executor*> executor_;
tf::Taskflow taskflow_;
};
DEFINE_SINGLETON_INSTANCE(mixer)

View File

@ -1,7 +1,5 @@
#include "mixer_track.h"
#include <tracy/tracy.hpp>
#include "channel_interface.h"
#include "audio/device/audio_device_manager.h"
#include "audio/plugin_host/plugin_host.h"
@ -12,7 +10,6 @@ mixer_track::~mixer_track() {
}
void mixer_track::init() {
ZoneScoped;
const uint32_t channel_count = g_audio_device_manager.get_output_channel_count();
buffer.resize(channel_count, g_audio_device_manager.get_buffer_size());
for (int i = 0; i < buffer.get_num_channels(); ++i) {
@ -50,7 +47,6 @@ void mixer_track::remove_child(mixer_track* in_child) {
}
void mixer_track::process(uint32_t in_frames) {
ZoneScoped;
for (auto effect : effects)
effect->process(in_frames);
buffer.multiple(volume);

View File

@ -1,7 +1,5 @@
#include "midi_sequencer.h"
#include <tracy/tracy.hpp>
#include "vst2/vst2_plugin_host.h"
IMPL_SINGLETON_INSTANCE(midi_sequencer)
@ -14,5 +12,4 @@ void midi_sequencer::init(singleton_initliazer& initliazer) {
}
void midi_sequencer::process(double sample_rate, uint32_t in_frames) {
ZoneScoped;
}

View File

@ -1,6 +1,5 @@
#include "plugin_host_manager.h"
#include "plugin_host.h"
#include "audio/device/audio_device_manager.h"
#include "audio/mixer/channel_interface.h"
@ -11,31 +10,27 @@
#include "thread_message/thread_message_hubs.h"
#include "vst2/vst2_plugin_host.h"
#include "window/window_manager.h"
#include "tracy/tracy.hpp"
IMPL_SINGLETON_INSTANCE(plugin_host_manager)
void plugin_host_manager::init(singleton_initliazer& initliazer) {
ZoneScoped;
singleton_t<plugin_host_manager>::init(initliazer);
auto* mixer_ptr = initliazer.require<mixer>();
mixer_ptr->on_remove_track.add_raw(this, &plugin_host_manager::on_mixer_track_removed);
executor_ = new tf::Executor(std::thread::hardware_concurrency());
executor_.store(new tf::Executor(std::thread::hardware_concurrency()));
}
void plugin_host_manager::release(singleton_release_guard& release_guard) {
ZoneScoped;
singleton_t<plugin_host_manager>::release(release_guard);
release_guard.require_release<audio_device_manager>();
for (const plugin_host* host: plugin_hosts_) {
delete host;
}
delete executor_;
plugin_hosts_.clear();
}
plugin_host* plugin_host_manager::create_instrument_plugin_host(const char* path) {
ZoneScoped;
auto host = load_plugin(path);
if (host) {
register_instrument_plugin(host);
@ -54,12 +49,12 @@ void plugin_host_manager::remove_instrument_plugin_host(plugin_host* host) {
on_instrument_removed.broadcast(host);
delete host;
});
update_taskflow(g_audio_device_manager.get_buffer_size());
}
});
}
plugin_host* plugin_host_manager::load_plugin(const char* path) {
ZoneScoped;
auto host = new vst2_plugin_host();
try {
host->load_plugin(path);
@ -83,27 +78,39 @@ void plugin_host_manager::register_instrument_plugin(plugin_host* host) {
host->channel->set_output_channel(instrument_track->get_channel_interface()->output_channel_nodes);
host->owner_tracks.push_back(instrument_track);
host->update_channel_node_name();
update_taskflow(g_audio_device_manager.get_buffer_size());
g_main_thread_hub.push_message([host, this]() {
on_instrument_added.broadcast(host);
});
});
}
void plugin_host_manager::process(uint32_t in_frames) const {
ZoneScoped;
void plugin_host_manager::process(uint32_t in_frames) {
tf::Executor* executor = executor_.load();
if (!executor) {
return;
}
executor->run(taskflow_).wait();
}
void plugin_host_manager::on_mixer_track_removed(mixer_track* track) {
for (auto host : plugin_hosts_) {
host->channel->remove_track(track);
}
}
void plugin_host_manager::update_taskflow(uint32_t in_frames) {
tf::Taskflow taskflow;
for (auto host : plugin_hosts_) {
taskflow.emplace([host, in_frames] {
host->process(in_frames);
});
}
executor_->run(taskflow).wait();
taskflow_ = std::move(taskflow);
}
void plugin_host_manager::on_mixer_track_removed(mixer_track* track) {
ZoneScoped;
for (auto host : plugin_hosts_) {
host->channel->remove_track(track);
}
void plugin_host_manager::begin_release(singleton_release_guard& release_guard) {
auto executor = executor_.exchange(nullptr);
executor->wait_for_all();
delete executor;
}

View File

@ -9,6 +9,7 @@ class mixer_track;
class CORE_API plugin_host_manager : public singleton_t<plugin_host_manager> {
public:
void init(singleton_initliazer& initliazer) override;
void begin_release(singleton_release_guard& release_guard) override;
void release(singleton_release_guard& release_guard) override;
@ -16,7 +17,7 @@ public:
void remove_instrument_plugin_host(plugin_host* host);
const std::vector<plugin_host*>& get_plugin_hosts() { return plugin_hosts_; }
void process(uint32_t in_frames) const;
void process(uint32_t in_frames);
const char* get_name() override { return "plugin_host_manager"; }
@ -30,7 +31,9 @@ private:
std::vector<plugin_host*> instrument_plugins_{};
std::vector<plugin_host*> plugin_hosts_{};
tf::Executor* executor_;
void update_taskflow(uint32_t in_frames);
std::atomic<tf::Executor*> executor_;
tf::Taskflow taskflow_;
};
DEFINE_SINGLETON_INSTANCE(plugin_host_manager)

View File

@ -1,7 +1,5 @@
#include "vst2_plugin_host.h"
#include <tracy/tracy.hpp>
#include "audio/device/audio_device_manager.h"
#include "audio/mixer/channel_interface.h"
@ -212,7 +210,6 @@ void vst2_plugin_host::update_channel_node_name() {
void vst2_plugin_host::process(uint32_t frame_num) {
// TODO send midi
ZoneScoped;
effect_->processReplacing(effect_, channel->get_input_headers(), channel->get_output_headers(), frame_num);
}

View File

@ -0,0 +1,20 @@
#include <iostream>
#include <thread>
#if PLATFORM_WINDOWS
#include <windows.h>
#include <processthreadsapi.h>
#elif PLATFORM_LINUX
#include <pthread.h>
#elif PLATFORM_MACOS
#include <pthread.h>
#endif
void set_thread_name(const char* name) {
#if PLATFORM_WINDOWS
SetThreadDescription(GetCurrentThread(), std::wstring(name, name + strlen(name)).c_str());
#elif PLATFORM_LINUX
pthread_setname_np(pthread_self(), name);
#elif PLATFORM_MACOS
pthread_setname_np(name);
#endif
}