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