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