2 * Copyright (C) 1999-2017 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2006-2012 David Robillard <d@drobilla.net>
4 * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
5 * Copyright (C) 2015-2018 Robin Gareus <robin@gareus.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 #include "pbd/error.h"
26 #include "pbd/enumwriter.h"
27 #include "pbd/stacktrace.h"
28 #include "pbd/pthread_utils.h"
30 #include "ardour/debug.h"
31 #include "ardour/session_event.h"
36 using namespace ARDOUR;
39 PerThreadPool* SessionEvent::pool;
42 SessionEvent::init_event_pool ()
44 pool = new PerThreadPool;
48 SessionEvent::has_per_thread_pool ()
50 return pool->has_per_thread_pool ();
54 SessionEvent::create_per_thread_pool (const std::string& name, uint32_t nitems)
56 /* this is a per-thread call that simply creates a thread-private ptr to
57 a CrossThreadPool for use by this thread whenever events are allocated/released
58 from SessionEvent::pool()
60 pool->create_per_thread_pool (name, sizeof (SessionEvent), nitems);
63 SessionEvent::SessionEvent (Type t, Action a, samplepos_t when, samplepos_t where, double spd, bool yn, bool yn2, bool yn3)
66 , action_sample (when)
67 , target_sample (where)
70 , second_yes_or_no (yn2)
71 , third_yes_or_no (yn3)
74 DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("NEW SESSION EVENT, type = %1 action = %2\n", enum_2_string (type), enum_2_string (action)));
78 SessionEvent::operator new (size_t)
80 CrossThreadPool* p = pool->per_thread_pool ();
81 SessionEvent* ev = static_cast<SessionEvent*> (p->alloc ());
82 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,
83 p->total(), p->available(), p->used()));
90 SessionEvent::operator delete (void *ptr, size_t /*size*/)
92 Pool* p = pool->per_thread_pool (false);
93 SessionEvent* ev = static_cast<SessionEvent*> (ptr);
95 DEBUG_TRACE (DEBUG::SessionEvents, string_compose (
96 "%1 Deleting SessionEvent @ %2 type %3 action %4 ev thread pool = %5 ev pool = %6 size %7 free %8 used %9\n",
97 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()
100 if (p && p == ev->own_pool) {
103 assert(ev->own_pool);
104 ev->own_pool->push (ev);
105 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",
106 pthread_name(), ev->own_pool->name(),
107 ev->own_pool->total(), ev->own_pool->available(), ev->own_pool->used(),
108 ev->own_pool->pending_size()));
113 SessionEventManager::add_event (samplepos_t sample, SessionEvent::Type type, samplepos_t target_sample)
115 SessionEvent* ev = new SessionEvent (type, SessionEvent::Add, sample, target_sample, 0);
120 SessionEventManager::remove_event (samplepos_t sample, SessionEvent::Type type)
122 SessionEvent* ev = new SessionEvent (type, SessionEvent::Remove, sample, 0, 0);
127 SessionEventManager::replace_event (SessionEvent::Type type, samplepos_t sample, samplepos_t target)
129 SessionEvent* ev = new SessionEvent (type, SessionEvent::Replace, sample, target, 0);
134 SessionEventManager::clear_events (SessionEvent::Type type)
136 SessionEvent* ev = new SessionEvent (type, SessionEvent::Clear, SessionEvent::Immediate, 0, 0);
141 SessionEventManager::clear_events (SessionEvent::Type type, boost::function<void (void)> after)
143 SessionEvent* ev = new SessionEvent (type, SessionEvent::Clear, SessionEvent::Immediate, 0, 0);
146 /* in the calling thread, after the clear is complete, arrange to flush things from the event
147 pool pending list (i.e. to make sure they are really back in the free list and available
151 ev->event_loop = PBD::EventLoop::get_event_loop_for_thread ();
152 if (ev->event_loop) {
153 ev->rt_return = boost::bind (&CrossThreadPool::flush_pending_with_ev, ev->event_pool(), _1);
160 SessionEventManager::dump_events () const
162 cerr << "EVENT DUMP" << endl;
163 for (Events::const_iterator i = events.begin(); i != events.end(); ++i) {
165 cerr << "\tat " << (*i)->action_sample << ' ' << enum_2_string ((*i)->type) << " target = " << (*i)->target_sample << endl;
167 cerr << "Next event: ";
169 if ((Events::const_iterator) next_event == events.end()) {
170 cerr << "none" << endl;
172 cerr << "at " << (*next_event)->action_sample << ' '
173 << enum_2_string ((*next_event)->type) << " target = "
174 << (*next_event)->target_sample << endl;
176 cerr << "Immediate events pending:\n";
177 for (Events::const_iterator i = immediate_events.begin(); i != immediate_events.end(); ++i) {
178 cerr << "\tat " << (*i)->action_sample << ' ' << enum_2_string((*i)->type) << " target = " << (*i)->target_sample << endl;
180 cerr << "END EVENT_DUMP" << endl;
184 SessionEventManager::merge_event (SessionEvent* ev)
186 switch (ev->action) {
187 case SessionEvent::Remove:
192 case SessionEvent::Replace:
196 case SessionEvent::Clear:
197 _clear_event_type (ev->type);
198 /* run any additional realtime callback, if any */
202 if (ev->event_loop) {
203 /* run non-realtime callback (in some other thread) */
204 ev->event_loop->call_slot (MISSING_INVALIDATOR, boost::bind (ev->rt_return, ev));
210 case SessionEvent::Add:
214 /* try to handle immediate events right here */
216 if (ev->type == SessionEvent::Locate || ev->type == SessionEvent::LocateRoll) {
217 /* remove any existing Locates that are waiting to execute */
218 _clear_event_type (ev->type);
221 if (ev->action_sample == SessionEvent::Immediate) {
227 case SessionEvent::AutoLoop:
228 _clear_event_type (ev->type);
231 for (Events::iterator i = events.begin(); i != events.end(); ++i) {
232 if ((*i)->type == ev->type && (*i)->action_sample == ev->action_sample) {
233 error << string_compose(_("Session: cannot have two events of type %1 at the same sample (%2)."),
234 enum_2_string (ev->type), ev->action_sample) << endmsg;
240 events.insert (events.begin(), ev);
241 events.sort (SessionEvent::compare);
242 next_event = events.begin();
246 /** @return true when @a ev is deleted. */
248 SessionEventManager::_replace_event (SessionEvent* ev)
253 /* private, used only for events that can only exist once in the queue */
255 for (i = events.begin(); i != events.end(); ++i) {
256 if ((*i)->type == ev->type) {
257 (*i)->action_sample = ev->action_sample;
258 (*i)->target_sample = ev->target_sample;
267 if (i == events.end()) {
268 events.insert (events.begin(), ev);
271 events.sort (SessionEvent::compare);
272 next_event = events.end();
278 /** @return true when @a ev is deleted. */
280 SessionEventManager::_remove_event (SessionEvent* ev)
285 for (i = events.begin(); i != events.end(); ++i) {
286 if ((*i)->type == ev->type && (*i)->action_sample == ev->action_sample) {
292 if (i == next_event) {
295 i = events.erase (i);
300 if (i != events.end()) {
308 SessionEventManager::_clear_event_type (SessionEvent::Type type)
310 Events::iterator i, tmp;
312 for (i = events.begin(); i != events.end(); ) {
317 if ((*i)->type == type) {
319 if (i == next_event) {
328 for (i = immediate_events.begin(); i != immediate_events.end(); ) {
333 if ((*i)->type == type) {
335 immediate_events.erase (i);