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