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