Add a RT-Tasklist

This commit is contained in:
Robin Gareus 2017-10-30 05:52:18 +01:00
parent c0dbe8eaaf
commit fba0fce441
3 changed files with 239 additions and 0 deletions

View File

@ -0,0 +1,67 @@
/*
* Copyright (C) 2017 Robin Gareus <robin@gareus.org>
*
* 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 <list>
#include <boost/function.hpp>
#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<boost::function<void ()> > TaskList;
/** process tasks in list in parallel, wait for them to complete */
void process (TaskList const&);
private:
gint _threads_active;
std::vector<pthread_t> _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

171
libs/ardour/rt_tasklist.cc Normal file
View File

@ -0,0 +1,171 @@
/*
* Copyright (C) 2017 Robin Gareus <robin@gareus.org>
*
* 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<pthread_t>::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<RTTaskList *>(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<void ()> 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 ();
}
}

View File

@ -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',