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