ardour
session_events.cc
Go to the documentation of this file.
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
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 
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
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
117 {
118  SessionEvent* ev = new SessionEvent (type, SessionEvent::Add, frame, target_frame, 0);
119  queue_event (ev);
120 }
121 
122 void
124 {
125  SessionEvent* ev = new SessionEvent (type, SessionEvent::Remove, frame, 0, 0);
126  queue_event (ev);
127 }
128 
129 void
131 {
132  SessionEvent* ev = new SessionEvent (type, SessionEvent::Replace, frame, target, 0);
133  queue_event (ev);
134 }
135 
136 void
138 {
140  queue_event (ev);
141 }
142 
143 void
144 SessionEventManager::clear_events (SessionEvent::Type type, boost::function<void (void)> after)
145 {
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 
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
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
188 {
189  switch (ev->action) {
191  _remove_event (ev);
192  delete ev;
193  return;
194 
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 
220  process_event (ev);
221  return;
222  }
223 
224  switch (ev->type) {
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);
243  next_event = events.begin();
244  set_next_event ();
245 }
246 
248 bool
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 
273  next_event = events.end();
274  set_next_event ();
275 
276  return ret;
277 }
278 
280 bool
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
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 
LIBPBD_API const char * pthread_name()
void clear_events(SessionEvent::Type type)
PBD::EventLoop * event_loop
guint used() const
Definition: pool.h:45
void push(void *)
Definition: pool.cc:262
CrossThreadPool * event_pool() const
#define enum_2_string(e)
Definition: enumwriter.h:97
boost::function< void(void)> rt_slot
framepos_t action_frame
Definition: session_event.h:80
void merge_event(SessionEvent *)
Definition: Beats.hpp:239
LIBPBD_API Transmitter error
RTeventCallback rt_return
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
static EventLoop * get_event_loop_for_thread()
Definition: event_loop.cc:31
void _clear_event_type(SessionEvent::Type)
framepos_t target_frame
Definition: session_event.h:81
guint pending_size() const
Definition: pool.h:110
CrossThreadPool * own_pool
bool _replace_event(SessionEvent *)
LIBARDOUR_API uint64_t SessionEvents
Definition: debug.cc:43
virtual void queue_event(SessionEvent *ev)=0
#define _(Text)
Definition: i18n.h:11
virtual void release(void *)
Definition: pool.cc:82
virtual void set_next_event()=0
static const framepos_t Immediate
Definition: amp.h:29
class LIBPBD_API PerThreadPool
Definition: pool.h:83
Events::iterator next_event
Definition: pool.h:34
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
int64_t framepos_t
Definition: types.h:66
void * alloc()
Definition: pool.cc:253
LIBPBD_API uint64_t debug_bits
Definition: debug.cc:55
const char * name
std::string name() const
Definition: pool.h:43
void replace_event(SessionEvent::Type, framepos_t action_frame, framepos_t target=0)
guint available() const
Definition: pool.h:44
virtual void call_slot(InvalidationRecord *, const boost::function< void()> &)=0
Definition: debug.h:30
guint total() const
Definition: pool.h:46
#define MISSING_INVALIDATOR
Definition: event_loop.h:86
void remove_event(framepos_t frame, SessionEvent::Type type)
static bool compare(const SessionEvent *e1, const SessionEvent *e2)
void flush_pending_with_ev(void *)
Definition: pool.cc:226
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
bool _remove_event(SessionEvent *)
void add_event(framepos_t action_frame, SessionEvent::Type type, framepos_t target_frame=0)
virtual void process_event(SessionEvent *)=0