1 /*===========================================================================
2 *
3 * PUBLIC DOMAIN NOTICE
4 * National Center for Biotechnology Information
5 *
6 * This software/database is a "United States Government Work" under the
7 * terms of the United States Copyright Act. It was written as part of
8 * the author's official duties as a United States Government employee and
9 * thus cannot be copyrighted. This software/database is freely available
10 * to the public for use. The National Library of Medicine and the U.S.
11 * Government have not placed any restriction on its use or reproduction.
12 *
13 * Although all reasonable efforts have been taken to ensure the accuracy
14 * and reliability of the software and data, the NLM and the U.S.
15 * Government do not and cannot warrant the performance or results that
16 * may be obtained by using this software or data. The NLM and the U.S.
17 * Government disclaim all warranties, express or implied, including
18 * warranties of performance, merchantability or fitness for any particular
19 * purpose.
20 *
21 * Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26
27 #include <kfc/ctx.h>
28 #include <kfc/rsrc.h>
29 #include <kfc/rsrc-global.h>
30 #include <kfc/except.h>
31 #include <kfc/rc.h>
32 #include <kfc/xc.h>
33 #include <kproc/procmgr.h>
34 #include <kproc/task.h>
35 #include <kproc/impl.h>
36 #include <klib/container.h>
37
38 #include <sysalloc.h>
39
40 #include <atomic32.h>
41
42 #include "rsrc-priv.h"
43 #include "sysctx-priv.h"
44
45 #include <WINDOWS.H>
46 #include <string.h>
47 #include <assert.h>
48
49 /* thread-local storage */
50 typedef struct TLS TLS;
51 struct TLS
52 {
53 DLNode n;
54 KRsrc rsrc;
55 KCtx ctx;
56 DWORD threadId;
57 };
58
59 static DWORD key;
60 static DLList s_tls;
61 static uint32_t s_tls_count;
62 static CRITICAL_SECTION crit;
63 static atomic32_t key_once, crit_once;
64
65
66 /* whack
67 * whack thread-local storage
68 */
69 static
whack_tls(DLNode * n,void * ignore)70 void CC whack_tls ( DLNode * n, void * ignore )
71 {
72 TLS * tls = ( TLS * ) n;
73
74 /* the "parent" ctx */
75 ctx_t ctx = & tls -> ctx;
76 FUNC_ENTRY ( ctx, rcRuntime, rcThread, rcDestroying );
77
78 /* whack the resource managers */
79 KRsrcWhack ( & tls -> rsrc, ctx );
80
81 /* free the memory */
82 free ( tls );
83
84 /* reduce the count */
85 -- s_tls_count;
86 }
87
88 static
win_thread_once(atomic32_t * once,void (* trigger)(void))89 void win_thread_once ( atomic32_t * once, void ( * trigger ) ( void ) )
90 {
91 switch ( atomic32_test_and_set ( once, 1, 0 ) )
92 {
93 case 0:
94 ( * trigger ) ();
95 atomic32_set ( once, 2 );
96 break;
97 case 1:
98 while ( atomic32_read ( once ) == 1 )
99 Sleep ( 1 );
100 break;
101 case 2:
102 break;
103 }
104 }
105
106 static
init_crit(void)107 void init_crit ( void )
108 {
109 InitializeCriticalSection ( & crit );
110 }
111
112 static
enter_critical_section(void)113 void enter_critical_section ( void )
114 {
115 /* Windows didn't come up with the concept of
116 multi-thread-safe critical sections until Vista */
117 win_thread_once ( & crit_once, init_crit );
118
119 EnterCriticalSection ( & crit );
120 }
121
122 static
leave_critical_section(void)123 void leave_critical_section ( void )
124 {
125 LeaveCriticalSection ( & crit );
126 }
127
128 static
win_thread_getspecific(DWORD tlsKey)129 TLS * win_thread_getspecific ( DWORD tlsKey )
130 {
131 return TlsGetValue ( tlsKey );
132 }
133
134 static
eliminate_dups(DLNode * n,void * data)135 bool CC eliminate_dups ( DLNode * n, void * data )
136 {
137 const TLS * tls = ( const void * ) data;
138 if ( ( ( const TLS * ) n ) -> threadId != tls -> threadId )
139 return false;
140
141 DLListUnlink ( & s_tls, n );
142 whack_tls ( n, NULL );
143 return true;
144 }
145
146 static
garbage_collect_tls(DLNode * n,void * ignore)147 void CC garbage_collect_tls ( DLNode * n, void * ignore )
148 {
149 TLS * tls = ( TLS* ) n;
150 HANDLE t = OpenThread ( THREAD_QUERY_INFORMATION, FALSE, tls -> threadId );
151 if ( t != NULL )
152 CloseHandle ( t );
153 else
154 {
155 DLListUnlink ( & s_tls, & tls -> n );
156 whack_tls ( & tls -> n, NULL );
157 }
158 }
159
160 static
win_thread_setspecific(DWORD tlsKey,TLS * tls)161 void win_thread_setspecific ( DWORD tlsKey, TLS * tls )
162 {
163 /* record the thread id */
164 tls -> threadId = GetCurrentThreadId ();
165
166 /* need to record these for cleanup function */
167 enter_critical_section ();
168
169 /* quickly scan for reused thread id */
170 DLListDoUntil ( & s_tls, false, eliminate_dups, tls );
171
172 /* push the entry */
173 DLListPushTail ( & s_tls, & tls -> n );
174
175 /* try to garbage collect for threads
176 that have already exited */
177 if ( ++ s_tls_count >= 64 )
178 DLListForEach ( & s_tls, false, garbage_collect_tls, NULL );
179
180 /* done with the list */
181 leave_critical_section ();
182
183 /* store it on the thread */
184 TlsSetValue ( tlsKey, tls );
185 }
186
187
188 /* make_tls
189 * create thread-local storage
190 */
191 static
make_tls(const KFuncLoc * func_loc)192 TLS * make_tls ( const KFuncLoc * func_loc )
193 {
194 ctx_t ctx;
195
196 /* create the storage */
197 TLS * tls = calloc ( 1, sizeof * tls );
198 if ( tls == NULL )
199 exit ( -1 );
200
201 /* grab its context */
202 ctx = & tls -> ctx;
203
204 /* recover process-global resources */
205 TRY ( KRsrcGlobalInit ( & tls -> ctx, func_loc, true ) )
206 {
207 /* attach references */
208 TRY ( KRsrcInit ( & tls -> rsrc, ctx ) )
209 {
210 /* reset context */
211 tls -> ctx . rsrc = & tls -> rsrc;
212
213 /* set on thread */
214 win_thread_setspecific ( key, tls );
215 assert ( ! FAILED () );
216 }
217 }
218
219 if ( FAILED () )
220 {
221 free ( tls );
222 exit ( -1 );
223 }
224
225 return tls;
226 }
227
228 static
TLSCleanupTaskWhack(KTask * self)229 rc_t CC TLSCleanupTaskWhack ( KTask * self )
230 {
231 KTaskDestroy ( self, "TLSCleanupTask" );
232 free ( self );
233 return 0;
234 }
235
236 static
TLSCleanupTaskExecute(KTask * self)237 rc_t CC TLSCleanupTaskExecute ( KTask * self )
238 {
239 DLListWhack ( & s_tls, whack_tls, NULL );
240 return 0;
241 }
242
243 static KTask_vt_v1 TLSCleanupTask_vt =
244 {
245 1, 0,
246 TLSCleanupTaskWhack,
247 TLSCleanupTaskExecute
248 };
249
250 static
install_cleanup_task(void)251 void install_cleanup_task ( void )
252 {
253 rc_t rc = KProcMgrInit ();
254 if ( rc == 0 )
255 {
256 KProcMgr * mgr;
257 rc = KProcMgrMakeSingleton ( & mgr );
258 if ( rc == 0 )
259 {
260 /* create task to install into procmgr */
261 KTask * task = malloc ( sizeof * task );
262 if ( task != NULL )
263 {
264 rc = KTaskInit ( task,
265 ( const KTask_vt * ) & TLSCleanupTask_vt,
266 "TLSCleanupTask", "" );
267 if ( rc != 0 )
268 free ( task );
269 else
270 {
271 KTaskTicket ticket;
272 rc = KProcMgrAddCleanupTask ( mgr, & ticket, task );
273 if ( rc != 0 )
274 KTaskRelease ( task );
275 }
276 }
277
278 KProcMgrRelease ( mgr );
279 }
280 }
281 }
282
283 /* make_key
284 * initialize the thread-local storage key
285 */
286 static
make_key(void)287 void make_key ( void )
288 {
289 /* install cleanup task -
290 Windows doesn't appear to support
291 libraries that create TLS but
292 don't own the thread main */
293 install_cleanup_task ();
294
295 /* create key into thread-local storage */
296 key = TlsAlloc ();
297 }
298
299
300 /* get_tls_ctx
301 * reads thread-local storage
302 */
303 static
get_tls_ctx(const KFuncLoc * func_loc)304 const KCtx * get_tls_ctx ( const KFuncLoc * func_loc )
305 {
306 TLS * tls;
307
308 /* ensure the key is there */
309 win_thread_once ( & key_once, make_key );
310
311 /* retrieve the existing tls */
312 tls = win_thread_getspecific ( key );
313
314 /* create a new one if missing */
315 if ( tls == NULL )
316 {
317 tls = make_tls ( func_loc );
318 assert ( tls != NULL );
319 }
320
321 return & tls -> ctx;
322 }
323
324
325 /* ctx_recover
326 * queries thread for previously stored KRsrc block
327 * creates a new one if necessary
328 */
ctx_recover(KCtx * new_ctx,const KFuncLoc * func_loc)329 ctx_t ctx_recover ( KCtx * new_ctx, const KFuncLoc * func_loc )
330 {
331 DECLARE_FUNC_LOC ( rcRuntime, rcMgr, rcOpening );
332
333 if ( new_ctx != NULL )
334 {
335 const KCtx * ctx = get_tls_ctx ( & s_func_loc );
336
337 /* clear new_ctx and initialize special members */
338 RESET_CTX(new_ctx, ctx, func_loc);
339 }
340
341 return new_ctx;
342 }
343