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