fix crash when copy'ing latent plugins
[ardour.git] / libs / ardour / session_events.cc
1 /*
2     Copyright (C) 1999-2004 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 <cmath>
21 #include <unistd.h>
22
23 #include "pbd/error.h"
24 #include "pbd/enumwriter.h"
25 #include "pbd/stacktrace.h"
26 #include "pbd/pthread_utils.h"
27
28 #include "ardour/debug.h"
29 #include "ardour/session_event.h"
30
31 #include "pbd/i18n.h"
32
33 using namespace std;
34 using namespace ARDOUR;
35 using namespace PBD;
36
37 PerThreadPool* SessionEvent::pool;
38
39 void
40 SessionEvent::init_event_pool ()
41 {
42         pool = new PerThreadPool;
43 }
44
45 bool
46 SessionEvent::has_per_thread_pool ()
47 {
48         return pool->has_per_thread_pool ();
49 }
50
51 void
52 SessionEvent::create_per_thread_pool (const std::string& name, uint32_t nitems)
53 {
54         /* this is a per-thread call that simply creates a thread-private ptr to
55            a CrossThreadPool for use by this thread whenever events are allocated/released
56            from SessionEvent::pool()
57         */
58         pool->create_per_thread_pool (name, sizeof (SessionEvent), nitems);
59 }
60
61 SessionEvent::SessionEvent (Type t, Action a, framepos_t when, framepos_t where, double spd, bool yn, bool yn2, bool yn3)
62         : type (t)
63         , action (a)
64         , action_frame (when)
65         , target_frame (where)
66         , speed (spd)
67         , yes_or_no (yn)
68         , second_yes_or_no (yn2)
69         , third_yes_or_no (yn3)
70         , event_loop (0)
71 {
72         DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("NEW SESSION EVENT, type = %1 action = %2\n", enum_2_string (type), enum_2_string (action)));
73 }
74
75 void *
76 SessionEvent::operator new (size_t)
77 {
78         CrossThreadPool* p = pool->per_thread_pool ();
79         SessionEvent* ev = static_cast<SessionEvent*> (p->alloc ());
80         DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("%1 Allocating SessionEvent from %2 ev @ %3 pool size %4 free %5 used %6\n", pthread_name(), p->name(), ev,
81                                                            p->total(), p->available(), p->used()));
82
83         ev->own_pool = p;
84         return ev;
85 }
86
87 void
88 SessionEvent::operator delete (void *ptr, size_t /*size*/)
89 {
90         Pool* p = pool->per_thread_pool (false);
91         SessionEvent* ev = static_cast<SessionEvent*> (ptr);
92
93         DEBUG_TRACE (DEBUG::SessionEvents, string_compose (
94                              "%1 Deleting SessionEvent @ %2 type %3 action %4 ev thread pool = %5 ev pool = %6 size %7 free %8 used %9\n",
95                              pthread_name(), ev, enum_2_string (ev->type), enum_2_string (ev->action), p->name(), ev->own_pool->name(), ev->own_pool->total(), ev->own_pool->available(), ev->own_pool->used()
96                              ));
97
98         if (p && p == ev->own_pool) {
99                 p->release (ptr);
100         } else {
101                 assert(ev->own_pool);
102                 ev->own_pool->push (ev);
103                 DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("%1 was wrong thread for this pool, pushed event onto pending list, will be deleted on next alloc from %2 pool size %3 free %4 used %5 pending %6\n",
104                                                                    pthread_name(), ev->own_pool->name(),
105                                                                    ev->own_pool->total(), ev->own_pool->available(), ev->own_pool->used(),
106                                                                    ev->own_pool->pending_size()));
107         }
108 }
109
110 void
111 SessionEventManager::add_event (framepos_t frame, SessionEvent::Type type, framepos_t target_frame)
112 {
113         SessionEvent* ev = new SessionEvent (type, SessionEvent::Add, frame, target_frame, 0);
114         queue_event (ev);
115 }
116
117 void
118 SessionEventManager::remove_event (framepos_t frame, SessionEvent::Type type)
119 {
120         SessionEvent* ev = new SessionEvent (type, SessionEvent::Remove, frame, 0, 0);
121         queue_event (ev);
122 }
123
124 void
125 SessionEventManager::replace_event (SessionEvent::Type type, framepos_t frame, framepos_t target)
126 {
127         SessionEvent* ev = new SessionEvent (type, SessionEvent::Replace, frame, target, 0);
128         queue_event (ev);
129 }
130
131 void
132 SessionEventManager::clear_events (SessionEvent::Type type)
133 {
134         SessionEvent* ev = new SessionEvent (type, SessionEvent::Clear, SessionEvent::Immediate, 0, 0);
135         queue_event (ev);
136 }
137
138 void
139 SessionEventManager::clear_events (SessionEvent::Type type, boost::function<void (void)> after)
140 {
141         SessionEvent* ev = new SessionEvent (type, SessionEvent::Clear, SessionEvent::Immediate, 0, 0);
142         ev->rt_slot = after;
143
144         /* in the calling thread, after the clear is complete, arrange to flush things from the event
145            pool pending list (i.e. to make sure they are really back in the free list and available
146            for future events).
147         */
148
149         ev->event_loop = PBD::EventLoop::get_event_loop_for_thread ();
150         if (ev->event_loop) {
151                 ev->rt_return = boost::bind (&CrossThreadPool::flush_pending_with_ev, ev->event_pool(), _1);
152         }
153
154         queue_event (ev);
155 }
156
157 void
158 SessionEventManager::dump_events () const
159 {
160         cerr << "EVENT DUMP" << endl;
161         for (Events::const_iterator i = events.begin(); i != events.end(); ++i) {
162
163                 cerr << "\tat " << (*i)->action_frame << ' ' << enum_2_string ((*i)->type) << " target = " << (*i)->target_frame << endl;
164         }
165         cerr << "Next event: ";
166
167         if ((Events::const_iterator) next_event == events.end()) {
168                 cerr << "none" << endl;
169         } else {
170                 cerr << "at " << (*next_event)->action_frame << ' '
171                      << enum_2_string ((*next_event)->type) << " target = "
172                      << (*next_event)->target_frame << endl;
173         }
174         cerr << "Immediate events pending:\n";
175         for (Events::const_iterator i = immediate_events.begin(); i != immediate_events.end(); ++i) {
176                 cerr << "\tat " << (*i)->action_frame << ' ' << enum_2_string((*i)->type) << " target = " << (*i)->target_frame << endl;
177         }
178         cerr << "END EVENT_DUMP" << endl;
179 }
180
181 void
182 SessionEventManager::merge_event (SessionEvent* ev)
183 {
184         switch (ev->action) {
185         case SessionEvent::Remove:
186                 _remove_event (ev);
187                 delete ev;
188                 return;
189
190         case SessionEvent::Replace:
191                 _replace_event (ev);
192                 return;
193
194         case SessionEvent::Clear:
195                 _clear_event_type (ev->type);
196                 /* run any additional realtime callback, if any */
197                 if (ev->rt_slot) {
198                         ev->rt_slot ();
199                 }
200                 if (ev->event_loop) {
201                         /* run non-realtime callback (in some other thread) */
202                         ev->event_loop->call_slot (MISSING_INVALIDATOR, boost::bind (ev->rt_return, ev));
203                 } else {
204                         delete ev;
205                 }
206                 return;
207
208         case SessionEvent::Add:
209                 break;
210         }
211
212         /* try to handle immediate events right here */
213
214         if (ev->action_frame == SessionEvent::Immediate) {
215                 process_event (ev);
216                 return;
217         }
218
219         switch (ev->type) {
220         case SessionEvent::AutoLoop:
221         case SessionEvent::AutoLoopDeclick:
222         case SessionEvent::StopOnce:
223                 _clear_event_type (ev->type);
224                 break;
225
226         default:
227                 for (Events::iterator i = events.begin(); i != events.end(); ++i) {
228                         if ((*i)->type == ev->type && (*i)->action_frame == ev->action_frame) {
229                           error << string_compose(_("Session: cannot have two events of type %1 at the same frame (%2)."),
230                                                   enum_2_string (ev->type), ev->action_frame) << endmsg;
231                                 return;
232                         }
233                 }
234         }
235
236         events.insert (events.begin(), ev);
237         events.sort (SessionEvent::compare);
238         next_event = events.begin();
239         set_next_event ();
240 }
241
242 /** @return true when @a ev is deleted. */
243 bool
244 SessionEventManager::_replace_event (SessionEvent* ev)
245 {
246         bool ret = false;
247         Events::iterator i;
248
249         /* private, used only for events that can only exist once in the queue */
250
251         for (i = events.begin(); i != events.end(); ++i) {
252                 if ((*i)->type == ev->type) {
253                         (*i)->action_frame = ev->action_frame;
254                         (*i)->target_frame = ev->target_frame;
255                         if ((*i) == ev) {
256                                 ret = true;
257                         }
258                         delete ev;
259                         break;
260                 }
261         }
262
263         if (i == events.end()) {
264                 events.insert (events.begin(), ev);
265         }
266
267         events.sort (SessionEvent::compare);
268         next_event = events.end();
269         set_next_event ();
270
271         return ret;
272 }
273
274 /** @return true when @a ev is deleted. */
275 bool
276 SessionEventManager::_remove_event (SessionEvent* ev)
277 {
278         bool ret = false;
279         Events::iterator i;
280
281         for (i = events.begin(); i != events.end(); ++i) {
282                 if ((*i)->type == ev->type && (*i)->action_frame == ev->action_frame) {
283                         if ((*i) == ev) {
284                                 ret = true;
285                         }
286
287                         delete *i;
288                         if (i == next_event) {
289                                 ++next_event;
290                         }
291                         i = events.erase (i);
292                         break;
293                 }
294         }
295
296         if (i != events.end()) {
297                 set_next_event ();
298         }
299
300         return ret;
301 }
302
303 void
304 SessionEventManager::_clear_event_type (SessionEvent::Type type)
305 {
306         Events::iterator i, tmp;
307
308         for (i = events.begin(); i != events.end(); ) {
309
310                 tmp = i;
311                 ++tmp;
312
313                 if ((*i)->type == type) {
314                         delete *i;
315                         if (i == next_event) {
316                                 ++next_event;
317                         }
318                         events.erase (i);
319                 }
320
321                 i = tmp;
322         }
323
324         for (i = immediate_events.begin(); i != immediate_events.end(); ) {
325
326                 tmp = i;
327                 ++tmp;
328
329                 if ((*i)->type == type) {
330                         delete *i;
331                         immediate_events.erase (i);
332                 }
333
334                 i = tmp;
335         }
336
337         set_next_event ();
338 }
339