Optimize automation-event process splitting
[ardour.git] / libs / ardour / rt_tasklist.cc
1 /*
2  * Copyright (C) 2017 Robin Gareus <robin@gareus.org>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18
19
20 #include "pbd/pthread_utils.h"
21
22 #include "ardour/audioengine.h"
23 #include "ardour/debug.h"
24 #include "ardour/rt_tasklist.h"
25 #include "ardour/utils.h"
26
27 #include "pbd/i18n.h"
28
29 using namespace ARDOUR;
30
31 RTTaskList::RTTaskList ()
32         : _threads_active (0)
33         , _task_run_sem ("rt_task_run", 0)
34         , _task_end_sem ("rt_task_done", 0)
35 {
36         reset_thread_list ();
37 }
38
39 RTTaskList::~RTTaskList ()
40 {
41         drop_threads ();
42 }
43
44 void
45 RTTaskList::drop_threads ()
46 {
47         Glib::Threads::Mutex::Lock pm (_process_mutex);
48         g_atomic_int_set (&_threads_active, 0);
49
50         uint32_t nt = _threads.size ();
51         for (uint32_t i = 0; i < nt; ++i) {
52                 _task_run_sem.signal ();
53         }
54         for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i)
55         {
56                 pthread_join (*i, NULL);
57         }
58         _threads.clear ();
59         _task_run_sem.reset ();
60         _task_end_sem.reset ();
61 }
62
63 /*static*/ void*
64 RTTaskList::_thread_run (void *arg)
65 {
66         RTTaskList *d = static_cast<RTTaskList *>(arg);
67         d->run ();
68         pthread_exit (0);
69         return 0;
70 }
71
72 void
73 RTTaskList::reset_thread_list ()
74 {
75         drop_threads ();
76
77         const uint32_t num_threads = how_many_dsp_threads ();
78         if (num_threads < 2) {
79                 return;
80         }
81
82         Glib::Threads::Mutex::Lock pm (_process_mutex);
83
84         g_atomic_int_set (&_threads_active, 1);
85         for (uint32_t i = 0; i < num_threads; ++i) {
86                 pthread_t thread_id;
87                 size_t stacksize = 100000;
88                 if (!AudioEngine::instance()->is_realtime ()
89                     ||
90                     pbd_realtime_pthread_create (PBD_SCHED_FIFO, AudioEngine::instance()->client_real_time_priority(), stacksize, &thread_id, _thread_run, this)) {
91                         pthread_attr_t attr;
92                         pthread_attr_init (&attr);
93                         pthread_attr_setstacksize (&attr, stacksize);
94                         if (pthread_create (&thread_id, &attr, _thread_run, this)) {
95                                 PBD::fatal << _("Cannot create thread for TaskList!") << endmsg;
96                                 /* NOT REACHED */
97                         }
98                         pthread_attr_destroy (&attr);
99                 }
100                 pbd_mach_set_realtime_policy (thread_id, 5. * 1e-5);
101                 _threads.push_back (thread_id);
102         }
103 }
104
105 void
106 RTTaskList::run ()
107 {
108         Glib::Threads::Mutex::Lock tm (_tasklist_mutex, Glib::Threads::NOT_LOCK);
109         bool wait = true;
110
111         while (true) {
112                 if (wait) {
113                         _task_run_sem.wait ();
114                 }
115
116                 if (0 == g_atomic_int_get (&_threads_active)) {
117                         _task_end_sem.signal ();
118                         break;
119                 }
120
121                 wait = false;
122
123                 boost::function<void ()> to_run;
124                 tm.acquire ();
125                 if (_tasklist.size () > 0) {
126                         to_run = _tasklist.front();
127                         _tasklist.pop_front ();
128                 }
129                 tm.release ();
130
131                 if (!to_run.empty ()) {
132                         to_run ();
133                         continue;
134                 }
135
136                 if (!wait) {
137                         _task_end_sem.signal ();
138                 }
139
140                 wait = true;
141         }
142 }
143
144 void
145 RTTaskList::process (TaskList const& tl)
146 {
147         Glib::Threads::Mutex::Lock pm (_process_mutex);
148         _tasklist = tl;
149         process_tasklist ();
150         _tasklist.clear ();
151 }
152
153 void
154 RTTaskList::process_tasklist ()
155 {
156         if (0 == g_atomic_int_get (&_threads_active) || _threads.size () == 0) {
157                 for (TaskList::iterator i = _tasklist.begin (); i != _tasklist.end(); ++i) {
158                         (*i)();
159                 }
160                 return;
161         }
162
163         uint32_t nt = std::min (_threads.size (), _tasklist.size ());
164
165         for (uint32_t i = 0; i < nt; ++i) {
166                 _task_run_sem.signal ();
167         }
168         for (uint32_t i = 0; i < nt; ++i) {
169                 _task_end_sem.wait ();
170         }
171 }