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