LCOV - code coverage report
Current view: top level - lunchbox - thread.cpp (source / functions) Hit Total Coverage
Test: Lunchbox Lines: 78 138 56.5 %
Date: 2015-07-07 14:54:45 Functions: 17 23 73.9 %

          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 : }

Generated by: LCOV version 1.11