1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
|
// Copyright (c) 2006-2018 Maxim Khizhinsky
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef CDSLIB_THREADING_DETAILS_PTHREAD_MANAGER_H
#define CDSLIB_THREADING_DETAILS_PTHREAD_MANAGER_H
#include <system_error>
#include <stdio.h>
#include <pthread.h>
#include <cds/threading/details/_common.h>
#include <cds/details/throw_exception.h>
//@cond
namespace cds { namespace threading {
/// cds::threading::Manager implementation based on pthread thread-specific data functions
inline namespace pthread {
/// Thread-specific data manager based on pthread thread-specific data functions
/**
Manager throws an exception of Manager::pthread_exception class if an error occurs
*/
class Manager {
private :
/// pthread error code type
typedef int pthread_error_code;
/// pthread exception
class pthread_exception: public std::system_error
{
public:
/// Exception constructor
pthread_exception( int nCode, const char * pszFunction )
: std::system_error( nCode, std::system_category(), pszFunction )
{}
};
/// pthread TLS key holder
struct Holder {
//@cond
static pthread_key_t m_key;
static void key_destructor(void * p)
{
if ( p ) {
reinterpret_cast<ThreadData *>(p)->fini();
delete reinterpret_cast<ThreadData *>(p);
}
}
static void init()
{
pthread_error_code nErr;
if ( ( nErr = pthread_key_create( &m_key, key_destructor )) != 0 )
CDS_THROW_EXCEPTION( pthread_exception( nErr, "pthread_key_create" ));
}
static void fini()
{
pthread_error_code nErr;
if ( ( nErr = pthread_key_delete( m_key )) != 0 )
CDS_THROW_EXCEPTION( pthread_exception( nErr, "pthread_key_delete" ));
}
static ThreadData * get()
{
return reinterpret_cast<ThreadData *>( pthread_getspecific( m_key ));
}
static void alloc()
{
pthread_error_code nErr;
ThreadData * pData = new ThreadData;
if ( ( nErr = pthread_setspecific( m_key, pData )) != 0 )
CDS_THROW_EXCEPTION( pthread_exception( nErr, "pthread_setspecific" ));
}
static void free()
{
ThreadData * p = get();
pthread_setspecific( m_key, nullptr );
delete p;
}
//@endcond
};
//@cond
enum EThreadAction {
do_getData,
do_attachThread,
do_detachThread,
do_checkData,
init_holder,
fini_holder
};
//@endcond
//@cond
static ThreadData * _threadData( EThreadAction nAction )
{
switch ( nAction ) {
case do_getData:
return Holder::get();
case do_checkData:
return Holder::get();
case do_attachThread:
if ( Holder::get() == nullptr )
Holder::alloc();
return Holder::get();
case do_detachThread:
Holder::free();
return nullptr;
case init_holder:
case fini_holder:
break;
default:
assert( false ) ; // anything forgotten?..
}
assert(false) ; // how did we get here?
return nullptr;
}
//@endcond
public:
/// Initialize manager
/**
This function is automatically called by cds::Initialize
*/
static void init()
{
Holder::init();
}
/// Terminate manager
/**
This function is automatically called by cds::Terminate
*/
static void fini()
{
Holder::fini();
}
/// Checks whether current thread is attached to \p libcds feature or not.
static bool isThreadAttached()
{
return _threadData( do_checkData ) != nullptr;
}
/// This method must be called in beginning of thread execution
/**
If TLS pointer to manager's data is \p nullptr, pthread_exception is thrown
with code = -1.
If an error occurs in call of pthread API function, pthread_exception is thrown
with pthread error code.
*/
static void attachThread()
{
ThreadData * pData = _threadData( do_attachThread );
assert( pData );
if ( pData ) {
pData->init();
}
else
CDS_THROW_EXCEPTION( pthread_exception( -1, "cds::threading::pthread::Manager::attachThread" ));
}
/// This method must be called in end of thread execution
/**
If TLS pointer to manager's data is \p nullptr, pthread_exception is thrown
with code = -1.
If an error occurs in call of pthread API function, pthread_exception is thrown
with pthread error code.
*/
static void detachThread()
{
ThreadData * pData = _threadData( do_getData );
assert( pData );
if ( pData ) {
if ( pData->fini())
_threadData( do_detachThread );
}
else
CDS_THROW_EXCEPTION( pthread_exception( -1, "cds::threading::pthread::Manager::detachThread" ));
}
/// Returns ThreadData pointer for the current thread
static ThreadData * thread_data()
{
return _threadData( do_getData );
}
//@cond
static size_t fake_current_processor()
{
return _threadData( do_getData )->fake_current_processor();
}
//@endcond
};
} // namespace pthread
}} // namespace cds::threading
//@endcond
#endif // #ifndef CDSLIB_THREADING_DETAILS_PTHREAD_MANAGER_H
|