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