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