diff options
| author | Carl Hetherington <cth@carlh.net> | 2023-05-09 01:33:45 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2023-05-10 21:35:46 +0200 |
| commit | 0f11f0c25465464b28db713cccd37d7d8de483e1 (patch) | |
| tree | 415dc88b05b951f5d590d0c41b52503fd8fd3da5 /src/lib/change_signaller.h | |
| parent | b168d211622f94a5240c945c1df03b0bed48d3bc (diff) | |
Allow signals to be blocked and resumed, and so make sure that a set
of content changes taken from an examiner are handled at the same
time.
Should fix DoM #2523.
Diffstat (limited to 'src/lib/change_signaller.h')
| -rw-r--r-- | src/lib/change_signaller.h | 102 |
1 files changed, 90 insertions, 12 deletions
diff --git a/src/lib/change_signaller.h b/src/lib/change_signaller.h index 8612cf426..1d7d482df 100644 --- a/src/lib/change_signaller.h +++ b/src/lib/change_signaller.h @@ -23,6 +23,10 @@ #define DCPOMATIC_CHANGE_SIGNALLER_H +#include <boost/thread.hpp> +#include <vector> + + enum class ChangeType { PENDING, @@ -32,29 +36,103 @@ enum class ChangeType template <class T, class P> +class ChangeSignal +{ +public: + ChangeSignal(T* thing_, P property_, ChangeType type_) + : thing(thing_) + , property(property_) + , type(type_) + {} + + T* thing; + P property; + ChangeType type; +}; + + +class ChangeSignalDespatcherBase +{ +protected: + static boost::mutex _instance_mutex; +}; + + +template <class T, class P> +class ChangeSignalDespatcher : public ChangeSignalDespatcherBase +{ +public: + ChangeSignalDespatcher() = default; + + ChangeSignalDespatcher(ChangeSignalDespatcher const&) = delete; + ChangeSignalDespatcher& operator=(ChangeSignalDespatcher const&) = delete; + + void signal_change(ChangeSignal<T, P> const& signal) + { + if (_suspended) { + boost::mutex::scoped_lock lm(_mutex); + _pending.push_back(signal); + } else { + signal.thing->signal_change(signal.type, signal.property); + } + } + + void suspend() + { + boost::mutex::scoped_lock lm(_mutex); + _suspended = true; + } + + void resume() + { + boost::mutex::scoped_lock lm(_mutex); + auto pending = _pending; + lm.unlock(); + + for (auto signal: pending) { + signal.thing->signal_change(signal.type, signal.property); + } + + lm.lock(); + _pending.clear(); + _suspended = false; + } + + static ChangeSignalDespatcher* instance() + { + static boost::mutex _instance_mutex; + static boost::mutex::scoped_lock lm(_instance_mutex); + static ChangeSignalDespatcher<T, P>* _instance; + if (!_instance) { + _instance = new ChangeSignalDespatcher<T, P>(); + } + return _instance; + } + +private: + std::vector<ChangeSignal<T, P>> _pending; + bool _suspended = false; + boost::mutex _mutex; +}; + + +template <class T, class P> class ChangeSignaller { public: ChangeSignaller (T* t, P p) - : _thing (t) - , _property (p) - , _done (true) + : _thing(t) + , _property(p) + , _done(true) { - _thing->signal_change (ChangeType::PENDING, _property); + ChangeSignalDespatcher<T, P>::instance()->signal_change({_thing, _property, ChangeType::PENDING}); } ~ChangeSignaller () { - if (_done) { - _thing->signal_change (ChangeType::DONE, _property); - } else { - _thing->signal_change (ChangeType::CANCELLED, _property); - } + ChangeSignalDespatcher<T, P>::instance()->signal_change({_thing, _property, _done ? ChangeType::DONE : ChangeType::CANCELLED}); } - ChangeSignaller (ChangeSignaller const&) = delete; - ChangeSignaller& operator= (ChangeSignaller const&) = delete; - void abort () { _done = false; |
