Missed update to private test repo version.
[dcpomatic.git] / src / lib / signaller.h
1 /*
2     Copyright (C) 2015-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21
22 #ifndef DCPOMATIC_SIGNALLER_H
23 #define DCPOMATIC_SIGNALLER_H
24
25
26 #include "signal_manager.h"
27 #include <boost/thread/mutex.hpp>
28
29
30 class WrapperBase
31 {
32 public:
33         virtual ~WrapperBase () {}
34
35         /* Can be called from any thread */
36         void invalidate ()
37         {
38                 boost::mutex::scoped_lock lm (_mutex);
39                 _valid = false;
40         }
41
42         bool finished () const {
43                 boost::mutex::scoped_lock lm (_mutex, boost::try_to_lock);
44                 if (!lm) {
45                         /* It's possible that emission of this
46                            wrapper's signal causes another signal to
47                            be emitted, which causes finished() on this
48                            wrapper to be called (by Signaller::emit).
49                            In this case, just say that the wrapper is
50                            not yet finished.
51                         */
52                         return false;
53                 }
54                 return _finished;
55         }
56
57 protected:
58         /* Protect _valid and _finished */
59         mutable boost::mutex _mutex;
60         bool _valid = true;
61         bool _finished = false;
62 };
63
64
65 /** Helper class to manage lifetime of signals, specifically to address
66  *  the problem where an object containing a signal is deleted before
67  *  its signal is emitted.
68  */
69 template <class T>
70 class Wrapper : public WrapperBase
71 {
72 public:
73         explicit Wrapper (T signal)
74                 : _signal (signal)
75         {
76
77         }
78
79         /* Called by the UI thread only */
80         void signal ()
81         {
82                 boost::mutex::scoped_lock lm (_mutex);
83                 if (_valid) {
84                         _signal ();
85                 }
86                 _finished = true;
87         }
88
89 private:
90         T _signal;
91 };
92
93
94 /** Parent for any class which needs to raise cross-thread signals (from non-UI
95  *  to UI).  Subclasses should call, e.g. emit (boost::bind (boost::ref (MySignal), foo, bar));
96  */
97 class Signaller
98 {
99 public:
100         /* Can be called from any thread */
101         virtual ~Signaller () {
102                 boost::mutex::scoped_lock lm (_signaller_mutex);
103                 for (auto i: _wrappers) {
104                         i->invalidate();
105                 }
106         }
107
108         /* Can be called from any thread */
109         template <class T>
110         void emit (T signal)
111         {
112                 auto w = new Wrapper<T> (signal);
113                 if (signal_manager) {
114                         signal_manager->emit (boost::bind(&Wrapper<T>::signal, w));
115                 }
116
117                 boost::mutex::scoped_lock lm (_signaller_mutex);
118
119                 /* Clean up finished Wrappers */
120                 auto i = _wrappers.begin ();
121                 while (i != _wrappers.end ()) {
122                         auto tmp = i;
123                         ++tmp;
124                         if ((*i)->finished ()) {
125                                 delete *i;
126                                 _wrappers.erase (i);
127                         }
128                         i = tmp;
129                 }
130
131                 /* Add the new one */
132                 _wrappers.push_back (w);
133         }
134
135 private:
136         /* Protect _wrappers */
137         boost::mutex _signaller_mutex;
138         std::list<WrapperBase*> _wrappers;
139 };
140
141
142 #endif