OptionEditor::hw_monitor_clicked ()
{
Config->set_use_hardware_monitoring (hw_monitor_button.get_active());
+ if (session) {
+ session->reset_input_monitor_state ();
+ }
}
void
string get_midi_port_name();
void set_midi_port_name(string);
+
+ uint32_t get_midi_feedback_interval_ms();
+ void set_midi_feedback_interval_ms (uint32_t);
bool get_use_hardware_monitoring();
void set_use_hardware_monitoring(bool);
bool use_vst; /* always per-user */
bool quieten_at_speed;
bool quieten_at_speed_is_user;
+ uint32_t midi_feedback_interval_ms;
+ bool midi_feedback_interval_ms_is_user;
XMLNode *key_node;
bool user_configuration;
id_t _id;
atomic_t _record_enabled;
- bool rec_monitoring_off_for_roll;
AudioPlaylist* _playlist;
double _visible_speed;
double _actual_speed;
void flush_redirects ();
template<class T> void foreach_redirect (T *obj, void (T::*func)(Redirect *)) {
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, false, __LINE__, __FILE__);
for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) {
(obj->*func) (*i);
}
}
Redirect *nth_redirect (uint32_t n) {
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, false, __LINE__, __FILE__);
RedirectList::iterator i;
for (i = _redirects.begin(); i != _redirects.end() && n; ++i, --n);
if (i == _redirects.end()) {
jack_nframes_t _roll_delay;
jack_nframes_t _own_latency;
RedirectList _redirects;
- PBD::NonBlockingLock redirect_lock;
+ PBD::NonBlockingRWLock redirect_lock;
IO *_control_outs;
PBD::NonBlockingLock control_outs_lock;
RouteGroup *_edit_group;
typedef list<DiskStream *> DiskStreamList;
Session::DiskStreamList disk_streams() const {
- LockMonitor lm (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__);
return diskstreams; /* XXX yes, force a copy */
}
typedef slist<Route *> RouteList;
RouteList get_routes() const {
- LockMonitor rlock (route_lock, __LINE__, __FILE__);
+ RWLockMonitor rlock (route_lock, false, __LINE__, __FILE__);
return routes; /* XXX yes, force a copy */
}
void set_auto_play (bool yn);
void set_auto_return (bool yn);
void set_auto_input (bool yn);
+ void reset_input_monitor_state ();
void set_input_auto_connect (bool yn);
void set_output_auto_connect (AutoConnectOption);
void set_punch_in (bool yn);
Location* end_location;
Slave *_slave;
SlaveSource _slave_type;
- float _transport_speed;
+ volatile float _transport_speed;
volatile float _desired_transport_speed;
float _last_transport_speed;
jack_nframes_t _last_slave_transport_frame;
/* disk-streams */
DiskStreamList diskstreams;
- mutable PBD::Lock diskstream_lock;
+ mutable PBD::NonBlockingRWLock diskstream_lock;
uint32_t dstream_buffer_size;
void add_diskstream (DiskStream*);
int load_diskstreams (const XMLNode&);
/* routes stuff */
RouteList routes;
- mutable PBD::NonBlockingLock route_lock;
+ mutable PBD::NonBlockingRWLock route_lock;
void add_route (Route*);
int load_routes (const XMLNode&);
Sample* click_emphasis_data;
jack_nframes_t click_length;
jack_nframes_t click_emphasis_length;
+ mutable PBD::NonBlockingRWLock click_lock;
static const Sample default_click[];
static const jack_nframes_t default_click_length;
template<class T> void
Session::foreach_diskstream (T *obj, void (T::*func)(DiskStream&))
{
- LockMonitor lm (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__);
for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); i++) {
if (!(*i)->hidden()) {
(obj->*func) (**i);
RouteList public_order;
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
public_order = routes;
}
RouteList public_order;
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
public_order = routes;
}
RouteList public_order;
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
public_order = routes;
}
Sample* tmpb;
jack_nframes_t transport_frame;
- automation_snapshot (start_frame);
-
+ {
+ TentativeRWLockMonitor lm (redirect_lock, false, __LINE__, __FILE__);
+ if (lm.locked()) {
+ // automation snapshot can also be called from the non-rt context
+ // and it uses the redirect list, so we take the lock out here
+ automation_snapshot (start_frame);
+ }
+ }
+
if (n_outputs() == 0 && _redirects.empty()) {
return 0;
}
}
}
- /* don't waste time with automation if we're recording */
+ /* don't waste time with automation if we're recording or we've just stopped (yes it can happen) */
- if (!diskstream->record_enabled()) {
+ if (!diskstream->record_enabled() && _session.transport_rolling()) {
+ TentativeLockMonitor am (automation_lock, __LINE__, __FILE__);
- if (gain_automation_playback()) {
+ if (am.locked() && gain_automation_playback()) {
apply_gain_automation = _gain_automation_curve.rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes);
}
}
_silent = true;
apply_gain_automation = false;
-
+
silence (nframes, offset);
return diskstream->process (_session.transport_frame() + offset, nframes, offset, can_record, rec_monitors_input);
vector<Sample*>::iterator bi;
Sample * b;
- LockMonitor rlock (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor rlock (redirect_lock, false, __LINE__, __FILE__);
if (diskstream->playlist()->read (buffers[0], mix_buffer, gain_buffer, start, nframes) != nframes) {
return -1;
_freeze_record.have_mementos = true;
{
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, false, __LINE__, __FILE__);
for (RedirectList::iterator r = _redirects.begin(); r != _redirects.end(); ++r) {
} else {
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, false, __LINE__, __FILE__); // should this be a write lock? jlc
for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) {
for (vector<FreezeRecordInsertInfo*>::iterator ii = _freeze_record.insert_info.begin(); ii != _freeze_record.insert_info.end(); ++ii) {
if ((*ii)->id == (*i)->id()) {
node->add_child_nocopy(option_node("disk-choice-space-threshold", string(buf)));
}
+ if (!user_only || midi_feedback_interval_ms_is_user) {
+ snprintf(buf, sizeof(buf), "%" PRIu32, midi_feedback_interval_ms);
+ node->add_child_nocopy(option_node("midi-feedback-interval-ms", string(buf)));
+ }
+
if (!user_only || mute_affects_pre_fader_is_user) {
node->add_child_nocopy(option_node("mute-affects-pre-fader", mute_affects_pre_fader?"yes":"no"));
}
if (sscanf (option_value.c_str(), "%f", &v) == 1) {
set_quieten_at_speed (v);
}
+ } else if (option_name == "midi-feedback-interval-ms") {
+ set_midi_feedback_interval_ms (atoi (option_value.c_str()));
}
}
stop_recording_on_xrun = false;
verify_remove_last_capture = true;
stop_at_session_end = true;
- seamless_looping = false;
+ seamless_looping = true;
auto_xfade = true;
no_new_session_dialog = false;
timecode_source_is_synced = true;
use_vst = true; /* if we build with VST_SUPPORT, otherwise no effect */
quieten_at_speed = true;
+ midi_feedback_interval_ms = 100;
+
// this is about 5 minutes at 48kHz, 4 bytes/sample
disk_choice_space_threshold = 57600000;
no_new_session_dialog_is_user = false;
timecode_source_is_synced_is_user = false;
quieten_at_speed_is_user = false;
+ midi_feedback_interval_ms_is_user = false;
}
Configuration::MidiPortDescriptor::MidiPortDescriptor (const XMLNode& node)
}
}
+uint32_t
+Configuration::get_midi_feedback_interval_ms ()
+{
+ return midi_feedback_interval_ms;
+}
+
+void
+Configuration::set_midi_feedback_interval_ms (uint32_t val)
+{
+ midi_feedback_interval_ms = val;
+ if (user_configuration) {
+ midi_feedback_interval_ms_is_user = true;
+ }
+}
+
bool
Configuration::get_use_hardware_monitoring()
{
_alignment_style = ExistingMaterial;
_persistent_alignment_style = ExistingMaterial;
first_input_change = true;
- rec_monitoring_off_for_roll = false;
_playlist = 0;
i_am_the_modifier = 0;
atomic_set (&_record_enabled, 0);
if (can_record && !_last_capture_regions.empty()) {
_last_capture_regions.clear ();
}
-
- if (rec_nframes) {
-
- if (Config->get_use_hardware_monitoring() && re && rec_monitoring_off_for_roll && rec_monitors_input) {
- for (c = channels.begin(); c != channels.end(); ++c) {
- (*c).source->ensure_monitor_input (true);
- }
- rec_monitoring_off_for_roll = false;
- }
- }
if (nominally_recording || rec_nframes) {
} else {
+
/* we can't use the capture buffer as the playback buffer, because
we recorded only a part of the current process' cycle data
for capture.
void
DiskStream::finish_capture (bool rec_monitors_input)
{
- if (Config->get_use_hardware_monitoring() && record_enabled()) {
- if (rec_monitors_input) {
- if (rec_monitoring_off_for_roll) {
- for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
- (*chan).source->ensure_monitor_input (true);
- }
- rec_monitoring_off_for_roll = false;
- }
- } else {
- if (!rec_monitoring_off_for_roll) {
- for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
- (*chan).source->ensure_monitor_input (false);
- }
- rec_monitoring_off_for_roll = true;
- }
- }
- }
-
was_recording = false;
if (capture_captured == 0) {
void
DiskStream::set_record_enabled (bool yn, void* src)
{
+ bool rolling = _session.transport_speed() != 0.0f;
+
if (!recordable() || !_session.record_enabling_legal()) {
return;
}
if (yn) {
atomic_set (&_record_enabled, 1);
capturing_sources.clear ();
- if (Config->get_use_hardware_monitoring()) {
+ if (Config->get_use_hardware_monitoring()) {
for (ChannelList::iterator chan = channels.begin(); chan != channels.end(); ++chan) {
if ((*chan).source) {
- (*chan).source->request_monitor_input (true);
+ (*chan).source->request_monitor_input (!(_session.get_auto_input() && rolling));
}
capturing_sources.push_back ((*chan).write_source);
}
if (get_control_info (ch, ev, additional)) {
data.controller_number = additional;
data.value = val;
-
+ last_written = val;
+
io._session.send_midi_message (get_port(), ev, ch, data);
}
//send_midi_feedback (gain_to_midi (gain));
if (get_control_info (ch, ev, additional)) {
data.controller_number = additional;
data.value = val;
-
+ last_written = val;
+
sp.get_parent().session().send_midi_message (get_port(), ev, ch, data);
}
if (get_control_info (ch, ev, additional)) {
data.controller_number = additional;
data.value = val;
-
+ last_written = val;
+
plugin.session().send_midi_message (get_port(), ev, ch, data);
}
}
-------------------------------------------------------------------------------------------------- */
if (with_redirects) {
- TentativeLockMonitor rm (redirect_lock, __LINE__, __FILE__);
+ TentativeRWLockMonitor rm (redirect_lock, false, __LINE__, __FILE__);
if (rm.locked()) {
if (mute_gain > 0 || !_mute_affects_pre_fader) {
for (i = _redirects.begin(); i != _redirects.end(); ++i) {
}
}
- if (apply_gain_automation) {
+ if (apply_gain_automation && _session.transport_rolling()) {
_effective_gain = gab[nframes-1];
}
if (post_fader_work) {
- TentativeLockMonitor rm (redirect_lock, __LINE__, __FILE__);
+ TentativeRWLockMonitor rm (redirect_lock, false, __LINE__, __FILE__);
if (rm.locked()) {
if (mute_gain > 0 || !_mute_affects_post_fader) {
for (i = _redirects.begin(); i != _redirects.end(); ++i) {
}
{
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, true, __LINE__, __FILE__);
PluginInsert* pi;
PortInsert* porti;
}
{
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, true, __LINE__, __FILE__);
RedirectList::iterator existing_end = _redirects.end();
--existing_end;
}
{
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, true, __LINE__, __FILE__);
for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) {
delete *i;
redirect_max_outs = 0;
{
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, true, __LINE__, __FILE__);
RedirectList::iterator i;
bool removed = false;
int
Route::reset_plugin_counts (uint32_t* lpc)
{
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, true, __LINE__, __FILE__);
return _reset_plugin_counts (lpc);
}
RedirectList to_be_deleted;
{
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, true, __LINE__, __FILE__);
RedirectList::iterator tmp;
RedirectList the_copy;
void
Route::all_redirects_flip ()
{
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, false, __LINE__, __FILE__);
if (_redirects.empty()) {
return;
void
Route::all_redirects_active (bool state)
{
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, false, __LINE__, __FILE__);
if (_redirects.empty()) {
return;
{
{
RedirectSorter comparator;
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, true, __LINE__, __FILE__);
uint32_t old_rmo = redirect_max_outs;
/* the sweet power of C++ ... */
}
{
- TentativeLockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ TentativeRWLockMonitor lm (redirect_lock, false, __LINE__, __FILE__);
if (lm.locked()) {
for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) {
{
jack_nframes_t now = _session.transport_frame();
- if (!did_locate) {
- automation_snapshot (now);
- }
-
{
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, false, __LINE__, __FILE__);
+
+ if (!did_locate) {
+ automation_snapshot (now);
+ }
+
for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) {
if (Config->get_plugins_stop_with_transport() && can_flush_redirects) {
}
apply_gain_automation = false;
-
+
if (n_inputs()) {
passthru (start_frame, end_frame, nframes, offset, 0, false);
} else {
Route::roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t offset, int declick,
bool can_record, bool rec_monitors_input)
{
- automation_snapshot (_session.transport_frame());
-
+ {
+ TentativeRWLockMonitor lm(redirect_lock, false, __LINE__, __FILE__);
+ if (lm.locked()) {
+ // automation snapshot can also be called from the non-rt context
+ // and it uses the redirect list, so we take the lock out here
+ automation_snapshot (_session.transport_frame());
+ }
+ }
+
if ((n_outputs() == 0 && _redirects.empty()) || n_inputs() == 0 || !_active) {
silence (nframes, offset);
return 0;
}
_silent = false;
+
apply_gain_automation = false;
{
TentativeLockMonitor am (automation_lock, __LINE__, __FILE__);
- if (am.locked()) {
+ if (am.locked() && _session.transport_rolling()) {
jack_nframes_t start_frame = end_frame - nframes;
if (_session.get_midi_feedback()) {
{
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, false, __LINE__, __FILE__);
for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) {
(*i)->send_all_midi_feedback ();
}
buf = _midi_mute_control.write_feedback (buf, bufsize, _muted);
{
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, false, __LINE__, __FILE__);
for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) {
buf = (*i)->write_midi_feedback (buf, bufsize);
}
this is called from the RT audio thread.
*/
- LockMonitor lm (redirect_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (redirect_lock, false, __LINE__, __FILE__);
for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) {
(*i)->deactivate ();
if (get_control_info (ch, ev, additional)) {
data.controller_number = additional;
data.value = val;
-
+ last_written = value;
+
route._session.send_midi_message (get_port(), ev, ch, data);
}
}
}
if (take_lock) {
- route_lock.lock ();
+ route_lock.read_lock ();
}
for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
{
if (auto_input != yn) {
auto_input = yn;
+
+ if (Config->get_use_hardware_monitoring() && transport_rolling()) {
+ /* auto-input only makes a difference if we're rolling */
+
+ /* Even though this can called from RT context we are using
+ a non-tentative rwlock here, because the action must occur.
+ The rarity and short potential lock duration makes this "OK"
+ */
+ RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+ for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (!auto_input);
+ }
+ }
+ }
+
set_dirty();
ControlChanged (AutoInput);
}
}
+void
+Session::reset_input_monitor_state ()
+{
+ if (transport_rolling()) {
+ RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+ for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (Config->get_use_hardware_monitoring() && !auto_input);
+ }
+ }
+ } else {
+ RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+ for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (Config->get_use_hardware_monitoring());
+ }
+ }
+ }
+}
+
+
void
Session::set_input_auto_connect (bool yn)
{
atomic_set (&_record_status, Recording);
_last_record_location = _transport_frame;
send_mmc_in_another_thread (MIDI::MachineControl::cmdRecordStrobe);
+
+ if (Config->get_use_hardware_monitoring() && auto_input) {
+ /* Even though this can be called from RT context we are using
+ a non-tentative rwlock here, because the action must occur.
+ The rarity and short potential lock duration makes this "OK"
+ */
+ RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+
+ for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ //cerr << "switching to input" << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (true);
+ }
+ }
+ }
+
RecordEnabled ();
}
}
if (atomic_read (&_record_status) != Disabled) {
atomic_set (&_record_status, Disabled);
send_mmc_in_another_thread (MIDI::MachineControl::cmdRecordExit);
+
+ if (Config->get_use_hardware_monitoring() && auto_input) {
+ /* Even though this can be called from RT context we are using
+ a non-tentative rwlock here, because the action must occur.
+ The rarity and short potential lock duration makes this "OK"
+ */
+ RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+
+ for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (false);
+ }
+ }
+ }
+
RecordDisabled ();
remove_pending_capture_state ();
+
}
}
Session::step_back_from_record ()
{
atomic_set (&_record_status, Enabled);
+
+ if (Config->get_use_hardware_monitoring()) {
+ /* Even though this can be called from RT context we are using
+ a non-tentative rwlock here, because the action must occur.
+ The rarity and short potential lock duration makes this "OK"
+ */
+ RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+
+ for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+ if (auto_input && (*i)->record_enabled ()) {
+ //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (false);
+ }
+ }
+ }
}
void
*/
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
+ RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
vector<Sample*>::iterator i;
uint32_t np;
default_fade_steepness = steepness;
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ // jlc, WTF is this!
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
AudioRegion::set_default_fade (steepness, fade_frames);
}
/* count existing audio tracks */
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
if (dynamic_cast<AudioTrack*>(*i) != 0) {
if (!(*i)->hidden()) {
/* count existing audio busses */
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
if (dynamic_cast<AudioTrack*>(*i) == 0) {
if (!(*i)->hidden()) {
Session::add_route (Route* route)
{
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, true, __LINE__, __FILE__);
routes.push_front (route);
resort_routes(0);
}
void
Session::add_diskstream (DiskStream* dstream)
{
+ /* need to do this in case we're rolling at the time, to prevent false underruns */
+ dstream->do_refill(0, 0);
+
{
- LockMonitor lm (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (diskstream_lock, true, __LINE__, __FILE__);
diskstreams.push_back (dstream);
}
Session::remove_route (Route& route)
{
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, true, __LINE__, __FILE__);
routes.remove (&route);
/* deleting the master out seems like a dumb
}
{
- LockMonitor lm (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (diskstream_lock, true, __LINE__, __FILE__);
AudioTrack* at;
return;
}
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
bool is_track;
is_track = (dynamic_cast<AudioTrack*>(route) != 0);
basis, but needs the global overview that only the session
has.
*/
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
update_route_solo_state();
}
Route *
Session::route_by_name (string name)
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
if ((*i)->name() == name) {
DiskStream *
Session::diskstream_by_name (string name)
{
- LockMonitor lm (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__);
for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
if ((*i)->name() == name) {
DiskStream *
Session::diskstream_by_id (id_t id)
{
- LockMonitor lm (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__);
for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
if ((*i)->id() == id) {
{
list<Region*> r;
+ RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__);
+
for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
list<Region*>& l = (*i)->last_capture_regions();
void
Session::set_all_solo (bool yn)
{
- /* XXX this copy is not safe: the Routes within the list
- can still be deleted after the Route lock is released.
- */
-
- RouteList copy;
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
- copy = routes;
- }
-
- for (RouteList::iterator i = copy.begin(); i != copy.end(); ++i) {
- if (!(*i)->hidden()) {
- (*i)->set_solo (yn, this);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
+
+ for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
+ if (!(*i)->hidden()) {
+ (*i)->set_solo (yn, this);
+ }
}
}
void
Session::set_all_mute (bool yn)
{
- RouteList copy;
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
- copy = routes;
- }
-
- for (RouteList::iterator i = copy.begin(); i != copy.end(); ++i) {
- if (!(*i)->hidden()) {
- (*i)->set_mute (yn, this);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
+
+ for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
+ if (!(*i)->hidden()) {
+ (*i)->set_mute (yn, this);
+ }
}
}
uint32_t
Session::n_diskstreams () const
{
- LockMonitor lm (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__);
uint32_t n = 0;
for (DiskStreamList::const_iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
void
Session::foreach_diskstream (void (DiskStream::*func)(void))
{
- LockMonitor lm (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__);
for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
if (!(*i)->hidden()) {
((*i)->*func)();
return;
}
- LockMonitor lm1 (route_lock, __LINE__, __FILE__);
- LockMonitor lm2 (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor lm1 (route_lock, true, __LINE__, __FILE__);
+ RWLockMonitor lm2 (diskstream_lock, false, __LINE__, __FILE__);
resort_routes (0);
void
Session::record_enable_change_all (bool yn)
{
- LockMonitor lm1 (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm1 (route_lock, false, __LINE__, __FILE__);
for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
AudioTrack* at;
void
Session::reset_native_file_format ()
{
- LockMonitor lm1 (route_lock, __LINE__, __FILE__);
- LockMonitor lm2 (diskstream_lock, __LINE__, __FILE__);
+ // jlc - WHY take routelock?
+ //RWLockMonitor lm1 (route_lock, true, __LINE__, __FILE__);
+ RWLockMonitor lm2 (diskstream_lock, false, __LINE__, __FILE__);
for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
(*i)->reset_write_sources (false);
bool
Session::route_name_unique (string n) const
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
for (RouteList::const_iterator i = routes.begin(); i != routes.end(); ++i) {
if ((*i)->name() == n) {
int
Session::freeze (InterThreadInfo& itt)
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
Session::ntracks () const
{
uint32_t n = 0;
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
for (RouteList::const_iterator i = routes.begin(); i != routes.end(); ++i) {
if (dynamic_cast<AudioTrack*> (*i)) {
Session::nbusses () const
{
uint32_t n = 0;
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
for (RouteList::const_iterator i = routes.begin(); i != routes.end(); ++i) {
if (dynamic_cast<AudioTrack*> (*i) == 0) {
gettimeofday (&begin, 0);
+ RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+
for (i = diskstreams.begin(); !transport_work_requested() && butler_should_run && i != diskstreams.end(); ++i) {
// cerr << "rah fondr " << (*i)->io()->name () << endl;
} else {
- LockMonitor dm (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor dm (diskstream_lock, false, __LINE__, __FILE__);
for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
(*i)->set_pending_overwrite (true);
}
if (_click_io == 0) {
return;
}
+
+ TentativeRWLockMonitor clickm (click_lock, true, __LINE__, __FILE__);
- if (_transport_speed != 1.0 || !_clicking || click_data == 0) {
+ if (!clickm.locked() || _transport_speed != 1.0 || !_clicking || click_data == 0) {
_click_io->silence (nframes, offset);
return;
}
void
Session::clear_clicks ()
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (click_lock, true, __LINE__, __FILE__);
for (Clicks::iterator i = clicks.begin(); i != clicks.end(); ++i) {
delete *i;
case Event::PunchIn:
// cerr << "PunchIN at " << transport_frame() << endl;
if (punch_in && record_status() == Enabled) {
- {
- LockMonitor lm (diskstream_lock, __LINE__, __FILE__);
- for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
- (*i)->punch_in();
- }
- }
enable_record ();
}
remove = false;
/* take everyone out of awrite to avoid disasters */
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
(*i)->protect_automation ();
}
/* get everyone to the right position */
{
- LockMonitor lm (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__);
for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
if ((*i)-> seek (spec.start_frame, true)) {
error << string_compose (_("%1: cannot seek to %2 for export"),
if (active_feedback) {
/* XXX use Config->feedback_interval_usecs()*/;
- timeout = 250;
+ timeout = max (5, (int) Config->get_midi_feedback_interval_ms());
} else {
timeout = -1;
}
switch ((FeedbackRequest::Type) req) {
case FeedbackRequest::Start:
- timeout = 250;
+ timeout = max (5, (int) Config->get_midi_feedback_interval_ms());
active_feedback++;
break;
}
}
- if (!active_feedback) {
+ if (!active_feedback || transport_stopped()) {
continue;
}
MIDI::byte* end = buf;
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
end = (*i)->write_midi_feedback (end, bsize);
return 0;
}
- // cerr << "MIDI feedback: write " << (int32_t) (end - buf) << " of " << buf << " to midi port\n";
-
deliver_midi (_midi_port, buf, (int32_t) (end - buf));
+ //cerr << "MIDI feedback: wrote " << (int32_t) (end - buf) << " to midi port\n";
+
+
return 0;
}
MachineControl::CommandSignature MMC_CommandSignature;
MachineControl::ResponseSignature MMC_ResponseSignature;
-MultiAllocSingleReleasePool Session::MIDIRequest::pool ("midi", sizeof (Session::MIDIRequest), 256);
+MultiAllocSingleReleasePool Session::MIDIRequest::pool ("midi", sizeof (Session::MIDIRequest), 1024);
int
Session::use_config_midi_ports ()
poke_midi_thread ();
if (_midi_port) {
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
(*i)->reset_midi_control (_midi_port, midi_control);
}
{
if (midi_feedback) {
// send out current state of all routes
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
(*i)->send_all_midi_feedback ();
}
end_frame = _transport_frame + nframes;
{
- TentativeLockMonitor rm (route_lock, __LINE__, __FILE__);
+ TentativeRWLockMonitor rm (route_lock, false, __LINE__, __FILE__);
+ TentativeRWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+
Event* this_event;
Events::iterator the_next_one;
- if (!rm.locked() || (post_transport_work & (PostTransportLocate|PostTransportStop))) {
+ if (!rm.locked() || !dsm.locked() || (post_transport_work & (PostTransportLocate|PostTransportStop))) {
no_roll (nframes, 0);
return;
}
if (slave_state == Waiting) {
// cerr << "waiting at " << slave_transport_frame << endl;
-
- if (slave_transport_frame >= slave_wait_end) {
+ TentativeRWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+
+ if (dsm.locked() && slave_transport_frame >= slave_wait_end) {
// cerr << "\tstart at " << _transport_frame << endl;
+
slave_state = Running;
bool ok = true;
*/
bool need_butler;
+
+ TentativeRWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+ if (!dsm.locked()) {
+ goto noroll;
+ }
+
prepare_diskstreams ();
silent_process_routes (nframes, offset);
commit_diskstreams (nframes, need_butler);
long frames_moved;
{
- TentativeLockMonitor rm (route_lock, __LINE__, __FILE__);
+ TentativeRWLockMonitor rm (route_lock, false, __LINE__, __FILE__);
+ TentativeRWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
- if (!rm.locked() || (post_transport_work & (PostTransportLocate|PostTransportStop))) {
+ if (!rm.locked() || !dsm.locked() || (post_transport_work & (PostTransportLocate|PostTransportStop))) {
no_roll (nframes, 0);
return;
}
void
Session::process_audition (jack_nframes_t nframes)
{
- TentativeLockMonitor rm (route_lock, __LINE__, __FILE__);
+ TentativeRWLockMonitor rm (route_lock, false, __LINE__, __FILE__);
Event* ev;
if (rm.locked()) {
child = node->add_child ("DiskStreams");
{
- LockMonitor dl (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor dl (diskstream_lock, false, __LINE__, __FILE__);
for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
if (!(*i)->hidden()) {
child->add_child_nocopy ((*i)->get_state());
child = node->add_child ("Routes");
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
RoutePublicOrderSorter cmp;
RouteList public_order(routes);
void
Session::swap_configuration(Configuration** new_config)
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, true, __LINE__, __FILE__); // jlc - WHY?
Configuration* tmp = *new_config;
*new_config = Config;
Config = tmp;
void
Session::copy_configuration(Configuration* new_config)
{
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, true, __LINE__, __FILE__);
new_config = new Configuration(*Config);
}
Session::get_global_route_boolean (bool (Route::*method)(void) const)
{
GlobalRouteBooleanState s;
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
if (!(*i)->hidden()) {
Session::get_global_route_metering ()
{
GlobalRouteMeterState s;
- LockMonitor lm (route_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
if (!(*i)->hidden()) {
disable_record ();
reset_slave_state ();
-
+
_transport_speed = 0;
+
transport_sub_state = (auto_return ? AutoReturning : 0);
}
void
Session::butler_transport_work ()
{
+ RWLockMonitor rm (route_lock, false, __LINE__, __FILE__);
+ RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+
if (post_transport_work & PostTransportCurveRealloc) {
for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
(*i)->curve_reallocate();
void
Session::non_realtime_set_speed ()
{
- LockMonitor lm (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__);
for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
(*i)->non_realtime_set_speed ();
void
Session::non_realtime_overwrite ()
{
- LockMonitor lm (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (diskstream_lock, false, __LINE__, __FILE__);
for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
if ((*i)->pending_overwrite) {
{
/* Called from event-handling context */
- if (actively_recording() || _locations.auto_loop_location() == 0) {
+ if ((actively_recording() && yn) || _locations.auto_loop_location() == 0) {
return;
}
} else {
- /* XXX i don't know where else to put this. something has to clear the
- current clicks, and without deadlocking. clear_clicks() takes
- the route lock which would deadlock in this context.
- */
+ /* this is functionally what clear_clicks() does but with a tentative lock */
- for (Clicks::iterator i = clicks.begin(); i != clicks.end(); ++i) {
- delete *i;
- }
+ TentativeRWLockMonitor clickm (click_lock, true, __LINE__, __FILE__);
+
+ if (clickm.locked()) {
+
+ for (Clicks::iterator i = clicks.begin(); i != clicks.end(); ++i) {
+ delete *i;
+ }
- clicks.clear ();
+ clicks.clear ();
+ }
}
+ if (with_roll) {
+ /* switch from input if we're going to roll */
+ if (Config->get_use_hardware_monitoring()) {
+ /* Even though this is called from RT context we are using
+ a non-tentative rwlock here, because the action must occur.
+ The rarity and short potential lock duration makes this "OK"
+ */
+ RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+ for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (!auto_input);
+ }
+ }
+ }
+ } else {
+ /* otherwise we're going to stop, so do the opposite */
+ if (Config->get_use_hardware_monitoring()) {
+ /* Even though this is called from RT context we are using
+ a non-tentative rwlock here, because the action must occur.
+ The rarity and short potential lock duration makes this "OK"
+ */
+ RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+ for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ cerr << "switching to input" << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (true);
+ }
+ }
+ }
+ }
/* cancel autoloop if transport pos outside of loop range */
if (auto_loop) {
if (transport_rolling() && speed == 0.0) {
+ if (Config->get_use_hardware_monitoring())
+ {
+ /* Even though this is called from RT context we are using
+ a non-tentative rwlock here, because the action must occur.
+ The rarity and short potential lock duration makes this "OK"
+ */
+ RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+ for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ //cerr << "switching to input" << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (true);
+ }
+ }
+ }
+
if (synced_to_jack ()) {
_engine.transport_stop ();
} else {
} else if (transport_stopped() && speed == 1.0) {
+ if (Config->get_use_hardware_monitoring()) {
+ /* Even though this is called from RT context we are using
+ a non-tentative rwlock here, because the action must occur.
+ The rarity and short potential lock duration makes this "OK"
+ */
+ RWLockMonitor dsm (diskstream_lock, false, __LINE__, __FILE__);
+ for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
+ if (auto_input && (*i)->record_enabled ()) {
+ //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (false);
+ }
+ }
+ }
+
if (synced_to_jack()) {
_engine.transport_start ();
} else {
_last_transport_speed = _transport_speed;
_transport_speed = speed;
-
+
for (DiskStreamList::iterator i = diskstreams.begin(); i != diskstreams.end(); ++i) {
if ((*i)->realtime_set_speed ((*i)->speed(), true)) {
post_transport_work = PostTransportWork (post_transport_work | PostTransportSpeed);
return;
}
- LockMonitor lm (route_lock, __LINE__, __FILE__);
- LockMonitor lm2 (diskstream_lock, __LINE__, __FILE__);
+ RWLockMonitor lm (route_lock, false, __LINE__, __FILE__);
+ RWLockMonitor lm2 (diskstream_lock, false, __LINE__, __FILE__);
_worst_track_latency = 0;
for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
{
TR_FN ();
int R;
+ int totwritten = 0;
snd_midi_event_reset_encode (encoder);
int nwritten = snd_midi_event_encode (encoder, msg, msglen, &SEv);
TR_VAL (nwritten);
- if (0 < nwritten) {
+ while (0 < nwritten) {
if (0 <= (R = snd_seq_event_output (seq, &SEv)) &&
0 <= (R = snd_seq_drain_output (seq))) {
bytes_written += nwritten;
+ totwritten += nwritten;
if (output_parser) {
output_parser->raw_preparse (*output_parser, msg, nwritten);
for (int i = 0; i < nwritten; i++) {
}
output_parser->raw_postparse (*output_parser, msg, nwritten);
}
- return nwritten;
} else {
TR_VAL(R);
return R;
}
- } else
- return nwritten;
+
+ msglen -= nwritten;
+ msg += nwritten;
+ if (msglen > 0) {
+ nwritten = snd_midi_event_encode (encoder, msg, msglen, &SEv);
+ TR_VAL(nwritten);
+ }
+ else {
+ break;
+ }
+ }
+
+ return totwritten;
}
int ALSA_SequencerMidiPort::read (byte *buf, size_t max)
int unlock() { return pthread_mutex_unlock (&_mutex); }
};
+class RWLock {
+ public:
+ RWLock() { pthread_rwlock_init (&_mutex, 0); }
+ virtual ~RWLock() { pthread_rwlock_destroy(&_mutex); }
+
+ virtual int write_lock () { return pthread_rwlock_wrlock (&_mutex); }
+ virtual int read_lock () { return pthread_rwlock_rdlock (&_mutex); }
+ virtual int unlock() { return pthread_rwlock_unlock (&_mutex); }
+
+ pthread_rwlock_t *mutex() { return &_mutex; }
+
+ protected:
+ pthread_rwlock_t _mutex;
+};
+
+class NonBlockingRWLock : public RWLock {
+ public:
+ NonBlockingRWLock() {}
+ ~NonBlockingRWLock(){}
+
+ int write_trylock () { return pthread_rwlock_trywrlock (&_mutex); }
+ int read_trylock () { return pthread_rwlock_tryrdlock (&_mutex); }
+};
+
+
class LockMonitor
{
public:
#endif
};
+
+class RWLockMonitor
+{
+ public:
+ RWLockMonitor (RWLock& lck, bool write, unsigned long l, const char *f)
+ : lock (lck)
+#ifdef DEBUG_LOCK_MONITOR
+ , line (l), file (f)
+#endif
+ {
+
+#ifdef DEBUG_LOCK_MONITOR
+ unsigned long long when;
+ when = get_cycles();
+ cerr << when << " lock " << &lock << " at " << line << " in " << file << endl;
+#endif
+ if (write) {
+ lock.write_lock ();
+ } else {
+ lock.read_lock ();
+ }
+#ifdef DEBUG_LOCK_MONITOR
+ when = get_cycles();
+ cerr << '\t' << when
+ << " locked: "
+ << &lock << " at "
+ << line << " in " << file << endl;
+#endif
+ }
+
+ ~RWLockMonitor () {
+ lock.unlock ();
+#ifdef DEBUG_LOCK_MONITOR
+ unsigned long long when;
+ when = get_cycles();
+ cerr << '\t' << when << ' '
+ << " UNLOCKED "
+ << &lock << " at "
+ << line << " in " << file << endl;
+#endif
+ }
+ private:
+ RWLock& lock;
+#ifdef DEBUG_LOCK_MONITOR
+ unsigned long line;
+ const char * file;
+#endif
+};
+
+class TentativeRWLockMonitor
+{
+ public:
+ TentativeRWLockMonitor (NonBlockingRWLock& lck, bool write, unsigned long l, const char *f)
+ : lock (lck)
+#ifdef DEBUG_LOCK_MONITOR
+ , line (l), file (f)
+#endif
+ {
+
+#ifdef DEBUG_LOCK_MONITOR
+ unsigned long long when;
+ when = get_cycles();
+ cerr << when << " tentative lock " << &lock << " at " << line << " in " << file << endl;
+#endif
+ if (write) {
+ _locked = (lock.write_trylock() == 0);
+ } else {
+ _locked = (lock.read_trylock() == 0);
+ }
+
+#ifdef DEBUG_LOCK_MONITOR
+ when = get_cycles();
+ cerr << '\t' << when << ' '
+ << _locked
+ << " lock: "
+ << &lock << " at "
+ << line << " in " << file << endl;
+#endif
+ }
+
+ ~TentativeRWLockMonitor () {
+ if (_locked) {
+ lock.unlock ();
+#ifdef DEBUG_LOCK_MONITOR
+ unsigned long long when;
+ when = get_cycles();
+ cerr << '\t' << when << ' '
+ << " UNLOCKED "
+ << &lock << " at "
+ << line << " in " << file << endl;
+#endif
+ }
+ }
+
+ bool locked() { return _locked; }
+
+ private:
+ NonBlockingRWLock& lock;
+ bool _locked;
+#ifdef DEBUG_LOCK_MONITOR
+ unsigned long line;
+ const char * file;
+#endif
+};
+
+
} /* namespace */
#endif /* __pbd_lockmonitor_h__*/