From fba0fce4416578de29b7701cf4d400399d44ceff Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 30 Oct 2017 05:52:18 +0100 Subject: [PATCH] Add a RT-Tasklist --- libs/ardour/ardour/rt_tasklist.h | 67 ++++++++++++ libs/ardour/rt_tasklist.cc | 171 +++++++++++++++++++++++++++++++ libs/ardour/wscript | 1 + 3 files changed, 239 insertions(+) create mode 100644 libs/ardour/ardour/rt_tasklist.h create mode 100644 libs/ardour/rt_tasklist.cc diff --git a/libs/ardour/ardour/rt_tasklist.h b/libs/ardour/ardour/rt_tasklist.h new file mode 100644 index 0000000000..2924705cf3 --- /dev/null +++ b/libs/ardour/ardour/rt_tasklist.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2017 Robin Gareus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _ardour_rt_tasklist_h_ +#define _ardour_rt_tasklist_h_ + +#include +#include + +#include "pbd/semutils.h" + +#include "ardour/libardour_visibility.h" +#include "ardour/types.h" +#include "ardour/audio_backend.h" +#include "ardour/session_handle.h" + +namespace ARDOUR { + +class LIBARDOUR_API RTTaskList +{ +public: + RTTaskList (); + ~RTTaskList (); + + // TODO use dedicated allocator of a boost::intrusive::list + typedef std::list > TaskList; + + /** process tasks in list in parallel, wait for them to complete */ + void process (TaskList const&); + +private: + gint _threads_active; + std::vector _threads; + + void reset_thread_list (); + void drop_threads (); + + void process_tasklist (); + + static void* _thread_run (void *arg); + void run (); + + Glib::Threads::Mutex _process_mutex; + Glib::Threads::Mutex _tasklist_mutex; + PBD::Semaphore _task_run_sem; + PBD::Semaphore _task_end_sem; + + TaskList _tasklist; +}; + +} // namespace ARDOUR +#endif diff --git a/libs/ardour/rt_tasklist.cc b/libs/ardour/rt_tasklist.cc new file mode 100644 index 0000000000..db2a33015c --- /dev/null +++ b/libs/ardour/rt_tasklist.cc @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2017 Robin Gareus + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#include "pbd/pthread_utils.h" + +#include "ardour/audioengine.h" +#include "ardour/debug.h" +#include "ardour/rt_tasklist.h" +#include "ardour/utils.h" + +#include "pbd/i18n.h" + +using namespace ARDOUR; + +RTTaskList::RTTaskList () + : _threads_active (0) + , _task_run_sem ("rt_task_run", 0) + , _task_end_sem ("rt_task_done", 0) +{ + reset_thread_list (); +} + +RTTaskList::~RTTaskList () +{ + drop_threads (); +} + +void +RTTaskList::drop_threads () +{ + Glib::Threads::Mutex::Lock pm (_process_mutex); + g_atomic_int_set (&_threads_active, 0); + + uint32_t nt = _threads.size (); + for (uint32_t i = 0; i < nt; ++i) { + _task_run_sem.signal (); + } + for (std::vector::const_iterator i = _threads.begin (); i != _threads.end (); ++i) + { + pthread_join (*i, NULL); + } + _threads.clear (); + _task_run_sem.reset (); + _task_end_sem.reset (); +} + +/*static*/ void* +RTTaskList::_thread_run (void *arg) +{ + RTTaskList *d = static_cast(arg); + d->run (); + pthread_exit (0); + return 0; +} + +void +RTTaskList::reset_thread_list () +{ + drop_threads (); + + const uint32_t num_threads = how_many_dsp_threads (); + if (num_threads < 2) { + return; + } + + Glib::Threads::Mutex::Lock pm (_process_mutex); + + g_atomic_int_set (&_threads_active, 1); + for (uint32_t i = 0; i < num_threads; ++i) { + pthread_t thread_id; + size_t stacksize = 100000; + if (!AudioEngine::instance()->is_realtime () + || + pbd_realtime_pthread_create (PBD_SCHED_FIFO, -22, stacksize, &thread_id, _thread_run, this)) { + pthread_attr_t attr; + pthread_attr_init (&attr); + pthread_attr_setstacksize (&attr, stacksize); + if (pthread_create (&thread_id, &attr, _thread_run, this)) { + PBD::fatal << _("Cannot create thread for TaskList!") << endmsg; + /* NOT REACHED */ + } + pthread_attr_destroy (&attr); + } + pbd_mach_set_realtime_policy (thread_id, 5. * 1e-5); + _threads.push_back (thread_id); + } +} + +void +RTTaskList::run () +{ + Glib::Threads::Mutex::Lock tm (_tasklist_mutex, Glib::Threads::NOT_LOCK); + bool wait = true; + + while (true) { + if (wait) { + _task_run_sem.wait (); + } + + if (0 == g_atomic_int_get (&_threads_active)) { + _task_end_sem.signal (); + break; + } + + wait = false; + + boost::function to_run; + tm.acquire (); + if (_tasklist.size () > 0) { + to_run = _tasklist.front(); + _tasklist.pop_front (); + } + tm.release (); + + if (!to_run.empty ()) { + to_run (); + continue; + } + + if (!wait) { + _task_end_sem.signal (); + } + + wait = true; + } +} + +void +RTTaskList::process (TaskList const& tl) +{ + Glib::Threads::Mutex::Lock pm (_process_mutex); + _tasklist = tl; + process_tasklist (); + _tasklist.clear (); +} + +void +RTTaskList::process_tasklist () +{ + if (0 == g_atomic_int_get (&_threads_active) || _threads.size () == 0) { + for (TaskList::iterator i = _tasklist.begin (); i != _tasklist.end(); ++i) { + (*i)(); + } + return; + } + + uint32_t nt = std::min (_threads.size (), _tasklist.size ()); + + for (uint32_t i = 0; i < nt; ++i) { + _task_run_sem.signal (); + } + for (uint32_t i = 0; i < nt; ++i) { + _task_end_sem.wait (); + } +} diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 1604803884..f5d30f147b 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -192,6 +192,7 @@ libardour_sources = [ 'route_group.cc', 'route_group_member.cc', 'rb_effect.cc', + 'rt_tasklist.cc', 'scene_change.cc', 'search_paths.cc', 'selection.cc',