Fix deadlocks on MIDI record.
authorDavid Robillard <d@drobilla.net>
Thu, 22 Oct 2009 20:37:24 +0000 (20:37 +0000)
committerDavid Robillard <d@drobilla.net>
Thu, 22 Oct 2009 20:37:24 +0000 (20:37 +0000)
More locking than is strictly necessary, but the assertion in MidiModel::write_lock is a nice check, at least for now...

git-svn-id: svn://localhost/ardour2/branches/3.0@5868 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/ardour/ardour/midi_model.h
libs/ardour/midi_model.cc
libs/ardour/smf_source.cc

index cebc435435b6e96b6d3ed976f12e2273a2ab3645..b099f575a16b3e6b761c705c2830542d0d4fd144 100644 (file)
@@ -178,6 +178,7 @@ private:
        };
 
 public:
+       virtual WriteLock edit_lock();
        virtual WriteLock write_lock();
 
 private:
index 2b6353353712dbbf1f8dbbb25932d18f42e41c84..688e7666688b1e5080b324ca17af85bae76d447e 100644 (file)
@@ -135,7 +135,7 @@ MidiModel::DeltaCommand::operator()()
        // This could be made much faster by using a priority_queue for added and
        // removed notes (or sort here), and doing a single iteration over _model
 
-       MidiModel::WriteLock lock(_model->write_lock());
+       MidiModel::WriteLock lock(_model->edit_lock());
 
        for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
                _model->add_note_unlocked(*i);
@@ -155,7 +155,7 @@ MidiModel::DeltaCommand::undo()
        // This could be made much faster by using a priority_queue for added and
        // removed notes (or sort here), and doing a single iteration over _model
 
-       MidiModel::WriteLock lock(_model->write_lock());;
+       MidiModel::WriteLock lock(_model->edit_lock());;
 
        for (NoteList::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i) {
                _model->remove_note_unlocked(*i);
@@ -383,7 +383,7 @@ MidiModel::DiffCommand::change(const boost::shared_ptr< Evoral::Note<TimeType> >
 void
 MidiModel::DiffCommand::operator()()
 {
-       MidiModel::WriteLock lock(_model->write_lock());
+       MidiModel::WriteLock lock(_model->edit_lock());
 
        for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
                Property prop = i->property;
@@ -413,7 +413,7 @@ MidiModel::DiffCommand::operator()()
 void
 MidiModel::DiffCommand::undo()
 {
-       MidiModel::WriteLock lock(_model->write_lock());
+       MidiModel::WriteLock lock(_model->edit_lock());
 
        for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
                Property prop = i->property;
@@ -723,10 +723,23 @@ MidiModel::find_note (boost::shared_ptr<Evoral::Note<TimeType> > other)
        return boost::shared_ptr<Evoral::Note<TimeType> >();
 }
 
+/** Lock and invalidate the source.
+ * This should be used by commands and editing things
+ */
 MidiModel::WriteLock
-MidiModel::write_lock()
+MidiModel::edit_lock()
 {
        Glib::Mutex::Lock* source_lock = new Glib::Mutex::Lock(_midi_source->mutex());
        _midi_source->invalidate(); // Release cached iterator's read lock on model
        return WriteLock(new WriteLockImpl(source_lock, _lock, _control_lock));
 }
+
+/** Lock just the model, the source lock must already be held.
+ * This should only be called from libardour/evoral places
+ */
+MidiModel::WriteLock
+MidiModel::write_lock()
+{
+       assert(!_midi_source->mutex().trylock());
+       return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
+}
index c8b9e254fb9cb6ab6b49afdef158e7822b8aab7f..f69b1788a0555d51aa1beacba16b2bea1731ac5c 100644 (file)
@@ -362,6 +362,7 @@ SMFSource::set_state (const XMLNode& node, int version)
 void
 SMFSource::mark_streaming_midi_write_started (NoteMode mode, sframes_t start_frame)
 {
+       Glib::Mutex::Lock lm (_lock);
        MidiSource::mark_streaming_midi_write_started (mode, start_frame);
        Evoral::SMF::begin_write ();
        _last_ev_time_beats = 0.0;
@@ -371,6 +372,7 @@ SMFSource::mark_streaming_midi_write_started (NoteMode mode, sframes_t start_fra
 void
 SMFSource::mark_streaming_write_completed ()
 {
+       Glib::Mutex::Lock lm (_lock);
        MidiSource::mark_streaming_write_completed();
 
        if (!writable()) {