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