don't use inheritance when trying to define both POD-ish and highly-managed Events.

The compiler can rearrange class layouts in memory, which defeats
the entire purpose of using the zero-sized array hack, as well
as causing problems with pool allocators/intrusive refcnts
This commit is contained in:
Paul Davis 2016-12-02 15:22:32 +00:00
parent c6f81f82c0
commit 4069c84eca
5 changed files with 393 additions and 257 deletions

View file

@ -1368,6 +1368,8 @@ MidiRegionView::display_sysexes()
MidiRegionView::~MidiRegionView ()
{
cerr << "::~MidiRegionView()\n";
in_destructor = true;
hide_verbose_cursor ();
@ -1386,6 +1388,7 @@ MidiRegionView::~MidiRegionView ()
delete _note_group;
delete _note_diff_command;
delete _step_edit_cursor;
delete _ghost_note;
}
void

View file

@ -83,6 +83,8 @@ NoteBase::NoteBase(MidiRegionView& region, bool with_events, const NotePtr note)
NoteBase::~NoteBase()
{
cerr << "::~Notebase()";
_region.note_deleted (this);
delete _text;

View file

@ -25,6 +25,11 @@
#include <new>
#include <stdint.h>
//#define DEBUG_EVENT_REFCNT 1
#ifdef DEBUG_EVENT_REFCNT
#include <boost/atomic.hpp>
#endif
#include <boost/intrusive_ptr.hpp>
#include <boost/intrusive/list.hpp>
#include <boost/smart_ptr/intrusive_ref_counter.hpp>
@ -43,6 +48,227 @@ LIBEVORAL_API void init_event_id_counter(event_id_t n);
template<typename T> class Sequence;
#define COMMON_EVENT_DATA \
EventType _type; /*< Type of event (application relative, NOT MIDI 'type') */ \
Time _time; /*< Time stamp of event */ \
uint32_t _size; /*< Size of buffer in bytes */ \
event_id_t _id; /*< Unique event ID */ \
uint8_t _buf[0]; /*< Event data. Must be at end, to use C-style variable-sized structure hack */
#define COMMON_EVENT_METHODS \
inline EventType event_type() const { return _type; } \
inline Time time() const { return _time; } \
inline uint32_t size() const { return _size; } \
inline void set_size (uint32_t s) { _size = s; /* CAREFUL !!! */ } \
inline uint32_t object_size() const { return size() + sizeof (*this); } \
inline const uint8_t* buffer() const { return _buf; } \
inline uint8_t* buffer() { return _buf; } \
\
inline void set_event_type(EventType t) { _type = t; } \
\
inline void set_time(Time t) { _time = t; } \
\
inline event_id_t id() const { return _id; } \
inline void set_id(event_id_t n) { _id = n; } \
\
/* The following methods are type specific and only make sense for the \
correct event type. It is the caller's responsibility to only call \
methods which make sense for the given event type. Currently this
means \
they all only make sense for MIDI, but built-in support may be added
for \
other protocols in the future, or the internal representation may
change \
to be protocol agnostic. */ \
\
uint8_t type() const { return midi_type (_buf); } \
uint8_t channel() const { return midi_channel (_buf); } \
bool is_channel_msg() const { return midi_is_channel_msg (_buf); } \
bool is_note_on() const { return midi_is_note_on (_buf); } \
bool is_note_off() const { return midi_is_note_off (_buf); } \
bool is_note() const { return midi_is_note (_buf); } \
bool is_poly_pressure() const { return midi_is_poly_pressure (_buf); } \
bool is_channel_pressure() const { return midi_is_channel_pressure (_buf); } \
bool is_cc() const { return midi_is_cc (_buf); } \
bool is_pgm_change() const { return midi_is_pgm_change (_buf); } \
bool is_pitch_bender() const { return midi_is_pitch_bender (_buf); } \
bool is_channel_event() const { return midi_is_channel_event (_buf); } \
bool is_smf_meta_event() const { return midi_is_smf_meta_event (_buf); } \
bool is_sysex() const { return midi_is_sysex (_buf); } \
bool is_spp() const { return midi_is_spp (_buf, _size); } \
bool is_mtc_quarter() const { return midi_is_mtc_quarter (_buf, _size); } \
bool is_mtc_full() const { return midi_is_mtc_full (_buf, _size); } \
\
uint8_t note() const { return midi_note (_buf); } \
uint8_t velocity() const { return midi_velocity (_buf); } \
uint8_t poly_note() const { return midi_poly_note (_buf); } \
uint8_t poly_pressure() const { return midi_poly_pressure (_buf); } \
uint8_t channel_pressure() const { return midi_channel_pressure (_buf); } \
uint8_t cc_number() const { return midi_cc_number (_buf); } \
uint8_t cc_value() const { return midi_cc_value (_buf); } \
uint8_t pgm_number() const { return midi_pgm_number (_buf); } \
uint8_t pitch_bender_lsb() const { return midi_pitch_bender_lsb (_buf); } \
uint8_t pitch_bender_msb() const { return midi_pitch_bender_msb (_buf); } \
uint16_t pitch_bender_value() const { return midi_pitch_bender_value (_buf); } \
\
void set_channel(uint8_t channel) { _buf[0] = (0xF0 & _buf[0]) | (0x0F & channel); } \
void set_type(uint8_t type) { _buf[0] = (0x0F & _buf[0]) | (0xF0 & type); } \
void set_note(uint8_t num) { _buf[1] = num; } \
void set_velocity(uint8_t val) { _buf[2] = val; } \
void set_cc_number(uint8_t num) { _buf[1] = num; } \
void set_cc_value(uint8_t val) { _buf[2] = val; } \
void set_pgm_number(uint8_t num) { _buf[1] = num; } \
\
uint16_t value() const { \
switch (type()) { \
case MIDI_CMD_CONTROL: \
return cc_value(); \
case MIDI_CMD_BENDER: \
return pitch_bender_value(); \
case MIDI_CMD_NOTE_PRESSURE: \
return poly_pressure(); \
case MIDI_CMD_CHANNEL_PRESSURE: \
return channel_pressure(); \
default: \
return 0; \
} \
}
template<typename E>
bool event_time_order (const E & a, const E & other)
{
if (a.time() < other.time()) {
return true;
} else if (a.time() > other.time()) {
return false;
}
if (a.type() != MIDI_EVENT) {
/* times are equal, sort order is arbitrary */
return false;
}
/* times are equal. Use MIDI semantics */
bool other_first = false;
/* two events at identical times. we need to determine
the order in which they should occur.
the rule is:
Controller messages
Program Change
Note Off
Note On
Note Pressure
Channel Pressure
Pitch Bend
*/
if (!a.is_channel_msg() || !other.is_channel_msg() || (a.channel() != other.channel())) {
/* if either message is not a channel message, or if
* the channels are
* different, we don't care about the type.
*/
other_first = true;
} else {
switch (other.type()) {
case MIDI_CMD_CONTROL:
other_first = true;
break;
case MIDI_CMD_PGM_CHANGE:
switch (a.type()) {
case MIDI_CMD_CONTROL:
break;
case MIDI_CMD_PGM_CHANGE:
case MIDI_CMD_NOTE_OFF:
case MIDI_CMD_NOTE_ON:
case MIDI_CMD_NOTE_PRESSURE:
case MIDI_CMD_CHANNEL_PRESSURE:
case MIDI_CMD_BENDER:
other_first = true;
}
break;
case MIDI_CMD_NOTE_OFF:
switch (a.type()) {
case MIDI_CMD_CONTROL:
case MIDI_CMD_PGM_CHANGE:
break;
case MIDI_CMD_NOTE_OFF:
case MIDI_CMD_NOTE_ON:
case MIDI_CMD_NOTE_PRESSURE:
case MIDI_CMD_CHANNEL_PRESSURE:
case MIDI_CMD_BENDER:
other_first = true;
} \
break;
case MIDI_CMD_NOTE_ON:
switch (a.type()) {
case MIDI_CMD_CONTROL:
case MIDI_CMD_PGM_CHANGE:
case MIDI_CMD_NOTE_OFF:
break;
case MIDI_CMD_NOTE_ON:
case MIDI_CMD_NOTE_PRESSURE:
case MIDI_CMD_CHANNEL_PRESSURE:
case MIDI_CMD_BENDER:
other_first = true;
}
break;
case MIDI_CMD_NOTE_PRESSURE:
switch (a.type()) {
case MIDI_CMD_CONTROL:
case MIDI_CMD_PGM_CHANGE:
case MIDI_CMD_NOTE_OFF:
case MIDI_CMD_NOTE_ON:
break;
case MIDI_CMD_NOTE_PRESSURE:
case MIDI_CMD_CHANNEL_PRESSURE:
case MIDI_CMD_BENDER:
other_first = true;
}
break;
case MIDI_CMD_CHANNEL_PRESSURE:
switch (a.type()) {
case MIDI_CMD_CONTROL:
case MIDI_CMD_PGM_CHANGE:
case MIDI_CMD_NOTE_OFF:
case MIDI_CMD_NOTE_ON:
case MIDI_CMD_NOTE_PRESSURE:
break;
case MIDI_CMD_CHANNEL_PRESSURE:
case MIDI_CMD_BENDER:
other_first = true;
}
break;
case MIDI_CMD_BENDER:
switch (a.type()) {
case MIDI_CMD_CONTROL:
case MIDI_CMD_PGM_CHANGE:
case MIDI_CMD_NOTE_OFF:
case MIDI_CMD_NOTE_ON:
case MIDI_CMD_NOTE_PRESSURE:
case MIDI_CMD_CHANNEL_PRESSURE:
break;
case MIDI_CMD_BENDER:
other_first = true;
}
break;
}
}
return other_first;
}
/** An event.
*
* Template parameter Time is the type of the time stamp used for this event.
@ -50,11 +276,10 @@ template<typename T> class Sequence;
* This is not POD: the structure uses the C-style zero-sized array hack to
* allow us to place a contiguous data block immediately after the Event
* structure.
*
* This object is otherwise intended to be POD: DO NOT ADD VIRTUAL METHODS.
*/
template<typename Time>
class LIBEVORAL_API Event {
class LIBEVORAL_API Event /* INHERITANCE ILLEGAL HERE */
{
public:
Event (EventType ty, Time tm, size_t sz, uint8_t const * data, event_id_t id = -1)
: _type (ty)
@ -80,6 +305,8 @@ public:
memcpy (_buf, other.buffer(), _size);
}
~Event() {}
Event& operator= (Event const & other) {
/* Does NOT copy event ID */
if (this != &other) {
@ -99,219 +326,13 @@ public:
!memcmp (_buf, other._buf, _size);
}
inline EventType event_type() const { return _type; }
inline Time time() const { return _time; }
inline uint32_t size() const { return _size; }
inline void set_size (uint32_t s) { _size = s; /* CAREFUL !!! */ }
inline uint32_t object_size() const { return size() + sizeof (*this); }
inline const uint8_t* buffer() const { return _buf; }
inline uint8_t* buffer() { return _buf; }
inline void set_event_type(EventType t) { _type = t; }
inline void set_time(Time t) { _time = t; }
inline event_id_t id() const { return _id; }
inline void set_id(event_id_t n) { _id = n; }
/* The following methods are type specific and only make sense for the
correct event type. It is the caller's responsibility to only call
methods which make sense for the given event type. Currently this means
they all only make sense for MIDI, but built-in support may be added for
other protocols in the future, or the internal representation may change
to be protocol agnostic. */
uint8_t type() const { return midi_type (_buf); }
uint8_t channel() const { return midi_channel (_buf); }
bool is_channel_msg() const { return midi_is_channel_msg (_buf); }
bool is_note_on() const { return midi_is_note_on (_buf); }
bool is_note_off() const { return midi_is_note_off (_buf); }
bool is_note() const { return midi_is_note (_buf); }
bool is_poly_pressure() const { return midi_is_poly_pressure (_buf); }
bool is_channel_pressure() const { return midi_is_channel_pressure (_buf); }
bool is_cc() const { return midi_is_cc (_buf); }
bool is_pgm_change() const { return midi_is_pgm_change (_buf); }
bool is_pitch_bender() const { return midi_is_pitch_bender (_buf); }
bool is_channel_event() const { return midi_is_channel_event (_buf); }
bool is_smf_meta_event() const { return midi_is_smf_meta_event (_buf); }
bool is_sysex() const { return midi_is_sysex (_buf); }
bool is_spp() const { return midi_is_spp (_buf, _size); }
bool is_mtc_quarter() const { return midi_is_mtc_quarter (_buf, _size); }
bool is_mtc_full() const { return midi_is_mtc_full (_buf, _size); }
uint8_t note() const { return midi_note (_buf); }
uint8_t velocity() const { return midi_velocity (_buf); }
uint8_t poly_note() const { return midi_poly_note (_buf); }
uint8_t poly_pressure() const { return midi_poly_pressure (_buf); }
uint8_t channel_pressure() const { return midi_channel_pressure (_buf); }
uint8_t cc_number() const { return midi_cc_number (_buf); }
uint8_t cc_value() const { return midi_cc_value (_buf); }
uint8_t pgm_number() const { return midi_pgm_number (_buf); }
uint8_t pitch_bender_lsb() const { return midi_pitch_bender_lsb (_buf); }
uint8_t pitch_bender_msb() const { return midi_pitch_bender_msb (_buf); }
uint16_t pitch_bender_value() const { return midi_pitch_bender_value (_buf); }
void set_channel(uint8_t channel) { _buf[0] = (0xF0 & _buf[0]) | (0x0F & channel); }
void set_type(uint8_t type) { _buf[0] = (0x0F & _buf[0]) | (0xF0 & type); }
void set_note(uint8_t num) { _buf[1] = num; }
void set_velocity(uint8_t val) { _buf[2] = val; }
void set_cc_number(uint8_t num) { _buf[1] = num; }
void set_cc_value(uint8_t val) { _buf[2] = val; }
void set_pgm_number(uint8_t num) { _buf[1] = num; }
uint16_t value() const {
switch (type()) {
case MIDI_CMD_CONTROL:
return cc_value();
case MIDI_CMD_BENDER:
return pitch_bender_value();
case MIDI_CMD_NOTE_PRESSURE:
return poly_pressure();
case MIDI_CMD_CHANNEL_PRESSURE:
return channel_pressure();
default:
return 0;
}
bool time_order_before (Event const & other) const {
return event_time_order (*this, other);
}
inline bool time_order_before (Event const & other) const {
if (_time < other.time()) {
return true;
} else if (_time > other.time()) {
return false;
}
if (_type != MIDI_EVENT) {
/* times are equal, sort order is arbitrary */
return false;
}
/* times are equal. Use MIDI semantics */
bool other_first = false;
/* two events at identical times. we need to determine
the order in which they should occur.
the rule is:
Controller messages
Program Change
Note Off
Note On
Note Pressure
Channel Pressure
Pitch Bend
*/
if (!is_channel_msg() || !other.is_channel_msg() || (channel() != other.channel())) {
/* if either message is not a channel message, or if the channels are
* different, we don't care about the type.
*/
other_first = true;
} else {
switch (other.type()) {
case MIDI_CMD_CONTROL:
other_first = true;
break;
case MIDI_CMD_PGM_CHANGE:
switch (type()) {
case MIDI_CMD_CONTROL:
break;
case MIDI_CMD_PGM_CHANGE:
case MIDI_CMD_NOTE_OFF:
case MIDI_CMD_NOTE_ON:
case MIDI_CMD_NOTE_PRESSURE:
case MIDI_CMD_CHANNEL_PRESSURE:
case MIDI_CMD_BENDER:
other_first = true;
}
break;
case MIDI_CMD_NOTE_OFF:
switch (type()) {
case MIDI_CMD_CONTROL:
case MIDI_CMD_PGM_CHANGE:
break;
case MIDI_CMD_NOTE_OFF:
case MIDI_CMD_NOTE_ON:
case MIDI_CMD_NOTE_PRESSURE:
case MIDI_CMD_CHANNEL_PRESSURE:
case MIDI_CMD_BENDER:
other_first = true;
}
break;
case MIDI_CMD_NOTE_ON:
switch (type()) {
case MIDI_CMD_CONTROL:
case MIDI_CMD_PGM_CHANGE:
case MIDI_CMD_NOTE_OFF:
break;
case MIDI_CMD_NOTE_ON:
case MIDI_CMD_NOTE_PRESSURE:
case MIDI_CMD_CHANNEL_PRESSURE:
case MIDI_CMD_BENDER:
other_first = true;
}
break;
case MIDI_CMD_NOTE_PRESSURE:
switch (type()) {
case MIDI_CMD_CONTROL:
case MIDI_CMD_PGM_CHANGE:
case MIDI_CMD_NOTE_OFF:
case MIDI_CMD_NOTE_ON:
break;
case MIDI_CMD_NOTE_PRESSURE:
case MIDI_CMD_CHANNEL_PRESSURE:
case MIDI_CMD_BENDER:
other_first = true;
}
break;
case MIDI_CMD_CHANNEL_PRESSURE:
switch (type()) {
case MIDI_CMD_CONTROL:
case MIDI_CMD_PGM_CHANGE:
case MIDI_CMD_NOTE_OFF:
case MIDI_CMD_NOTE_ON:
case MIDI_CMD_NOTE_PRESSURE:
break;
case MIDI_CMD_CHANNEL_PRESSURE:
case MIDI_CMD_BENDER:
other_first = true;
}
break;
case MIDI_CMD_BENDER:
switch (type()) {
case MIDI_CMD_CONTROL:
case MIDI_CMD_PGM_CHANGE:
case MIDI_CMD_NOTE_OFF:
case MIDI_CMD_NOTE_ON:
case MIDI_CMD_NOTE_PRESSURE:
case MIDI_CMD_CHANNEL_PRESSURE:
break;
case MIDI_CMD_BENDER:
other_first = true;
}
break;
}
}
return other_first;
}
protected:
EventType _type; ///< Type of event (application relative, NOT MIDI 'type')
Time _time; ///< Time stamp of event
uint32_t _size; ///< Size of buffer in bytes
event_id_t _id; ///< Unique event ID
uint8_t _buf[0]; ///< Event data. Must be at end, to use C-style variable-sized structure hack
COMMON_EVENT_METHODS;
public:
COMMON_EVENT_DATA
private:
/* hide these methods since they are illegal. Event<T> can only be
@ -338,17 +359,13 @@ template<typename Time>
/** An reference-counted version of an Event, to be used in contexts where
* the event is shared between various contexts (e.g. editing of a Sequence)
*
* Inheritance order is based on the assumption that the ref-counter will
* likely be accessed more frequently than any actual data elements in
* Event. Could be wrong and probably needs measuring. But also based on the
* fact that the Event<T> needs to be at the end in order for the zero-size
* array hack to work.
* This cannot inherit from any classes, because we cannot control the memory
* layout, and it is mandatory that the _buf member occurs at the end of the
* object. Surprisingly, perhaps, memory layout is at the whim of the compiler
* and is not required to follow the inheritance order in any way.
*/
template<typename Time>
class LIBEVORAL_API ManagedEvent : public boost::intrusive_ref_counter<ManagedEvent<Time> >,
public PoolAllocated,
public boost::noncopyable,
public Event<Time>
class LIBEVORAL_API ManagedEvent /* INHERITANCE ILLEGAL HERE */
{
public:
static uint32_t memory_size (uint32_t size) { return sizeof (ManagedEvent<Time>) + size; }
@ -379,28 +396,128 @@ class LIBEVORAL_API ManagedEvent : public boost::intrusive_ref_counter<ManagedEv
return ::new (default_event_pool.alloc (memory_size (ev.size()))) ManagedEvent<Time> (default_event_pool, ev);
}
private:
ManagedEvent (EventPool& p, EventType ty, Time tm, size_t sz, uint8_t* data, event_id_t id = -1)
: PoolAllocated (&p)
, Event<Time> (ty, tm, sz, data, id)
{}
~ManagedEvent ();
ManagedEvent (EventPool& p, Event<Time> const & ev)
: PoolAllocated (&p)
, Event<Time> (ev)
{}
int refcnt() const { return _refcnt.load(); }
EventPool* pool() const { return _pool; }
void operator delete (void* ptr) {
ManagedEvent* ev = reinterpret_cast<ManagedEvent*> (ptr);
if (ev && ev->_pool) {
ev->_pool->release (ptr);
}
}
ManagedEvent& operator= (ManagedEvent<Time> const & other) {
/* Does NOT copy event ID */
if (this != &other) {
_type = other._type;
_time = other._time;
_size = other._size;
memcpy (_buf, other._buf, _size);
}
return *this;
}
bool operator== (ManagedEvent const & other) const {
/* Does NOT compare event IDs */
return _type == other._type &&
_time == other._time &&
_size == other._size &&
!memcmp (_buf, other._buf, _size);
}
bool time_order_before (ManagedEvent const & other) const {
return event_time_order (*this, other);
}
COMMON_EVENT_METHODS;
private:
mutable boost::atomic<int> _refcnt;
friend void intrusive_ptr_add_ref (const ManagedEvent<Time>* irc) {
irc->_refcnt.fetch_add (1, boost::memory_order_relaxed);
std::cerr << "refcnt for " << irc << " += " << irc->refcnt() << std::endl;
}
friend void intrusive_ptr_release (const ManagedEvent<Time>* irc) {
if (irc->_refcnt.fetch_sub (1, boost::memory_order_release) == 1) {
boost::atomic_thread_fence (boost::memory_order_acquire);
std::cerr << "refcnt => 0, delete " << irc << std::endl;
delete irc;
} else {
std::cerr << "refcnt for " << irc << " -= " << irc->refcnt() << std::endl;
}
}
private:
EventPool* _pool;
COMMON_EVENT_DATA;
ManagedEvent (EventPool& p, EventType ty, Time tm, size_t sz, uint8_t* data, event_id_t id = -1)
: _pool (&p)
, _type (ty)
, _time (tm)
, _size (sz)
, _id (id)
{
if (data) {
/* specs for memcpy(2) state that passing size == zero is completely legal */
memcpy (_buf, data, sz);
} else {
/* hopefully data will be written in shortly */
memset (_buf, 0, sz);
}
}
ManagedEvent (EventPool& p, Event<Time> const & other)
: _pool (&p)
, _type (other.event_type())
, _time (other.time())
, _size (other.size())
, _id (next_event_id())
{
memcpy (_buf, other.buffer(), _size);
}
ManagedEvent (EventType ty, Time tm, size_t sz, uint8_t* data, event_id_t id = -1)
: PoolAllocated (0)
, Event<Time> (ty, tm, sz, data, id)
{}
: _pool (0)
, _type (ty)
, _time (tm)
, _size (sz)
, _id (id)
{
if (data) {
/* specs for memcpy(2) state that passing size == zero is completely legal */
memcpy (_buf, data, sz);
} else {
/* hopefully data will be written in shortly */
memset (_buf, 0, sz);
}
}
ManagedEvent (Event<Time> const & ev)
: PoolAllocated (0)
, Event<Time> (ev)
{}
ManagedEvent (Event<Time> const & other)
: _pool (0)
, _type (other.event_type())
, _time (other.time())
, _size (other.size())
, _id (next_event_id())
{
memcpy (_buf, other.buffer(), _size);
}
};
template<typename Time>
/*LIBEVORAL_API*/ std::ostream& operator<<(std::ostream& o, const Evoral::ManagedEvent<Time>& ev) {
o << "Event #" << ev.id() << " type = " << ev.event_type() << " @ " << ev.time();
o << std::hex;
for (uint32_t n = 0; n < ev.size(); ++n) {
o << ' ' << (int) ev.buffer()[n];
}
o << std::dec;
return o;
}
/* A ref-counted pointer to an Event that can be placed inside an intrusive
* (doubly-linked) list. This is what most things that manipulate Events in
* "complex" or "smart" data structures should use at all times as the handle
@ -419,6 +536,7 @@ struct LIBEVORAL_API EventPointer : public boost::intrusive_ptr<ManagedEvent<Tim
{
EventPointer () {}
EventPointer (ManagedEvent<Time> * ev) : boost::intrusive_ptr<ManagedEvent<Time> > (ev) { }
~EventPointer ();
/* convenience static factory method to hide the need to call a
ManagedEvent factory method with the same parameters.

View file

@ -29,8 +29,14 @@
#include "evoral/visibility.h"
#include "pbd/stacktrace.h"
#define DEBUG_EVENT_POOL 1
#ifdef DEBUG_EVENT_POOL
#include <glib.h>
#endif
namespace Evoral {
class LIBEVORAL_API EventPool
@ -88,6 +94,7 @@ class LIBEVORAL_API EventPool
_freelists.reserve (_freelists.size() + sp.size());
for (SizePairs::const_iterator s = sp.begin(); s != sp.end(); ++s) {
_freelists.push_back (new FreeList (s->first, s->second));
std::cerr << name() << " freelist for " << s->first << " count = " << s->second << std::endl;
}
}
@ -117,10 +124,13 @@ class LIBEVORAL_API EventPool
void* ret = (*x)->back();
(*x)->pop_back ();
#ifdef DEBUG_EVENT_POOL
std::cerr << name() << ": alloc size[" << (*x)->item_size << "] free = "
std::cerr << name() << ": alloc " << ret << " w/size[" << (*x)->item_size << "] for " << sz << " free = "
<< (*x)->size()
<< " alloc = " << ((char*)(*x)->end - (char*)(*x)->block) / sizeof ((*x)->item_size)
<< std::endl;
if (g_getenv ("ARDOUR_DEBUG_EVENT_POOLS")) {
PBD::stacktrace (std::cerr, 20);
}
#endif
return ret;
@ -135,10 +145,13 @@ class LIBEVORAL_API EventPool
if ((*x)->owns (p)) {
(*x)->push_back (p);
#ifdef DEBUG_EVENT_POOL
std::cerr << name() << ": release size[" << (*x)->item_size << "] free = "
std::cerr << name() << ": release " << p << " w/size[" << (*x)->item_size << "] free = "
<< (*x)->size()
<< " alloc = " << ((char*)(*x)->end - (char*)(*x)->block) / sizeof ((*x)->item_size)
<< std::endl;
if (g_getenv ("ARDOUR_DEBUG_EVENT_POOLS")) {
PBD::stacktrace (std::cerr, 20);
}
#endif
return;
}
@ -150,22 +163,6 @@ class LIBEVORAL_API EventPool
std::string _name;
};
class LIBEVORAL_API PoolAllocated
{
public:
PoolAllocated (EventPool* p) : pool (p) {}
void operator delete (void *ptr) {
PoolAllocated* pa = reinterpret_cast<PoolAllocated*>(ptr);
if (pa && pa->pool) {
pa->pool->release (ptr);
}
}
private:
EventPool* pool;
};
} // namespace Evoral
#endif /* EVORAL_EVENTPOOL_HPP */

View file

@ -26,6 +26,18 @@ namespace Evoral {
static event_id_t _event_id_counter = 0;
template<typename Time>
EventPointer<Time>::~EventPointer ()
{
std::cerr << "::~EventPointer\n";
}
template<typename Time>
ManagedEvent<Time>::~ManagedEvent ()
{
std::cerr << "::~ManagedEvent\n";
}
event_id_t
event_id_counter()
{
@ -66,6 +78,10 @@ template class Event<Evoral::Beats>;
template class Event<double>;
template class Event<int64_t>; /* framepos_t in Ardour */
template class ManagedEvent<Evoral::Beats>;
template class ManagedEvent<double>;
template class ManagedEvent<int64_t>; /* framepos_t in Ardour */
template class EventPointer<Evoral::Beats>;
template class EventPointer<int64_t>; /* framepos_t in Ardour */