Line data Source code
1 :
2 : /* Copyright (c) 2005-2014, Stefan Eilemann <eile@equalizergraphics.com>
3 : * 2012, Marwan Abdellah <marwan.abdellah@epfl.ch>
4 : * 2012, Daniel Nachbaur <danielnachbaur@gmail.com>
5 : *
6 : * This library is free software; you can redistribute it and/or modify it under
7 : * the terms of the GNU Lesser General Public License version 2.1 as published
8 : * by the Free Software Foundation.
9 : *
10 : * This library is distributed in the hope that it will be useful, but WITHOUT
11 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 : * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
13 : * details.
14 : *
15 : * You should have received a copy of the GNU Lesser General Public License
16 : * along with this library; if not, write to the Free Software Foundation, Inc.,
17 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 : */
19 :
20 : #ifndef LUNCHBOX_THREAD_H
21 : #define LUNCHBOX_THREAD_H
22 :
23 : #include <lunchbox/api.h> // LUNCHBOX_API definition
24 : #include <lunchbox/debug.h> // debug macros in thread-safety checks
25 : #include <lunchbox/threadID.h> // member
26 : #include <lunchbox/types.h>
27 :
28 : #include <boost/noncopyable.hpp>
29 : #include <ostream>
30 :
31 : namespace lunchbox
32 : {
33 : namespace detail
34 : {
35 : class Thread;
36 : }
37 :
38 : /**
39 : * Utility class to execute code in a separate execution thread.
40 : * @deprecated Use Boost.Thread
41 : *
42 : * Example: @include tests/thread.cpp
43 : */
44 : class Thread
45 : {
46 : public:
47 : /** Enumeration values for thread affinity. */
48 : enum Affinity
49 : {
50 : NONE = 0, //!< Don't set any affinity
51 : CORE = 1, //!< Bind to a specific CPU core
52 : SOCKET = -65536, //!< Bind to all cores of a specific socket (CPU)
53 : SOCKET_MAX = -1024 //!< Highest bindable CPU
54 : };
55 :
56 : /** Construct a new thread. @version 1.0 */
57 : LUNCHBOX_API Thread();
58 :
59 : /** Copy constructor. @version 1.1.2 */
60 : LUNCHBOX_API explicit Thread(const Thread& from);
61 :
62 : /** Destruct the thread. @version 1.0 */
63 : LUNCHBOX_API virtual ~Thread();
64 :
65 : /**
66 : * Start the thread.
67 : *
68 : * @return true if the thread was launched and initialized successfully,
69 : * false otherwise.
70 : * @sa init(), run()
71 : * @version 1.0
72 : */
73 : LUNCHBOX_API virtual bool start();
74 :
75 : /**
76 : * The init function for the child thread.
77 : *
78 : * The parent thread will not be unlocked before this function has been
79 : * executed. If the thread initialization fails, that is, this method does
80 : * return false, the thread will be stopped and the start() method will
81 : * return false.
82 : *
83 : * @return the success value of the thread initialization.
84 : * @version 1.0
85 : */
86 1406 : virtual bool init() { return true; }
87 : /**
88 : * The entry function for the child thread.
89 : *
90 : * This method should contain the main execution routine for the thread and
91 : * is called after a successful init().
92 : *
93 : * @version 1.0
94 : */
95 : virtual void run() = 0;
96 :
97 : /**
98 : * Exit the child thread immediately.
99 : *
100 : * This function does not return. It is only to be called from the child
101 : * thread.
102 : *
103 : * @version 1.0
104 : */
105 : LUNCHBOX_API virtual void exit();
106 :
107 : /**
108 : * Cancel (stop) the child thread.
109 : *
110 : * This function is not to be called from the child thread.
111 : * @version 1.0
112 : */
113 : LUNCHBOX_API void cancel();
114 :
115 : /**
116 : * Wait for the exit of the child thread.
117 : *
118 : * @return true if the thread was joined, false otherwise.
119 : * @version 1.0
120 : */
121 : LUNCHBOX_API bool join();
122 :
123 : /**
124 : * Return if the thread is stopped.
125 : *
126 : * Note that the thread may be neither running nor stopped if it is
127 : * currently starting or stopping.
128 : *
129 : * @return true if the thread is stopped, false if not.
130 : * @version 1.0
131 : */
132 : LUNCHBOX_API bool isStopped() const;
133 :
134 : /**
135 : * Return if the thread is running.
136 : *
137 : * Note that the thread may be neither running nor stopped if it is
138 : * currently starting or stopping.
139 : *
140 : * @return true if the thread is running, false if not.
141 : * @version 1.0
142 : */
143 : LUNCHBOX_API bool isRunning() const;
144 :
145 : /**
146 : * @return true if the calling thread is the same thread as this
147 : * thread, false otherwise.
148 : * @version 1.0
149 : */
150 : LUNCHBOX_API bool isCurrent() const;
151 :
152 : /** @return a unique identifier for the calling thread. @version 1.0 */
153 : LUNCHBOX_API static ThreadID getSelfThreadID();
154 :
155 : /** @internal */
156 : LUNCHBOX_API static void yield();
157 :
158 : /** @internal */
159 : LUNCHBOX_API static void setName(const std::string& name);
160 :
161 : /** @internal
162 : * Set the affinity of the calling thread.
163 : *
164 : * If given a value greater or equal than CORE, this method binds the
165 : * calling thread to core affinity - CORE. If set to a value greater than
166 : * CPU and smaller than 0, this method binds the calling thread to all cores
167 : * of the given processor (affinity - CPU).
168 : *
169 : * @param affinity the affinity value (see above).
170 : */
171 : LUNCHBOX_API static void setAffinity(const int32_t affinity);
172 :
173 : private:
174 : detail::Thread* const _impl;
175 :
176 : Thread& operator=(const Thread&) { return *this; }
177 : static void* runChild(void* arg);
178 : void _runChild();
179 :
180 : LUNCHBOX_API friend void abort(bool);
181 : static void _dumpAll();
182 : };
183 :
184 : /** Output the affinity setting in human-readable form. @version 1.7.1 */
185 : LUNCHBOX_API std::ostream& operator<<(std::ostream&, const Thread::Affinity);
186 :
187 : // These thread-safety checks are for development purposes, to check that
188 : // certain objects are properly used within the framework. Leaving them enabled
189 : // during application development may cause false positives, e.g., when
190 : // threadsafety is ensured outside of the objects by the application.
191 :
192 : #ifndef NDEBUG
193 : #define LB_CHECK_THREADSAFETY
194 : #endif
195 :
196 : /** Declare a thread id variable to be used for thread-safety checks. */
197 : #define LB_TS_VAR(NAME) \
198 : public: \
199 : struct NAME##Struct \
200 : { \
201 : NAME##Struct() \
202 : : extMutex(false) \
203 : { \
204 : } \
205 : mutable lunchbox::ThreadID id; \
206 : mutable std::string name; \
207 : bool extMutex; \
208 : mutable lunchbox::ThreadID inRegion; \
209 : } NAME; \
210 : \
211 : private:
212 :
213 : #ifdef LB_CHECK_THREADSAFETY
214 : #define LB_TS_RESET(NAME) NAME.id = lunchbox::ThreadID();
215 :
216 : #define LB_TS_THREAD(NAME) \
217 : { \
218 : if (NAME.id == lunchbox::ThreadID()) \
219 : { \
220 : NAME.id = lunchbox::Thread::getSelfThreadID(); \
221 : NAME.name = lunchbox::Log::instance().getThreadName(); \
222 : LBVERB << "Functions for " << #NAME << " locked from" \
223 : << lunchbox::backtrace << std::endl; \
224 : } \
225 : if (!NAME.extMutex && NAME.id != lunchbox::Thread::getSelfThreadID()) \
226 : { \
227 : LBERROR << "Threadsafety check for " << #NAME \
228 : << " failed on object of type " \
229 : << lunchbox::className(this) << ", thread " \
230 : << lunchbox::Thread::getSelfThreadID() << " (" \
231 : << lunchbox::Log::instance().getThreadName() \
232 : << ") != " << NAME.id << " (" << NAME.name << ")" \
233 : << std::endl; \
234 : LBABORT("Non-threadsafe code called from two threads"); \
235 : } \
236 : }
237 :
238 : #define LB_TS_NOT_THREAD(NAME) \
239 : { \
240 : if (!NAME.extMutex && NAME.id != lunchbox::ThreadID()) \
241 : { \
242 : if (NAME.id == lunchbox::Thread::getSelfThreadID()) \
243 : { \
244 : LBERROR << "Threadsafety check for not " << #NAME \
245 : << " failed on object of type " \
246 : << lunchbox::className(this) << std::endl; \
247 : LBABORT("Code called from wrong thread"); \
248 : } \
249 : } \
250 : }
251 :
252 : /** @cond IGNORE */
253 : template <typename T>
254 : class ScopedThreadCheck : public boost::noncopyable
255 : {
256 : public:
257 1078328 : explicit ScopedThreadCheck(const T& data)
258 1078328 : : _data(data)
259 : {
260 1078328 : LBASSERTINFO(data.inRegion == lunchbox::ThreadID() ||
261 : data.inRegion == lunchbox::Thread::getSelfThreadID(),
262 : "Another thread already in critical region");
263 1078328 : data.inRegion = lunchbox::Thread::getSelfThreadID();
264 1078328 : }
265 :
266 1078328 : ~ScopedThreadCheck()
267 : {
268 1078328 : LBASSERTINFO(_data.inRegion == lunchbox::ThreadID() ||
269 : _data.inRegion == lunchbox::Thread::getSelfThreadID(),
270 : "Another thread entered critical region");
271 1078328 : _data.inRegion = lunchbox::ThreadID();
272 1078328 : }
273 :
274 : private:
275 : const T& _data;
276 : };
277 : /** @endcond */
278 :
279 : #define LB_TS_SCOPED(NAME) \
280 : lunchbox::ScopedThreadCheck<NAME##Struct> scoped##NAME##Check(NAME);
281 :
282 : #else
283 : #define LB_TS_RESET(NAME) \
284 : { \
285 : }
286 : #define LB_TS_THREAD(NAME) \
287 : { \
288 : }
289 : #define LB_TS_NOT_THREAD(NAME) \
290 : { \
291 : }
292 : #define LB_TS_SCOPED(NAME) \
293 : { \
294 : }
295 : #endif
296 : }
297 : #endif // LUNCHBOX_THREAD_H
|