Fix various points reported by cppcheck (#5656)
[oweals/minetest.git] / src / util / thread.h
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifndef UTIL_THREAD_HEADER
21 #define UTIL_THREAD_HEADER
22
23 #include "../irrlichttypes.h"
24 #include "../threading/thread.h"
25 #include "../threading/mutex.h"
26 #include "../threading/mutex_auto_lock.h"
27 #include "porting.h"
28 #include "log.h"
29 #include "container.h"
30
31 template<typename T>
32 class MutexedVariable
33 {
34 public:
35         MutexedVariable(const T &value):
36                 m_value(value)
37         {}
38
39         T get()
40         {
41                 MutexAutoLock lock(m_mutex);
42                 return m_value;
43         }
44
45         void set(const T &value)
46         {
47                 MutexAutoLock lock(m_mutex);
48                 m_value = value;
49         }
50
51         // You pretty surely want to grab the lock when accessing this
52         T m_value;
53 private:
54         Mutex m_mutex;
55 };
56
57 /*
58         A single worker thread - multiple client threads queue framework.
59 */
60 template<typename Key, typename T, typename Caller, typename CallerData>
61 class GetResult {
62 public:
63         Key key;
64         T item;
65         std::pair<Caller, CallerData> caller;
66 };
67
68 template<typename Key, typename T, typename Caller, typename CallerData>
69 class ResultQueue : public MutexedQueue<GetResult<Key, T, Caller, CallerData> > {
70 };
71
72 template<typename Caller, typename Data, typename Key, typename T>
73 class CallerInfo {
74 public:
75         Caller caller;
76         Data data;
77         ResultQueue<Key, T, Caller, Data> *dest;
78 };
79
80 template<typename Key, typename T, typename Caller, typename CallerData>
81 class GetRequest {
82 public:
83         GetRequest() {}
84         ~GetRequest() {}
85
86         GetRequest(const Key &a_key): key(a_key)
87         {
88         }
89
90         Key key;
91         std::list<CallerInfo<Caller, CallerData, Key, T> > callers;
92 };
93
94 /**
95  * Notes for RequestQueue usage
96  * @param Key unique key to identify a request for a specific resource
97  * @param T ?
98  * @param Caller unique id of calling thread
99  * @param CallerData data passed back to caller
100  */
101 template<typename Key, typename T, typename Caller, typename CallerData>
102 class RequestQueue {
103 public:
104         bool empty()
105         {
106                 return m_queue.empty();
107         }
108
109         void add(const Key &key, Caller caller, CallerData callerdata,
110                 ResultQueue<Key, T, Caller, CallerData> *dest)
111         {
112                 typename std::deque<GetRequest<Key, T, Caller, CallerData> >::iterator i;
113                 typename std::list<CallerInfo<Caller, CallerData, Key, T> >::iterator j;
114
115                 {
116                         MutexAutoLock lock(m_queue.getMutex());
117
118                         /*
119                                 If the caller is already on the list, only update CallerData
120                         */
121                         for (i = m_queue.getQueue().begin(); i != m_queue.getQueue().end(); ++i) {
122                                 GetRequest<Key, T, Caller, CallerData> &request = *i;
123                                 if (request.key != key)
124                                         continue;
125
126                                 for (j = request.callers.begin(); j != request.callers.end(); ++j) {
127                                         CallerInfo<Caller, CallerData, Key, T> &ca = *j;
128                                         if (ca.caller == caller) {
129                                                 ca.data = callerdata;
130                                                 return;
131                                         }
132                                 }
133
134                                 CallerInfo<Caller, CallerData, Key, T> ca;
135                                 ca.caller = caller;
136                                 ca.data = callerdata;
137                                 ca.dest = dest;
138                                 request.callers.push_back(ca);
139                                 return;
140                         }
141                 }
142
143                 /*
144                         Else add a new request to the queue
145                 */
146
147                 GetRequest<Key, T, Caller, CallerData> request;
148                 request.key = key;
149                 CallerInfo<Caller, CallerData, Key, T> ca;
150                 ca.caller = caller;
151                 ca.data = callerdata;
152                 ca.dest = dest;
153                 request.callers.push_back(ca);
154
155                 m_queue.push_back(request);
156         }
157
158         GetRequest<Key, T, Caller, CallerData> pop(unsigned int timeout_ms)
159         {
160                 return m_queue.pop_front(timeout_ms);
161         }
162
163         GetRequest<Key, T, Caller, CallerData> pop()
164         {
165                 return m_queue.pop_frontNoEx();
166         }
167
168         void pushResult(GetRequest<Key, T, Caller, CallerData> req, T res)
169         {
170                 for (typename std::list<CallerInfo<Caller, CallerData, Key, T> >::iterator
171                                 i = req.callers.begin();
172                                 i != req.callers.end(); ++i) {
173                         CallerInfo<Caller, CallerData, Key, T> &ca = *i;
174
175                         GetResult<Key,T,Caller,CallerData> result;
176
177                         result.key = req.key;
178                         result.item = res;
179                         result.caller.first = ca.caller;
180                         result.caller.second = ca.data;
181
182                         ca.dest->push_back(result);
183                 }
184         }
185
186 private:
187         MutexedQueue<GetRequest<Key, T, Caller, CallerData> > m_queue;
188 };
189
190 class UpdateThread : public Thread
191 {
192 public:
193         UpdateThread(const std::string &name) : Thread(name + "Update") {}
194         ~UpdateThread() {}
195
196         void deferUpdate() { m_update_sem.post(); }
197
198         void stop()
199         {
200                 Thread::stop();
201
202                 // give us a nudge
203                 m_update_sem.post();
204         }
205
206         void *run()
207         {
208                 DSTACK(FUNCTION_NAME);
209                 BEGIN_DEBUG_EXCEPTION_HANDLER
210
211                 while (!stopRequested()) {
212                         m_update_sem.wait();
213                         // Set semaphore to 0
214                         while (m_update_sem.wait(0));
215
216                         if (stopRequested()) break;
217
218                         doUpdate();
219                 }
220
221                 END_DEBUG_EXCEPTION_HANDLER
222
223                 return NULL;
224         }
225
226 protected:
227         virtual void doUpdate() = 0;
228
229 private:
230         Semaphore m_update_sem;
231 };
232
233 #endif
234