1 #ifdef JEMALLOC_INTERNAL_TSD_WIN_H
2 #error This file should be included only once, by tsd.h.
3 #endif
4 #define JEMALLOC_INTERNAL_TSD_WIN_H
5 
6 typedef struct {
7 	bool initialized;
8 	tsd_t val;
9 } tsd_wrapper_t;
10 
11 extern DWORD tsd_tsd;
12 extern tsd_wrapper_t tsd_boot_wrapper;
13 extern bool tsd_booted;
14 
15 /* Initialization/cleanup. */
16 JEMALLOC_ALWAYS_INLINE bool
tsd_cleanup_wrapper(void)17 tsd_cleanup_wrapper(void) {
18 	DWORD error = GetLastError();
19 	tsd_wrapper_t *wrapper = (tsd_wrapper_t *)TlsGetValue(tsd_tsd);
20 	SetLastError(error);
21 
22 	if (wrapper == NULL) {
23 		return false;
24 	}
25 
26 	if (wrapper->initialized) {
27 		wrapper->initialized = false;
28 		tsd_cleanup(&wrapper->val);
29 		if (wrapper->initialized) {
30 			/* Trigger another cleanup round. */
31 			return true;
32 		}
33 	}
34 	malloc_tsd_dalloc(wrapper);
35 	return false;
36 }
37 
38 JEMALLOC_ALWAYS_INLINE void
tsd_wrapper_set(tsd_wrapper_t * wrapper)39 tsd_wrapper_set(tsd_wrapper_t *wrapper) {
40 	if (!TlsSetValue(tsd_tsd, (void *)wrapper)) {
41 		malloc_write("<jemalloc>: Error setting TSD\n");
42 		abort();
43 	}
44 }
45 
46 JEMALLOC_ALWAYS_INLINE tsd_wrapper_t *
tsd_wrapper_get(bool init)47 tsd_wrapper_get(bool init) {
48 	DWORD error = GetLastError();
49 	tsd_wrapper_t *wrapper = (tsd_wrapper_t *) TlsGetValue(tsd_tsd);
50 	SetLastError(error);
51 
52 	if (init && unlikely(wrapper == NULL)) {
53 		wrapper = (tsd_wrapper_t *)
54 		    malloc_tsd_malloc(sizeof(tsd_wrapper_t));
55 		if (wrapper == NULL) {
56 			malloc_write("<jemalloc>: Error allocating TSD\n");
57 			abort();
58 		} else {
59 			wrapper->initialized = false;
60 			/* MSVC is finicky about aggregate initialization. */
61 			tsd_t tsd_initializer = TSD_INITIALIZER;
62 			wrapper->val = tsd_initializer;
63 		}
64 		tsd_wrapper_set(wrapper);
65 	}
66 	return wrapper;
67 }
68 
69 JEMALLOC_ALWAYS_INLINE bool
tsd_boot0(void)70 tsd_boot0(void) {
71 	tsd_tsd = TlsAlloc();
72 	if (tsd_tsd == TLS_OUT_OF_INDEXES) {
73 		return true;
74 	}
75 	malloc_tsd_cleanup_register(&tsd_cleanup_wrapper);
76 	tsd_wrapper_set(&tsd_boot_wrapper);
77 	tsd_booted = true;
78 	return false;
79 }
80 
81 JEMALLOC_ALWAYS_INLINE void
tsd_boot1(void)82 tsd_boot1(void) {
83 	tsd_wrapper_t *wrapper;
84 	wrapper = (tsd_wrapper_t *)
85 	    malloc_tsd_malloc(sizeof(tsd_wrapper_t));
86 	if (wrapper == NULL) {
87 		malloc_write("<jemalloc>: Error allocating TSD\n");
88 		abort();
89 	}
90 	tsd_boot_wrapper.initialized = false;
91 	tsd_cleanup(&tsd_boot_wrapper.val);
92 	wrapper->initialized = false;
93 	tsd_t initializer = TSD_INITIALIZER;
94 	wrapper->val = initializer;
95 	tsd_wrapper_set(wrapper);
96 }
97 JEMALLOC_ALWAYS_INLINE bool
tsd_boot(void)98 tsd_boot(void) {
99 	if (tsd_boot0()) {
100 		return true;
101 	}
102 	tsd_boot1();
103 	return false;
104 }
105 
106 JEMALLOC_ALWAYS_INLINE bool
tsd_booted_get(void)107 tsd_booted_get(void) {
108 	return tsd_booted;
109 }
110 
111 JEMALLOC_ALWAYS_INLINE bool
tsd_get_allocates(void)112 tsd_get_allocates(void) {
113 	return true;
114 }
115 
116 /* Get/set. */
117 JEMALLOC_ALWAYS_INLINE tsd_t *
tsd_get(bool init)118 tsd_get(bool init) {
119 	tsd_wrapper_t *wrapper;
120 
121 	assert(tsd_booted);
122 	wrapper = tsd_wrapper_get(init);
123 	if (tsd_get_allocates() && !init && wrapper == NULL) {
124 		return NULL;
125 	}
126 	return &wrapper->val;
127 }
128 
129 JEMALLOC_ALWAYS_INLINE void
tsd_set(tsd_t * val)130 tsd_set(tsd_t *val) {
131 	tsd_wrapper_t *wrapper;
132 
133 	assert(tsd_booted);
134 	wrapper = tsd_wrapper_get(true);
135 	if (likely(&wrapper->val != val)) {
136 		wrapper->val = *(val);
137 	}
138 	wrapper->initialized = true;
139 }
140