Rename SafeStringStream -> locked_stringstream. Bump deps for removal of stringstream.
[dcpomatic.git] / src / lib / signaller.h
1 /*
2     Copyright (C) 2015 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 #ifndef DCPOMATIC_SIGNALLER_H
22 #define DCPOMATIC_SIGNALLER_H
23
24 #include "signal_manager.h"
25 #include <boost/thread/mutex.hpp>
26
27 class WrapperBase
28 {
29 public:
30         WrapperBase ()
31                 : _valid (true)
32                 , _finished (false)
33         {}
34
35         virtual ~WrapperBase () {}
36
37         /* Can be called from any thread */
38         void invalidate ()
39         {
40                 boost::mutex::scoped_lock lm (_mutex);
41                 _valid = false;
42         }
43
44         bool finished () const {
45                 boost::mutex::scoped_lock lm (_mutex, boost::try_to_lock);
46                 if (!lm) {
47                         /* It's possible that emission of this
48                            wrapper's signal causes another signal to
49                            be emitted, which causes finished() on this
50                            wrapper to be called (by Signaller::emit).
51                            In this case, just say that the wrapper is
52                            not yet finished.
53                         */
54                         return false;
55                 }
56                 return _finished;
57         }
58
59 protected:
60         /* Protect _valid and _finished */
61         mutable boost::mutex _mutex;
62         bool _valid;
63         bool _finished;
64 };
65
66 /** Helper class to manage lifetime of signals, specifically to address
67  *  the problem where an object containing a signal is deleted before
68  *  its signal is emitted.
69  */
70 template <class T>
71 class Wrapper : public WrapperBase
72 {
73 public:
74         Wrapper (T signal)
75                 : _signal (signal)
76         {
77
78         }
79
80         /* Called by the UI thread only */
81         void signal ()
82         {
83                 boost::mutex::scoped_lock lm (_mutex);
84                 if (_valid) {
85                         _signal ();
86                 }
87                 _finished = true;
88         }
89
90 private:
91         T _signal;
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 (std::list<WrapperBase*>::iterator i = _wrappers.begin(); i != _wrappers.end(); ++i) {
104                         (*i)->invalidate ();
105                 }
106         }
107
108         /* Can be called from any thread */
109         template <class T>
110         void emit (T signal)
111         {
112                 Wrapper<T>* 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                 std::list<WrapperBase*>::iterator i = _wrappers.begin ();
121                 while (i != _wrappers.end ()) {
122                         std::list<WrapperBase*>::iterator 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 #endif