Removed trailing spaces
[libcds.git] / cds / threading / details / pthread_manager.h
1 /*
2     This file is a part of libcds - Concurrent Data Structures library
3
4     (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017
5
6     Source code repo: http://github.com/khizmax/libcds/
7     Download: http://sourceforge.net/projects/libcds/files/
8
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions are met:
11
12     * Redistributions of source code must retain the above copyright notice, this
13       list of conditions and the following disclaimer.
14
15     * Redistributions in binary form must reproduce the above copyright notice,
16       this list of conditions and the following disclaimer in the documentation
17       and/or other materials provided with the distribution.
18
19     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28     OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #ifndef CDSLIB_THREADING_DETAILS_PTHREAD_MANAGER_H
32 #define CDSLIB_THREADING_DETAILS_PTHREAD_MANAGER_H
33
34 #include <system_error>
35 #include <stdio.h>
36 #include <pthread.h>
37 #include <cds/threading/details/_common.h>
38 #include <cds/details/throw_exception.h>
39
40 //@cond
41 namespace cds { namespace threading {
42
43     /// cds::threading::Manager implementation based on pthread thread-specific data functions
44     CDS_CXX11_INLINE_NAMESPACE namespace pthread {
45
46         /// Thread-specific data manager based on pthread thread-specific data functions
47         /**
48             Manager throws an exception of Manager::pthread_exception class if an error occurs
49         */
50         class Manager {
51         private :
52             /// pthread error code type
53             typedef int pthread_error_code;
54
55             /// pthread exception
56             class pthread_exception: public std::system_error
57             {
58             public:
59                 /// Exception constructor
60                 pthread_exception( int nCode, const char * pszFunction )
61                     : std::system_error( nCode, std::system_category(), pszFunction )
62                 {}
63             };
64
65             /// pthread TLS key holder
66             struct Holder {
67             //@cond
68                 static pthread_key_t   m_key;
69
70                 static void key_destructor(void * p)
71                 {
72                     if ( p ) {
73                         reinterpret_cast<ThreadData *>(p)->fini();
74                         delete reinterpret_cast<ThreadData *>(p);
75                     }
76                 }
77
78                 static void init()
79                 {
80                     pthread_error_code  nErr;
81                     if ( ( nErr = pthread_key_create( &m_key, key_destructor )) != 0 )
82                         CDS_THROW_EXCEPTION( pthread_exception( nErr, "pthread_key_create" ));
83                 }
84
85                 static void fini()
86                 {
87                     pthread_error_code  nErr;
88                     if ( ( nErr = pthread_key_delete( m_key )) != 0 )
89                         CDS_THROW_EXCEPTION( pthread_exception( nErr, "pthread_key_delete" ));
90                 }
91
92                 static ThreadData *    get()
93                 {
94                     return reinterpret_cast<ThreadData *>( pthread_getspecific( m_key ));
95                 }
96
97                 static void alloc()
98                 {
99                     pthread_error_code  nErr;
100                     ThreadData * pData = new ThreadData;
101                     if ( ( nErr = pthread_setspecific( m_key, pData )) != 0 )
102                         CDS_THROW_EXCEPTION( pthread_exception( nErr, "pthread_setspecific" ));
103                 }
104                 static void free()
105                 {
106                     ThreadData * p = get();
107                     pthread_setspecific( m_key, nullptr );
108                     if ( p )
109                         delete p;
110                 }
111             //@endcond
112             };
113
114             //@cond
115             enum EThreadAction {
116                 do_getData,
117                 do_attachThread,
118                 do_detachThread,
119                 do_checkData,
120                 init_holder,
121                 fini_holder
122             };
123             //@endcond
124
125             //@cond
126             static ThreadData * _threadData( EThreadAction nAction )
127             {
128                 switch ( nAction ) {
129                     case do_getData:
130                         return Holder::get();
131                     case do_checkData:
132                         return Holder::get();
133                     case do_attachThread:
134                         if ( Holder::get() == nullptr )
135                             Holder::alloc();
136                         return Holder::get();
137                     case do_detachThread:
138                         Holder::free();
139                         return nullptr;
140                     case init_holder:
141                     case fini_holder:
142                         break;
143                     default:
144                         assert( false ) ;   // anything forgotten?..
145                 }
146                 assert(false)   ;   // how did we get here?
147                 return nullptr;
148             }
149             //@endcond
150
151         public:
152             /// Initialize manager
153             /**
154                 This function is automatically called by cds::Initialize
155             */
156             static void init()
157             {
158                 Holder::init();
159             }
160
161             /// Terminate manager
162             /**
163                 This function is automatically called by cds::Terminate
164             */
165             static void fini()
166             {
167                 Holder::fini();
168             }
169
170             /// Checks whether current thread is attached to \p libcds feature or not.
171             static bool isThreadAttached()
172             {
173                 return _threadData( do_checkData ) != nullptr;
174             }
175
176             /// This method must be called in beginning of thread execution
177             /**
178                 If TLS pointer to manager's data is \p nullptr, pthread_exception is thrown
179                 with code = -1.
180                 If an error occurs in call of pthread API function, pthread_exception is thrown
181                 with pthread error code.
182             */
183             static void attachThread()
184             {
185                 ThreadData * pData = _threadData( do_attachThread );
186                 assert( pData );
187
188                 if ( pData ) {
189                     pData->init();
190                 }
191                 else
192                     CDS_THROW_EXCEPTION( pthread_exception( -1, "cds::threading::pthread::Manager::attachThread" ));
193             }
194
195             /// This method must be called in end of thread execution
196             /**
197                 If TLS pointer to manager's data is \p nullptr, pthread_exception is thrown
198                 with code = -1.
199                 If an error occurs in call of pthread API function, pthread_exception is thrown
200                 with pthread error code.
201             */
202             static void detachThread()
203             {
204                 ThreadData * pData = _threadData( do_getData );
205                 assert( pData );
206
207                 if ( pData ) {
208                     if ( pData->fini())
209                         _threadData( do_detachThread );
210                 }
211                 else
212                     CDS_THROW_EXCEPTION( pthread_exception( -1, "cds::threading::pthread::Manager::detachThread" ));
213             }
214
215             /// Returns ThreadData pointer for the current thread
216             static ThreadData * thread_data()
217             {
218                 return _threadData( do_getData );
219             }
220
221             //@cond
222             static size_t fake_current_processor()
223             {
224                 return _threadData( do_getData )->fake_current_processor();
225             }
226             //@endcond
227
228         };
229
230     } // namespace pthread
231 }} // namespace cds::threading
232 //@endcond
233
234 #endif // #ifndef CDSLIB_THREADING_DETAILS_PTHREAD_MANAGER_H