thread: Add a function to pop and call all registered cleanup handlers
[strongswan.git] / src / libstrongswan / threading / windows / thread.c
1 /*
2 * Copyright (C) 2013 Martin Willi
3 * Copyright (C) 2013 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "thread.h"
17
18 #include <utils/debug.h>
19 #include <threading/spinlock.h>
20 #include <threading/thread.h>
21 #include <collections/hashtable.h>
22 #include <collections/array.h>
23
24
25 typedef struct private_thread_t private_thread_t;
26
27 struct private_thread_t {
28
29 /**
30 * Public interface.
31 */
32 thread_t public;
33
34 /**
35 * GetCurrentThreadId() of thread
36 */
37 DWORD id;
38
39 /**
40 * Printable thread id returned by thread_current_id()
41 */
42 u_int tid;
43
44 /**
45 * Windows thread handle
46 */
47 HANDLE handle;
48
49 /**
50 * Main function of this thread (NULL for the main thread).
51 */
52 thread_main_t main;
53
54 /**
55 * Argument for the main function.
56 */
57 void *arg;
58
59 /**
60 * Thread return value
61 */
62 void *ret;
63
64 /**
65 * Stack of cleanup handlers, as cleanup_t
66 */
67 array_t *cleanup;
68
69 /**
70 * Thread specific values for this thread
71 */
72 hashtable_t *tls;
73
74 /**
75 * Thread terminated?
76 */
77 bool terminated;
78
79 /**
80 * Thread detached?
81 */
82 bool detached;
83
84 /**
85 * Is thread in cancellable state
86 */
87 bool cancelability;
88
89 /**
90 * Has the thread been cancelled by thread->cancel()?
91 */
92 bool canceled;
93
94 /**
95 * Did we schedule an APC to docancel()?
96 */
97 bool cancel_pending;
98
99 /**
100 * Active condition variable thread is waiting in, if any
101 */
102 CONDITION_VARIABLE *condvar;
103 };
104
105 /**
106 * Global list of threads, GetCurrentThreadId() => private_thread_t
107 */
108 static hashtable_t *threads;
109
110 /**
111 * Lock for threads table
112 */
113 static spinlock_t *threads_lock;
114
115 /**
116 * Counter to assign printable thread IDs
117 */
118 static u_int threads_ids = 0;
119
120 /**
121 * Forward declaration
122 */
123 static private_thread_t *create_internal(DWORD id);
124
125 /**
126 * Set leak detective state
127 */
128 static inline bool set_leak_detective(bool state)
129 {
130 #ifdef LEAK_DETECTIVE
131 if (lib && lib->leak_detective)
132 {
133 return lib->leak_detective->set_state(lib->leak_detective, state);
134 }
135 #endif
136 return FALSE;
137 }
138
139 /**
140 * Store thread in index
141 */
142 static void put_thread(private_thread_t *this)
143 {
144 bool old;
145
146 old = set_leak_detective(FALSE);
147 threads_lock->lock(threads_lock);
148
149 threads->put(threads, (void*)(uintptr_t)this->id, this);
150
151 threads_lock->unlock(threads_lock);
152 set_leak_detective(old);
153 }
154
155 /**
156 * Remove thread from index
157 */
158 static void remove_thread(private_thread_t *this)
159 {
160 bool old;
161
162 old = set_leak_detective(FALSE);
163 threads_lock->lock(threads_lock);
164
165 threads->remove(threads, (void*)(uintptr_t)this->id);
166
167 threads_lock->unlock(threads_lock);
168 set_leak_detective(old);
169 }
170
171 /**
172 * Get thread data for calling thread
173 */
174 static private_thread_t *get_current_thread()
175 {
176 private_thread_t *this;
177
178 threads_lock->lock(threads_lock);
179
180 this = threads->get(threads, (void*)(uintptr_t)GetCurrentThreadId());
181
182 threads_lock->unlock(threads_lock);
183
184 if (!this)
185 {
186 this = create_internal(GetCurrentThreadId());
187 put_thread(this);
188 }
189
190 return this;
191 }
192
193 /**
194 * See header.
195 */
196 void* thread_tls_put(void *key, void *value)
197 {
198 private_thread_t *thread;
199 bool old;
200
201 thread = get_current_thread();
202
203 old = set_leak_detective(FALSE);
204 value = thread->tls->put(thread->tls, key, value);
205 set_leak_detective(old);
206
207 return value;
208 }
209
210 /**
211 * See header.
212 */
213 void* thread_tls_get(void *key)
214 {
215 private_thread_t *thread;
216 void *value;
217 bool old;
218
219 thread = get_current_thread();
220
221 old = set_leak_detective(FALSE);
222 value = thread->tls->get(thread->tls, key);
223 set_leak_detective(old);
224
225 return value;
226 }
227
228 /**
229 * See header.
230 */
231 void* thread_tls_remove(void *key)
232 {
233 private_thread_t *thread;
234 void *value;
235 bool old;
236
237 thread = get_current_thread();
238
239 old = set_leak_detective(FALSE);
240 threads_lock->lock(threads_lock);
241 value = thread->tls->remove(thread->tls, key);
242 threads_lock->unlock(threads_lock);
243 set_leak_detective(old);
244
245 return value;
246 }
247
248 /**
249 * Thread cleanup data
250 */
251 typedef struct {
252 /** Cleanup callback function */
253 thread_cleanup_t cb;
254 /** Argument provided to the cleanup function */
255 void *arg;
256 } cleanup_t;
257
258 /**
259 * Invoke pushed/tls cleanup handlers
260 */
261 static void docleanup(private_thread_t *this)
262 {
263 enumerator_t *enumerator;
264 cleanup_t cleanup, *tls;
265 bool old;
266
267 old = set_leak_detective(FALSE);
268
269 while (array_remove(this->cleanup, -1, &cleanup))
270 {
271 set_leak_detective(old);
272 cleanup.cb(cleanup.arg);
273 set_leak_detective(FALSE);
274 }
275
276 threads_lock->lock(threads_lock);
277 enumerator = this->tls->create_enumerator(this->tls);
278 while (enumerator->enumerate(enumerator, NULL, &tls))
279 {
280 this->tls->remove_at(this->tls, enumerator);
281
282 set_leak_detective(old);
283 thread_tls_cleanup(tls);
284 set_leak_detective(FALSE);
285 }
286 enumerator->destroy(enumerator);
287 threads_lock->unlock(threads_lock);
288
289 set_leak_detective(old);
290 }
291
292 /**
293 * Clean up and destroy a thread
294 */
295 static void destroy(private_thread_t *this)
296 {
297 bool old;
298
299 docleanup(this);
300
301 old = set_leak_detective(FALSE);
302
303 array_destroy(this->cleanup);
304 this->tls->destroy(this->tls);
305 if (this->handle)
306 {
307 CloseHandle(this->handle);
308 }
309 free(this);
310
311 set_leak_detective(old);
312 }
313
314 /**
315 * End a thread, destroy when detached
316 */
317 static void end_thread(private_thread_t *this)
318 {
319 if (this->detached)
320 {
321 remove_thread(this);
322 destroy(this);
323 }
324 else
325 {
326 this->terminated = TRUE;
327 docleanup(this);
328 }
329 }
330
331 /**
332 * See header.
333 */
334 void thread_set_active_condvar(CONDITION_VARIABLE *condvar)
335 {
336 private_thread_t *thread;
337
338 thread = get_current_thread();
339
340 threads_lock->lock(threads_lock);
341 thread->condvar = condvar;
342 threads_lock->unlock(threads_lock);
343
344 /* this is a cancellation point, as condvar wait is one */
345 SleepEx(0, TRUE);
346 }
347
348 /**
349 * APC to cancel a thread
350 */
351 static void WINAPI docancel(ULONG_PTR dwParam)
352 {
353 private_thread_t *this = (private_thread_t*)dwParam;
354
355 /* make sure cancel() does not access this anymore */
356 threads_lock->lock(threads_lock);
357 threads_lock->unlock(threads_lock);
358
359 end_thread(this);
360 ExitThread(0);
361 }
362
363 METHOD(thread_t, cancel, void,
364 private_thread_t *this)
365 {
366 this->canceled = TRUE;
367 if (this->cancelability)
368 {
369 threads_lock->lock(threads_lock);
370 if (!this->cancel_pending)
371 {
372 this->cancel_pending = TRUE;
373 QueueUserAPC(docancel, this->handle, (uintptr_t)this);
374 if (this->condvar)
375 {
376 WakeAllConditionVariable(this->condvar);
377 }
378 }
379 threads_lock->unlock(threads_lock);
380 }
381 }
382
383 METHOD(thread_t, kill_, void,
384 private_thread_t *this, int sig)
385 {
386 }
387
388 METHOD(thread_t, detach, void,
389 private_thread_t *this)
390 {
391 this->detached = TRUE;
392 }
393
394 METHOD(thread_t, join, void*,
395 private_thread_t *this)
396 {
397 void *ret;
398
399 if (this->detached)
400 {
401 return NULL;
402 }
403
404 while (!this->terminated)
405 {
406 /* join is a cancellation point, use alertable wait */
407 WaitForSingleObjectEx(this->handle, INFINITE, TRUE);
408 }
409
410 ret = this->ret;
411
412 remove_thread(this);
413 destroy(this);
414
415 return ret;
416 }
417
418 /**
419 * Main function wrapper for threads
420 */
421 static DWORD thread_cb(private_thread_t *this)
422 {
423 /* Enable cancelability once the thread starts. We must check for any
424 * pending cancellation request an queue the APC that gets executed
425 * at the first cancellation point. */
426 this->cancelability = TRUE;
427 if (this->canceled)
428 {
429 cancel(this);
430 }
431
432 this->ret = this->main(this->arg);
433
434 end_thread(this);
435
436 return 0;
437 }
438
439 /**
440 * Create an internal thread object.
441 */
442 static private_thread_t *create_internal(DWORD id)
443 {
444 private_thread_t *this;
445 bool old;
446
447 old = set_leak_detective(FALSE);
448
449 INIT(this,
450 .public = {
451 .cancel = _cancel,
452 .kill = _kill_,
453 .detach = _detach,
454 .join = _join,
455 },
456 .cleanup = array_create(sizeof(cleanup_t), 0),
457 .tls = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4),
458 .id = id,
459 .cancelability = TRUE,
460 );
461
462 set_leak_detective(old);
463
464 threads_lock->lock(threads_lock);
465 this->tid = threads_ids++;
466 threads_lock->unlock(threads_lock);
467
468 if (id)
469 {
470 this->handle = OpenThread(THREAD_ALL_ACCESS, FALSE, id);
471 }
472 return this;
473 }
474
475 /**
476 * Described in header.
477 */
478 thread_t *thread_create(thread_main_t main, void *arg)
479 {
480 private_thread_t *this;
481
482 this = create_internal(0);
483
484 this->main = main;
485 this->arg = arg;
486 /* not cancellable until started */
487 this->cancelability = FALSE;
488
489 this->handle = CreateThread(NULL, 0, (void*)thread_cb, this,
490 CREATE_SUSPENDED, &this->id);
491 if (!this->handle)
492 {
493 destroy(this);
494 return NULL;
495 }
496
497 put_thread(this);
498
499 DBG2(DBG_LIB, "created thread %u", this->id);
500
501 ResumeThread(this->handle);
502
503 return &this->public;
504 }
505
506 /**
507 * Described in header.
508 */
509 thread_t *thread_current()
510 {
511 return &get_current_thread()->public;
512 }
513
514 /**
515 * Described in header.
516 */
517 u_int thread_current_id()
518 {
519 return get_current_thread()->tid;
520 }
521
522 /**
523 * Described in header.
524 */
525 void thread_cleanup_push(thread_cleanup_t cb, void *arg)
526 {
527 private_thread_t *this;
528 cleanup_t cleanup = {
529 .cb = cb,
530 .arg = arg,
531 };
532 bool old;
533
534 this = get_current_thread();
535
536 old = set_leak_detective(FALSE);
537 array_insert(this->cleanup, -1, &cleanup);
538 set_leak_detective(old);
539 }
540
541 /**
542 * Described in header
543 */
544 void thread_cleanup_pop(bool execute)
545 {
546 private_thread_t *this;
547 cleanup_t cleanup = {};
548 bool old;
549
550 this = get_current_thread();
551
552 old = set_leak_detective(FALSE);
553 array_remove(this->cleanup, -1, &cleanup);
554 set_leak_detective(old);
555
556 if (execute)
557 {
558 cleanup.cb(cleanup.arg);
559 }
560 }
561
562 /**
563 * Described in header.
564 */
565 void thread_cleanup_popall()
566 {
567 private_thread_t *this;
568 cleanup_t cleanup = {};
569 bool old;
570
571 this = get_current_thread();
572 while (array_count(this->cleanup))
573 {
574 old = set_leak_detective(FALSE);
575 array_remove(this->cleanup, -1, &cleanup);
576 set_leak_detective(old);
577
578 cleanup.cb(cleanup.arg);
579 }
580 }
581
582 /**
583 * Described in header.
584 */
585 bool thread_cancelability(bool enable)
586 {
587 private_thread_t *this;
588 bool old;
589
590 this = get_current_thread();
591 old = this->cancelability;
592 this->cancelability = enable;
593
594 if (enable && !old && this->canceled)
595 {
596 cancel(this);
597 }
598 return old;
599 }
600
601 /**
602 * Described in header.
603 */
604 void thread_cancellation_point()
605 {
606 bool old;
607
608 old = thread_cancelability(TRUE);
609 SleepEx(0, TRUE);
610 thread_cancelability(old);
611 }
612
613 /**
614 * Described in header.
615 */
616 void thread_exit(void *val)
617 {
618 private_thread_t *this;
619
620 this = get_current_thread();
621 this->ret = val;
622
623 end_thread(this);
624 ExitThread(0);
625 }
626
627 /**
628 * Clean up thread data while it detaches
629 */
630 static void cleanup_tls()
631 {
632 private_thread_t *this;
633 bool old;
634
635 old = set_leak_detective(FALSE);
636 threads_lock->lock(threads_lock);
637
638 this = threads->remove(threads, (void*)(uintptr_t)GetCurrentThreadId());
639
640 threads_lock->unlock(threads_lock);
641 set_leak_detective(old);
642
643 if (this)
644 {
645 /* If the thread exited, but has not been joined, it is in terminated
646 * state. We must not mangle it, as we target externally spawned
647 * threads only. */
648 if (!this->terminated && !this->detached)
649 {
650 destroy(this);
651 }
652 }
653 }
654
655 /**
656 * DllMain called for dll events
657 */
658 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
659 {
660 switch (fdwReason)
661 {
662 case DLL_THREAD_DETACH:
663 cleanup_tls();
664 break;
665 default:
666 break;
667 }
668 return TRUE;
669 }
670
671 /*
672 * Described in header.
673 */
674 void threads_init()
675 {
676 threads_lock = spinlock_create();
677 threads = hashtable_create(hashtable_hash_ptr, hashtable_equals_ptr, 4);
678
679 /* reset counter should we initialize more than once */
680 threads_ids = 0;
681
682 put_thread(create_internal(GetCurrentThreadId()));
683 }
684
685 /**
686 * Described in header.
687 */
688 void threads_deinit()
689 {
690 private_thread_t *this;
691
692 this = threads->remove(threads, (void*)(uintptr_t)GetCurrentThreadId());
693 destroy(this);
694
695 threads_lock->destroy(threads_lock);
696 threads->destroy(threads);
697 }