MSVC project changes needed to support the new 'mp3 import' stuff
[ardour.git] / libs / pbd / event_loop.cc
1 /*
2  * Copyright (C) 2009-2018 Paul Davis <paul@linuxaudiosystems.com>
3  * Copyright (C) 2010 Carl Hetherington <carl@carlh.net>
4  * Copyright (C) 2015-2017 Robin Gareus <robin@gareus.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20
21 #include <cstring>
22
23 #include <pthread.h>
24
25 #include "pbd/compose.h"
26 #include "pbd/debug.h"
27 #include "pbd/event_loop.h"
28 #include "pbd/error.h"
29 #include "pbd/pthread_utils.h"
30 #include "pbd/stacktrace.h"
31
32 #include "pbd/i18n.h"
33
34 using namespace PBD;
35 using namespace std;
36
37 static void do_not_delete_the_loop_pointer (void*) { }
38
39 Glib::Threads::Private<EventLoop> EventLoop::thread_event_loop (do_not_delete_the_loop_pointer);
40
41 Glib::Threads::RWLock EventLoop::thread_buffer_requests_lock;
42 EventLoop::ThreadRequestBufferList EventLoop::thread_buffer_requests;
43 EventLoop::RequestBufferSuppliers EventLoop::request_buffer_suppliers;
44
45 EventLoop::EventLoop (string const& name)
46         : _name (name)
47 {
48 }
49
50 EventLoop::~EventLoop ()
51 {
52         trash.sort();
53         trash.unique();
54         for (std::list<InvalidationRecord*>::iterator r = trash.begin(); r != trash.end(); ++r) {
55                 if (!(*r)->in_use ()) {
56                         delete *r;
57                 }
58         }
59         trash.clear ();
60 }
61
62 EventLoop*
63 EventLoop::get_event_loop_for_thread()
64 {
65         return thread_event_loop.get ();
66 }
67
68 void
69 EventLoop::set_event_loop_for_thread (EventLoop* loop)
70 {
71         thread_event_loop.set (loop);
72 }
73
74 void*
75 EventLoop::invalidate_request (void* data)
76 {
77         InvalidationRecord* ir = (InvalidationRecord*) data;
78
79         /* Some of the requests queued with an EventLoop may involve functors
80          * that make method calls to objects whose lifetime is shorter
81          * than the EventLoop's. We do not want to make those calls if the
82          * object involve has been destroyed. To prevent this, we
83          * provide a way to invalidate those requests when the object is
84          * destroyed.
85          *
86          * An object was passed to __invalidator() which added a callback to
87          * EventLoop::invalidate_request() to its "notify when destroyed"
88          * list. __invalidator() returned an InvalidationRecord that has been
89          * to passed to this function as data.
90          *
91          * The object is currently being destroyed and so we want to
92          * mark all requests involving this object that are queued with
93          * any EventLoop as invalid.
94          *
95          * As of April 2012, we are usign sigc::trackable as the base object
96          * used to queue calls to ::invalidate_request() to be made upon
97          * destruction, via its ::add_destroy_notify_callback() API. This is
98          * not necessarily ideal, but it is very close to precisely what we
99          * want, and many of the objects we want to do this with already
100          * inherit (indirectly) from sigc::trackable.
101          */
102
103         if (ir->event_loop) {
104                 DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("%1: invalidating request from %2 (%3) @ %4\n", pthread_name(), ir->event_loop, ir->event_loop->event_loop_name(), ir));
105                 Glib::Threads::Mutex::Lock lm (ir->event_loop->slot_invalidation_mutex());
106                 ir->invalidate ();
107                 ir->event_loop->trash.push_back(ir);
108         }
109
110         return 0;
111 }
112
113 vector<EventLoop::ThreadBufferMapping>
114 EventLoop::get_request_buffers_for_target_thread (const std::string& target_thread)
115 {
116         vector<ThreadBufferMapping> ret;
117         Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
118
119         for (ThreadRequestBufferList::const_iterator x = thread_buffer_requests.begin();
120              x != thread_buffer_requests.end(); ++x) {
121
122                 if (x->second.target_thread_name == target_thread) {
123                         ret.push_back (x->second);
124                 }
125         }
126
127         DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("for thread \"%1\", found %2 request buffers\n", target_thread, ret.size()));
128
129         return ret;
130 }
131
132 void
133 EventLoop::register_request_buffer_factory (const string& target_thread_name,
134                                             void* (*factory)(uint32_t))
135 {
136
137         RequestBufferSupplier trs;
138         trs.name = target_thread_name;
139         trs.factory = factory;
140
141         {
142                 Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
143                 request_buffer_suppliers.push_back (trs);
144         }
145 }
146
147 void
148 EventLoop::pre_register (const string& emitting_thread_name, uint32_t num_requests)
149 {
150         /* Threads that need to emit signals "towards" other threads, but with
151            RT safe behavior may be created before the receiving threads
152            exist. This makes it impossible for them to use the
153            ThreadCreatedWithRequestSize signal to notify receiving threads of
154            their existence.
155
156            This function creates a request buffer for them to use with
157            the (not yet) created threads, and stores it where the receiving
158            thread can find it later.
159          */
160
161         ThreadBufferMapping mapping;
162         Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
163
164         for (RequestBufferSuppliers::iterator trs = request_buffer_suppliers.begin(); trs != request_buffer_suppliers.end(); ++trs) {
165
166                 if (!trs->factory) {
167                         /* no factory - no request buffer required or expected */
168                         continue;
169                 }
170
171                 if (emitting_thread_name == trs->name) {
172                         /* no need to register an emitter with itself */
173                         continue;
174                 }
175
176                 mapping.emitting_thread = pthread_self();
177                 mapping.target_thread_name = trs->name;
178
179                 /* Allocate a suitably sized request buffer. This will set the
180                  * thread-local variable that holds a pointer to this request
181                  * buffer.
182                  */
183                 mapping.request_buffer = trs->factory (num_requests);
184
185                 /* now store it where the receiving thread (trs->name) can find
186                    it if and when it is created. (Discovery happens in the
187                    AbstractUI constructor. Note that if
188                 */
189
190                 const string key = string_compose ("%1/%2", emitting_thread_name, mapping.target_thread_name);
191
192                 /* management of the thread_request_buffers map works as
193                  * follows:
194                  *
195                  * when the factory method was called above, the pointer to the
196                  * created buffer is set as a thread-local-storage (TLS) value
197                  * for this (the emitting) thread.
198                  *
199                  * The TLS value is set up with a destructor that marks the
200                  * request buffer as "dead" when the emitting thread exits.
201                  *
202                  * An entry will remain in the map after the thread exits.
203                  *
204                  * The receiving thread may (if it receives requests from other
205                  * threads) notice the dead buffer. If it does, it will delete
206                  * the request buffer, and call
207                  * ::remove_request_buffer_from_map() to get rid of it from the map.
208                  *
209                  * This does mean that the lifetime of the request buffer is
210                  * indeterminate: if the receiving thread were to receive no
211                  * further requests, the request buffer will live on
212                  * forever. But this is OK, because if there are no requests
213                  * arriving, the receiving thread is not attempting to use the
214                  * request buffer(s) in any way.
215                  *
216                  * Note, however, that *if* an emitting thread is recreated
217                  * with the same name (e.g. when a control surface is
218                  * enabled/disabled/enabled), then the request buffer for the
219                  * new thread will replace the map entry for the key, because
220                  * of the matching thread names. This does mean that
221                  * potentially the request buffer can leak in this case, but
222                  * (a) these buffers are not really that large anyway (b) the
223                  * scenario is not particularly common (c) the buffers would
224                  * typically last across a session instance if not program
225                  * lifetime anyway.
226                  */
227
228                 thread_buffer_requests[key] = mapping;
229                 DEBUG_TRACE (PBD::DEBUG::EventLoop, string_compose ("pre-registered request buffer for \"%1\" to send to \"%2\", buffer @ %3 (key was %4)\n",
230                                                                     emitting_thread_name, trs->name, mapping.request_buffer, key));
231         }
232 }
233
234 void
235 EventLoop::remove_request_buffer_from_map (void* ptr)
236 {
237         Glib::Threads::RWLock::WriterLock lm (thread_buffer_requests_lock);
238
239         for (ThreadRequestBufferList::iterator x = thread_buffer_requests.begin(); x != thread_buffer_requests.end(); ++x) {
240                 if (x->second.request_buffer == ptr) {
241                         thread_buffer_requests.erase (x);
242                         break;
243                 }
244         }
245 }