1 // Copyright 2012-2013 Intel Corporation
2 //
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // - Redistributions of source code must retain the above copyright notice, this
9 //   list of conditions and the following disclaimer.
10 //
11 // - Redistributions in binary form must reproduce the above copyright notice,
12 //   this list of conditions and the following disclaimer in the documentation
13 //   and/or other materials provided with the distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 #include <assert.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 
31 #include "threads.h"
32 
33 #include "wcore_error.h"
34 #include "wcore_tinfo.h"
35 
36 static once_flag wcore_tinfo_once = ONCE_FLAG_INIT;
37 static tss_t wcore_tinfo_key;
38 
39 #ifdef WAFFLE_HAS_TLS
40 /// @brief Thread-local storage for all of Waffle.
41 ///
42 /// For documentation on the tls_model, see the GCC manual [1] and
43 /// Drepper [2].
44 ///
45 /// [1] GCC 4.7.0 Manual, 6.59 Thread-Local Storage.
46 ///     http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Thread_002dLocal.html
47 ///
48 /// [2] Ulrich Drepper. "Elf Handling For Thread Local Storage".
49 ///     http://people.redhat.com/drepper/tls.pdf
50 static __thread struct wcore_tinfo wcore_tinfo
51 #ifdef WAFFLE_HAS_TLS_MODEL_INITIAL_EXEC
52     __attribute__((tls_model("initial-exec")))
53 #endif
54     ;
55 #endif // WAFFLE_HAS_TLS
56 
57 #if defined(__GNUC__)
58 #define NORETURN __attribute__((noreturn))
59 #elif defined(_MSC_VER)
60 #define NORETURN __declspec(noreturn)
61 #else
62 #define NORETURN
63 #endif
64 
65 static void NORETURN
wcore_tinfo_abort_init(void)66 wcore_tinfo_abort_init(void)
67 {
68     printf("waffle: fatal-error: failed to initialize thread local info\n");
69     abort();
70 }
71 
72 static void
wcore_tinfo_key_dtor(void * args)73 wcore_tinfo_key_dtor(void *args)
74 {
75     struct wcore_tinfo *tinfo = args;
76     if (!tinfo)
77         return;
78 
79     wcore_error_tinfo_destroy(tinfo->error);
80 
81 #ifndef WAFFLE_HAS_TLS
82     free(tinfo);
83 #endif
84 }
85 
86 static void
wcore_tinfo_key_create(void)87 wcore_tinfo_key_create(void)
88 {
89     int err;
90 
91     err = tss_create(&wcore_tinfo_key, wcore_tinfo_key_dtor);
92     if (err)
93         wcore_tinfo_abort_init();
94 }
95 
96 static void
wcore_tinfo_init(struct wcore_tinfo * tinfo)97 wcore_tinfo_init(struct wcore_tinfo *tinfo)
98 {
99     int err;
100 
101     if (tinfo->is_init)
102         return;
103 
104     tinfo->error = wcore_error_tinfo_create();
105     if (!tinfo->error)
106         wcore_tinfo_abort_init();
107 
108     tinfo->current_display = NULL;
109     tinfo->current_window = NULL;
110     tinfo->current_context = NULL;
111 
112     tinfo->is_init = true;
113 
114 #ifdef WAFFLE_HAS_TLS
115     // Register tinfo with the key's destructor to prevent memory leaks at
116     // thread exit. The destructor must be registered once per process, but
117     // each instance of tinfo must be registered individually. The key's data
118     // is never retrieved because use the key only to register tinfo for
119     // destruction.
120 
121     // With C11 threads call_once "can never fail"...
122     // http://open-std.org/twiki/pub/WG14/DefectReports/n1654.htm
123     call_once(&wcore_tinfo_once, wcore_tinfo_key_create);
124 #endif
125 
126     err = tss_set(wcore_tinfo_key, tinfo);
127     if (err)
128         wcore_tinfo_abort_init();
129 }
130 
131 struct wcore_tinfo*
wcore_tinfo_get(void)132 wcore_tinfo_get(void)
133 {
134 #ifdef WAFFLE_HAS_TLS
135     wcore_tinfo_init(&wcore_tinfo);
136     return &wcore_tinfo;
137 #else
138     struct wcore_tinfo *tinfo;
139 
140     // With C11 threads call_once "can never fail"...
141     // http://open-std.org/twiki/pub/WG14/DefectReports/n1654.htm
142     call_once(&wcore_tinfo_once, wcore_tinfo_key_create);
143 
144     tinfo = tss_get(wcore_tinfo_key);
145     if (tinfo)
146         return tinfo;
147 
148     tinfo = calloc(1, sizeof(*tinfo));
149     if (!tinfo)
150         wcore_tinfo_abort_init();
151 
152     wcore_tinfo_init(tinfo);
153     return tinfo;
154 #endif
155 }
156