Changed: call throw_exception() function instead of ordinary throw
[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-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_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 #include <cds/details/throw_exception.h>
38
39 //@cond
40 namespace cds { namespace threading {
41
42     /// cds::threading::Manager implementation based on Windows TLS API
43     CDS_CXX11_INLINE_NAMESPACE namespace wintls {
44
45         /// Thread-specific data manager based on Windows TLS API
46         /**
47             Manager throws an exception of Manager::api_exception class if an error occurs
48         */
49         class Manager {
50         private :
51             /// Windows TLS API error code type
52             typedef DWORD api_error_code;
53
54             /// TLS API exception
55             class api_exception : public std::system_error
56             {
57             public:
58                 /// Exception constructor
59                 api_exception( api_error_code nCode, const char * pszFunction )
60                     : std::system_error( static_cast<int>(nCode), std::system_category(), pszFunction )
61                 {}
62             };
63
64             //@cond
65             enum EThreadAction {
66                 do_getData,
67                 do_attachThread,
68                 do_detachThread,
69                 do_checkData
70             };
71             //@endcond
72
73             //@cond
74             /// TLS key holder
75             struct Holder {
76                 static CDS_EXPORT_API DWORD m_key;
77
78                 static void init()
79                 {
80                     if ( m_key == TLS_OUT_OF_INDEXES ) {
81                         if ( ( m_key = ::TlsAlloc() ) == TLS_OUT_OF_INDEXES )
82                             CDS_THROW_EXCEPTION( api_exception( ::GetLastError(), "TlsAlloc" ));
83                     }
84                 }
85
86                 static void fini()
87                 {
88                     if ( m_key != TLS_OUT_OF_INDEXES ) {
89                         if ( ::TlsFree( m_key ) == 0 )
90                             CDS_THROW_EXCEPTION( api_exception( ::GetLastError(), "TlsFree" ));
91                         m_key = TLS_OUT_OF_INDEXES;
92                     }
93                 }
94
95                 static ThreadData *    get()
96                 {
97                     api_error_code  nErr;
98                     void * pData = ::TlsGetValue( m_key );
99                     if ( pData == nullptr && ( nErr = ::GetLastError() ) != ERROR_SUCCESS )
100                         CDS_THROW_EXCEPTION( api_exception( nErr, "TlsGetValue" ));
101                     return reinterpret_cast<ThreadData *>( pData );
102                 }
103
104                 static void alloc()
105                 {
106                     ThreadData * pData = new ThreadData;
107                     if ( !::TlsSetValue( m_key, pData ))
108                         CDS_THROW_EXCEPTION( api_exception( ::GetLastError(), "TlsSetValue" ));
109                 }
110                 static void free()
111                 {
112                     ThreadData * p = get();
113                     ::TlsSetValue( m_key, nullptr );
114                     if ( p )
115                         delete p;
116                 }
117             };
118             //@endcond
119
120             //@cond
121             static ThreadData * _threadData( EThreadAction nAction )
122             {
123                 switch ( nAction ) {
124                     case do_getData:
125 #           ifdef _DEBUG
126                         {
127                             ThreadData * p = Holder::get();
128                             assert( p );
129                             return p;
130                         }
131 #           else
132                         return Holder::get();
133 #           endif
134                     case do_checkData:
135                         return Holder::get();
136                     case do_attachThread:
137                         if ( Holder::get() == nullptr )
138                             Holder::alloc();
139                         return Holder::get();
140                     case do_detachThread:
141                         Holder::free();
142                         return nullptr;
143                     default:
144                         assert( false ) ;   // anything forgotten?..
145                 }
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, api_exception is thrown
178                 with code = -1.
179                 If an error occurs in call of Win TLS API function, api_exception is thrown
180                 with Windows 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                 else
190                     CDS_THROW_EXCEPTION( 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                     CDS_THROW_EXCEPTION( 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             //@cond
220             static size_t fake_current_processor()
221             {
222                 return _threadData( do_getData )->fake_current_processor();
223             }
224             //@endcond
225         };
226
227     } // namespace wintls
228 }} // namespace cds::threading
229 //@endcond
230
231 #endif // #ifndef CDSLIB_THREADING_DETAILS_WINTLS_MANAGER_H