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