Supporters update.
[dcpomatic.git] / src / lib / change_signaller.h
index 55a5f2dc57f90b5d8307fbd882db9894d02714ae..1d7d482df2539d8c989609e4cf162a323ceb7bfd 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2018 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2018-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
+
 #ifndef DCPOMATIC_CHANGE_SIGNALLER_H
 #define DCPOMATIC_CHANGE_SIGNALLER_H
 
-#include <boost/noncopyable.hpp>
 
-template <class T>
-class ChangeSignaller : public boost::noncopyable
+#include <boost/thread.hpp>
+#include <vector>
+
+
+enum class ChangeType
+{
+       PENDING,
+       DONE,
+       CANCELLED
+};
+
+
+template <class T, class P>
+class ChangeSignal
 {
 public:
-       ChangeSignaller (T* t, int p)
-               : _thing (t)
-               , _property (p)
-               , _done (true)
+       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)
        {
-               _thing->signal_change (ChangeType::PENDING, _property);
+               if (_suspended) {
+                       boost::mutex::scoped_lock lm(_mutex);
+                       _pending.push_back(signal);
+               } else {
+                       signal.thing->signal_change(signal.type, signal.property);
+               }
        }
 
-       ~ChangeSignaller ()
+       void suspend()
        {
-               if (_done) {
-                       _thing->signal_change (ChangeType::DONE, _property);
-               } else {
-                       _thing->signal_change (ChangeType::CANCELLED, _property);
+               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)
+       {
+               ChangeSignalDespatcher<T, P>::instance()->signal_change({_thing, _property, ChangeType::PENDING});
+       }
+
+       ~ChangeSignaller ()
+       {
+               ChangeSignalDespatcher<T, P>::instance()->signal_change({_thing, _property, _done ? ChangeType::DONE : ChangeType::CANCELLED});
        }
 
        void abort ()
@@ -51,8 +140,9 @@ public:
 
 private:
        T* _thing;
-       int _property;
+       P _property;
        bool _done;
 };
 
+
 #endif