LocalPlayer::accelerateHorizontal: cleanups
[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_auto_lock.h"
26 #include "porting.h"
27 #include "log.h"
28 #include "container.h"
29
30 template<typename T>
31 class MutexedVariable
32 {
33 public:
34         MutexedVariable(const T &value):
35                 m_value(value)
36         {}
37
38         T get()
39         {
40                 MutexAutoLock lock(m_mutex);
41                 return m_value;
42         }
43
44         void set(const T &value)
45         {
46                 MutexAutoLock lock(m_mutex);
47                 m_value = value;
48         }
49
50         // You pretty surely want to grab the lock when accessing this
51         T m_value;
52 private:
53         std::mutex m_mutex;
54 };
55
56 /*
57         A single worker thread - multiple client threads queue framework.
58 */
59 template<typename Key, typename T, typename Caller, typename CallerData>
60 class GetResult {
61 public:
62         Key key;
63         T item;
64         std::pair<Caller, CallerData> caller;
65 };
66
67 template<typename Key, typename T, typename Caller, typename CallerData>
68 class ResultQueue : public MutexedQueue<GetResult<Key, T, Caller, CallerData> > {
69 };
70
71 template<typename Caller, typename Data, typename Key, typename T>
72 class CallerInfo {
73 public:
74         Caller caller;
75         Data data;
76         ResultQueue<Key, T, Caller, Data> *dest;
77 };
78
79 template<typename Key, typename T, typename Caller, typename CallerData>
80 class GetRequest {
81 public:
82         GetRequest() {}
83         ~GetRequest() {}
84
85         GetRequest(const Key &a_key): key(a_key)
86         {
87         }
88
89         Key key;
90         std::list<CallerInfo<Caller, CallerData, Key, T> > callers;
91 };
92
93 /**
94  * Notes for RequestQueue usage
95  * @param Key unique key to identify a request for a specific resource
96  * @param T ?
97  * @param Caller unique id of calling thread
98  * @param CallerData data passed back to caller
99  */
100 template<typename Key, typename T, typename Caller, typename CallerData>
101 class RequestQueue {
102 public:
103         bool empty()
104         {
105                 return m_queue.empty();
106         }
107
108         void add(const Key &key, Caller caller, CallerData callerdata,
109                 ResultQueue<Key, T, Caller, CallerData> *dest)
110         {
111                 typename std::deque<GetRequest<Key, T, Caller, CallerData> >::iterator i;
112                 typename std::list<CallerInfo<Caller, CallerData, Key, T> >::iterator j;
113
114                 {
115                         MutexAutoLock lock(m_queue.getMutex());
116
117                         /*
118                                 If the caller is already on the list, only update CallerData
119                         */
120                         for (i = m_queue.getQueue().begin(); i != m_queue.getQueue().end(); ++i) {
121                                 GetRequest<Key, T, Caller, CallerData> &request = *i;
122                                 if (request.key != key)
123                                         continue;
124
125                                 for (j = request.callers.begin(); j != request.callers.end(); ++j) {
126                                         CallerInfo<Caller, CallerData, Key, T> &ca = *j;
127                                         if (ca.caller == caller) {
128                                                 ca.data = callerdata;
129                                                 return;
130                                         }
131                                 }
132
133                                 CallerInfo<Caller, CallerData, Key, T> ca;
134                                 ca.caller = caller;
135                                 ca.data = callerdata;
136                                 ca.dest = dest;
137                                 request.callers.push_back(ca);
138                                 return;
139                         }
140                 }
141
142                 /*
143                         Else add a new request to the queue
144                 */
145
146                 GetRequest<Key, T, Caller, CallerData> request;
147                 request.key = key;
148                 CallerInfo<Caller, CallerData, Key, T> ca;
149                 ca.caller = caller;
150                 ca.data = callerdata;
151                 ca.dest = dest;
152                 request.callers.push_back(ca);
153
154                 m_queue.push_back(request);
155         }
156
157         GetRequest<Key, T, Caller, CallerData> pop(unsigned int timeout_ms)
158         {
159                 return m_queue.pop_front(timeout_ms);
160         }
161
162         GetRequest<Key, T, Caller, CallerData> pop()
163         {
164                 return m_queue.pop_frontNoEx();
165         }
166
167         void pushResult(GetRequest<Key, T, Caller, CallerData> req, T res)
168         {
169                 for (typename std::list<CallerInfo<Caller, CallerData, Key, T> >::iterator
170                                 i = req.callers.begin();
171                                 i != req.callers.end(); ++i) {
172                         CallerInfo<Caller, CallerData, Key, T> &ca = *i;
173
174                         GetResult<Key,T,Caller,CallerData> result;
175
176                         result.key = req.key;
177                         result.item = res;
178                         result.caller.first = ca.caller;
179                         result.caller.second = ca.data;
180
181                         ca.dest->push_back(result);
182                 }
183         }
184
185 private:
186         MutexedQueue<GetRequest<Key, T, Caller, CallerData> > m_queue;
187 };
188
189 class UpdateThread : public Thread
190 {
191 public:
192         UpdateThread(const std::string &name) : Thread(name + "Update") {}
193         ~UpdateThread() {}
194
195         void deferUpdate() { m_update_sem.post(); }
196
197         void stop()
198         {
199                 Thread::stop();
200
201                 // give us a nudge
202                 m_update_sem.post();
203         }
204
205         void *run()
206         {
207                 DSTACK(FUNCTION_NAME);
208                 BEGIN_DEBUG_EXCEPTION_HANDLER
209
210                 while (!stopRequested()) {
211                         m_update_sem.wait();
212                         // Set semaphore to 0
213                         while (m_update_sem.wait(0));
214
215                         if (stopRequested()) break;
216
217                         doUpdate();
218                 }
219
220                 END_DEBUG_EXCEPTION_HANDLER
221
222                 return NULL;
223         }
224
225 protected:
226         virtual void doUpdate() = 0;
227
228 private:
229         Semaphore m_update_sem;
230 };
231
232 #endif
233