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