Line data Source code
1 :
2 : /* Copyright (c) 2005-2014, Stefan Eilemann <eile@equalizergraphics.com>
3 : * 2012, Marwan Abdellah <marwan.abdellah@epfl.ch>
4 : * 2011-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 : #include "thread.h"
21 :
22 : #include "os.h"
23 : #include "debug.h"
24 : #include "lock.h"
25 : #include "log.h"
26 : #include "monitor.h"
27 : #include "rng.h"
28 : #include "scopedMutex.h"
29 : #include "sleep.h"
30 : #include "spinLock.h"
31 :
32 : #include <boost/lexical_cast.hpp>
33 : #include <errno.h>
34 : #include <pthread.h>
35 : #include <algorithm>
36 : #include <map>
37 :
38 : // Experimental Win32 thread pinning
39 : #ifdef _WIN32
40 : //# define LB_WIN32_THREAD_AFFINITY
41 : # pragma message ("Thread affinity not supported on WIN32")
42 : #endif
43 :
44 : #ifdef __linux__
45 : # include <sys/prctl.h>
46 : #endif
47 :
48 : #ifdef LUNCHBOX_USE_HWLOC
49 : # include <hwloc.h>
50 : #endif
51 :
52 : #include "detail/threadID.h"
53 :
54 : namespace lunchbox
55 : {
56 : namespace
57 : {
58 27 : a_int32_t _threadIDs;
59 :
60 : enum ThreadState //!< The current state of a thread.
61 : {
62 : STATE_STOPPED,
63 : STATE_STARTING, // start() in progress
64 : STATE_RUNNING,
65 : STATE_STOPPING // child no longer active, join() not yet called
66 : };
67 : }
68 :
69 : namespace detail
70 : {
71 1644 : class Thread
72 : {
73 : public:
74 1644 : Thread() : state( STATE_STOPPED ), index( ++_threadIDs ) {}
75 :
76 : lunchbox::ThreadID id;
77 : Monitor< ThreadState > state;
78 : int32_t index;
79 : };
80 : }
81 :
82 1644 : Thread::Thread()
83 1644 : : _impl( new detail::Thread )
84 : {
85 1644 : }
86 :
87 0 : Thread::Thread( const Thread& )
88 0 : : _impl( new detail::Thread )
89 : {
90 0 : }
91 :
92 1644 : Thread::~Thread()
93 : {
94 1644 : delete _impl;
95 1644 : }
96 :
97 257 : bool Thread::isStopped() const
98 : {
99 257 : return ( _impl->state == STATE_STOPPED );
100 : }
101 :
102 1 : bool Thread::isRunning() const
103 : {
104 1 : return ( _impl->state == STATE_RUNNING );
105 : }
106 :
107 1668 : void* Thread::runChild( void* arg )
108 : {
109 1668 : Thread* thread = static_cast<Thread*>(arg);
110 1668 : thread->_runChild();
111 0 : return 0; // not reached
112 : }
113 :
114 1668 : void Thread::_runChild()
115 : {
116 1668 : setName( boost::lexical_cast< std::string >( _impl->index ));
117 1668 : _impl->id._impl->pthread = pthread_self();
118 :
119 1668 : if( !init( ))
120 : {
121 5 : LBWARN << "Thread " << className( this ) << " failed to initialize"
122 4 : << std::endl;
123 1 : _impl->state = STATE_STOPPED;
124 1 : pthread_exit( 0 );
125 : LBUNREACHABLE;
126 : }
127 :
128 1667 : _impl->state = STATE_RUNNING;
129 8331 : LBDEBUG << "Thread #" << _impl->index << " type " << className( *this )
130 6664 : << " successfully initialized" << std::endl;
131 :
132 1663 : run();
133 1386 : LBVERB << "Thread " << className( this ) << " finished" << std::endl;
134 1386 : this->exit();
135 :
136 0 : LBUNREACHABLE;
137 0 : }
138 :
139 1668 : bool Thread::start()
140 : {
141 1668 : if( _impl->state != STATE_STOPPED )
142 0 : return false;
143 :
144 1668 : _impl->state = STATE_STARTING;
145 :
146 : pthread_attr_t attributes;
147 1668 : pthread_attr_init( &attributes );
148 1668 : pthread_attr_setscope( &attributes, PTHREAD_SCOPE_SYSTEM );
149 :
150 1668 : int nTries = 10;
151 3336 : while( nTries-- )
152 : {
153 : const int error = pthread_create( &_impl->id._impl->pthread,
154 1668 : &attributes, runChild, this );
155 1668 : if( error == 0 ) // succeeded
156 : {
157 1668 : LBVERB << "Created pthread " << this << std::endl;
158 1668 : break;
159 : }
160 0 : if( error != EAGAIN || nTries == 0 )
161 : {
162 0 : LBWARN << "Could not create thread: " << strerror( error )
163 0 : << std::endl;
164 0 : return false;
165 : }
166 0 : sleep( 1 ); // Give EAGAIN some time to recover
167 : }
168 :
169 : // avoid memleak, we don't use pthread_join
170 1668 : pthread_detach( _impl->id._impl->pthread );
171 1668 : _impl->state.waitNE( STATE_STARTING );
172 1668 : return (_impl->state != STATE_STOPPED);
173 : }
174 :
175 1642 : void Thread::exit()
176 : {
177 1642 : LBASSERTINFO( isCurrent(), "Thread::exit not called from child thread" );
178 1642 : LBVERB << "Exiting thread " << className( this ) << std::endl;
179 1642 : Log::instance().forceFlush();
180 1642 : Log::instance().exit();
181 :
182 1642 : _impl->state = STATE_STOPPING;
183 1641 : pthread_exit( 0 );
184 : LBUNREACHABLE;
185 : }
186 :
187 25 : void Thread::cancel()
188 : {
189 25 : LBASSERTINFO( !isCurrent(), "Thread::cancel called from child thread" );
190 :
191 25 : LBVERB << "Canceling thread " << className( this ) << std::endl;
192 25 : _impl->state = STATE_STOPPING;
193 :
194 25 : const int error = pthread_cancel( _impl->id._impl->pthread );
195 25 : if( error != 0 )
196 0 : LBWARN << "Could not cancel thread: " << strerror( error ) << std::endl;
197 25 : }
198 :
199 1899 : bool Thread::join()
200 : {
201 1899 : if( _impl->state == STATE_STOPPED )
202 1 : return false;
203 1898 : if( isCurrent( )) // can't join self
204 256 : return false;
205 :
206 1642 : _impl->state.waitNE( STATE_RUNNING );
207 1642 : _impl->state = STATE_STOPPED;
208 :
209 1642 : LBVERB << "Joined thread " << className( this ) << std::endl;
210 1642 : return true;
211 : }
212 :
213 3565 : bool Thread::isCurrent() const
214 : {
215 3565 : return pthread_equal( pthread_self(), _impl->id._impl->pthread );
216 : }
217 :
218 3231922 : ThreadID Thread::getSelfThreadID()
219 : {
220 3231922 : ThreadID threadID;
221 3241763 : threadID._impl->pthread = pthread_self();
222 3240404 : return threadID;
223 : }
224 :
225 0 : void Thread::yield()
226 : {
227 : #ifdef _MSC_VER
228 : ::Sleep( 0 ); // sleeps thread
229 : // or ::SwitchToThread() ? // switches to another waiting thread, if exists
230 : #elif defined (__APPLE__)
231 : ::pthread_yield_np();
232 : #else
233 0 : ::sched_yield();
234 : #endif
235 0 : }
236 :
237 : #ifdef _MSC_VER
238 : # ifndef MS_VC_EXCEPTION
239 : # define MS_VC_EXCEPTION 0x406D1388
240 : # endif
241 :
242 : # pragma pack(push,8)
243 : typedef struct tagTHREADNAME_INFO
244 : {
245 : DWORD dwType; // Must be 0x1000.
246 : LPCSTR szName; // Pointer to name (in user addr space).
247 : DWORD dwThreadID; // Thread ID (-1=caller thread).
248 : DWORD dwFlags; // Reserved for future use, must be zero.
249 : } THREADNAME_INFO;
250 : # pragma pack(pop)
251 : static void _setVCName( const char* name )
252 : {
253 : ::Sleep(10);
254 :
255 : THREADNAME_INFO info;
256 : info.dwType = 0x1000;
257 : info.szName = name;
258 : info.dwThreadID = GetCurrentThreadId();
259 : info.dwFlags = 0;
260 :
261 : __try
262 : {
263 : RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),
264 : (ULONG_PTR*)&info );
265 : }
266 : __except(EXCEPTION_EXECUTE_HANDLER)
267 : {
268 : }
269 : }
270 : #endif
271 :
272 1689 : void Thread::setName( const std::string& name )
273 : {
274 1689 : Log::instance().setThreadName( name );
275 :
276 : #ifdef _MSC_VER
277 : # ifndef NDEBUG
278 : _setVCName( name.c_str( ));
279 : # endif
280 : #elif __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
281 : pthread_setname_np( name.c_str( ));
282 : #elif defined(__linux__)
283 1689 : prctl( PR_SET_NAME, name.c_str(), 0, 0, 0 );
284 : #else
285 : // Not implemented
286 : LBVERB << "Thread::setName() not implemented" << std::endl;
287 : #endif
288 1689 : }
289 :
290 : #ifdef LUNCHBOX_USE_HWLOC
291 0 : static hwloc_bitmap_t _getCpuSet( const int32_t affinity,
292 : hwloc_topology_t topology )
293 : {
294 0 : hwloc_bitmap_t cpuSet = hwloc_bitmap_alloc(); // HWloc CPU set
295 0 : hwloc_bitmap_zero( cpuSet ); // Initialize to zeros
296 :
297 0 : if( affinity >= Thread::CORE )
298 : {
299 0 : const int32_t coreIndex = affinity - Thread::CORE;
300 0 : if( hwloc_get_obj_by_type( topology, HWLOC_OBJ_CORE, coreIndex ) == 0 )
301 : {
302 0 : LBWARN << "Core " << coreIndex << " does not exist in the topology"
303 0 : << std::endl;
304 0 : return cpuSet;
305 : }
306 :
307 : // Getting the core object #coreIndex
308 : const hwloc_obj_t coreObj = hwloc_get_obj_by_type( topology,
309 : HWLOC_OBJ_CORE,
310 0 : coreIndex );
311 : // Get the CPU set associated with the specified core
312 0 : cpuSet = coreObj->allowed_cpuset;
313 0 : return cpuSet;
314 : }
315 :
316 0 : if( affinity == Thread::NONE )
317 0 : return cpuSet;
318 :
319 : // Sets the affinity to a specific CPU or "socket"
320 0 : LBASSERT( affinity >= Thread::SOCKET && affinity < Thread::SOCKET_MAX );
321 0 : const int32_t socketIndex = affinity - Thread::SOCKET;
322 :
323 0 : if( hwloc_get_obj_by_type( topology, HWLOC_OBJ_SOCKET, socketIndex ) == 0 )
324 : {
325 0 : LBWARN << "Socket " << socketIndex << " does not exist in the topology"
326 0 : << std::endl;
327 0 : return cpuSet;
328 : }
329 :
330 : // Getting the CPU object #cpuIndex (subtree node)
331 : const hwloc_obj_t socketObj = hwloc_get_obj_by_type( topology,
332 : HWLOC_OBJ_SOCKET,
333 0 : socketIndex );
334 : // Get the CPU set associated with the specified socket
335 0 : hwloc_bitmap_copy( cpuSet, socketObj->allowed_cpuset );
336 0 : return cpuSet;
337 : }
338 : #endif
339 :
340 0 : void Thread::setAffinity( const int32_t affinity )
341 : {
342 0 : if( affinity == Thread::NONE )
343 0 : return;
344 :
345 : #ifdef LUNCHBOX_USE_HWLOC
346 : hwloc_topology_t topology;
347 0 : hwloc_topology_init( &topology ); // Allocate & initialize the topology
348 0 : hwloc_topology_load( topology ); // Perform HW topology detection
349 0 : const hwloc_bitmap_t cpuSet = _getCpuSet( affinity, topology );
350 : const int result = hwloc_set_cpubind( topology, cpuSet,
351 0 : HWLOC_CPUBIND_THREAD );
352 : char* cpuSetString;
353 0 : hwloc_bitmap_asprintf( &cpuSetString, cpuSet );
354 :
355 0 : if( result == 0 )
356 : {
357 0 : LBVERB << "Bound to cpu set " << cpuSetString << std::endl;
358 : }
359 : else
360 : {
361 0 : LBWARN << "Error binding to cpu set " << cpuSetString << std::endl;
362 : }
363 0 : ::free( cpuSetString );
364 0 : hwloc_bitmap_free( cpuSet );
365 0 : hwloc_topology_destroy( topology );
366 :
367 : #else
368 : LBWARN << "Thread::setAffinity not implemented, hwloc library missing"
369 : << std::endl;
370 : #endif
371 : }
372 :
373 0 : std::ostream& operator << ( std::ostream& os, const Thread::Affinity affinity )
374 : {
375 0 : if( affinity == Thread::NONE )
376 0 : return os << "No affinity";
377 0 : if( affinity >= Thread::CORE )
378 0 : return os << "Core " << affinity - Thread::CORE;
379 :
380 0 : LBASSERT( affinity >= Thread::SOCKET && affinity < Thread::SOCKET_MAX );
381 0 : return os << "Socket " << affinity - Thread::SOCKET;
382 : }
383 :
384 : #if 0
385 : std::ostream& operator << ( std::ostream& os, const Thread* thread )
386 : {
387 : os << "Thread " << thread->_impl->id << " state "
388 : << ( thread->_impl->state == Thread::STATE_STOPPED ? "stopped" :
389 : thread->_impl->state == Thread::STATE_STARTING ? "starting" :
390 : thread->_impl->state == Thread::STATE_RUNNING ? "running" :
391 : thread->_impl->state == Thread::STATE_STOPPING ? "stopping" :
392 : "unknown" );
393 :
394 : #ifdef PTW32_VERSION
395 : os << " called from " << pthread_self().p;
396 : #else
397 : os << " called from " << pthread_self();
398 : #endif
399 :
400 : return os;
401 : }
402 : #endif
403 81 : }
|