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