/* * 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 (); } }