xref: /qemu/include/qemu/coroutine-tls.h (revision b2a3cbb8)
1 /*
2  * QEMU Thread Local Storage for coroutines
3  *
4  * Copyright Red Hat
5  *
6  * SPDX-License-Identifier: LGPL-2.1-or-later
7  *
8  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
9  * See the COPYING.LIB file in the top-level directory.
10  *
11  * It is forbidden to access Thread Local Storage in coroutines because
12  * compiler optimizations may cause values to be cached across coroutine
13  * re-entry. Coroutines can run in more than one thread through the course of
14  * their life, leading bugs when stale TLS values from the wrong thread are
15  * used as a result of compiler optimization.
16  *
17  * An example is:
18  *
19  * ..code-block:: c
20  *   :caption: A coroutine that may see the wrong TLS value
21  *
22  *   static __thread AioContext *current_aio_context;
23  *   ...
24  *   static void coroutine_fn foo(void)
25  *   {
26  *       aio_notify(current_aio_context);
27  *       qemu_coroutine_yield();
28  *       aio_notify(current_aio_context); // <-- may be stale after yielding!
29  *   }
30  *
31  * This header provides macros for safely defining variables in Thread Local
32  * Storage:
33  *
34  * ..code-block:: c
35  *   :caption: A coroutine that safely uses TLS
36  *
37  *   QEMU_DEFINE_STATIC_CO_TLS(AioContext *, current_aio_context)
38  *   ...
39  *   static void coroutine_fn foo(void)
40  *   {
41  *       aio_notify(get_current_aio_context());
42  *       qemu_coroutine_yield();
43  *       aio_notify(get_current_aio_context()); // <-- safe
44  *   }
45  */
46 
47 #ifndef QEMU_COROUTINE_TLS_H
48 #define QEMU_COROUTINE_TLS_H
49 
50 /*
51  * To stop the compiler from caching TLS values we define accessor functions
52  * with __attribute__((noinline)) plus asm volatile("") to prevent
53  * optimizations that override noinline.
54  *
55  * The compiler can still analyze noinline code and make optimizations based on
56  * that knowledge, so an inline asm output operand is used to prevent
57  * optimizations that make assumptions about the address of the TLS variable.
58  *
59  * This is fragile and ultimately needs to be solved by a mechanism that is
60  * guaranteed to work by the compiler (e.g. stackless coroutines), but for now
61  * we use this approach to prevent issues.
62  */
63 
64 /**
65  * QEMU_DECLARE_CO_TLS:
66  * @type: the variable's C type
67  * @var: the variable name
68  *
69  * Declare an extern variable in Thread Local Storage from a header file:
70  *
71  * .. code-block:: c
72  *   :caption: Declaring an extern variable in Thread Local Storage
73  *
74  *   QEMU_DECLARE_CO_TLS(int, my_count)
75  *   ...
76  *   int c = get_my_count();
77  *   set_my_count(c + 1);
78  *   *get_ptr_my_count() = 0;
79  *
80  * This is a coroutine-safe replacement for the __thread keyword and is
81  * equivalent to the following code:
82  *
83  * .. code-block:: c
84  *   :caption: Declaring a TLS variable using __thread
85  *
86  *   extern __thread int my_count;
87  *   ...
88  *   int c = my_count;
89  *   my_count = c + 1;
90  *   *(&my_count) = 0;
91  */
92 #define QEMU_DECLARE_CO_TLS(type, var)                                       \
93     __attribute__((noinline)) type get_##var(void);                          \
94     __attribute__((noinline)) void set_##var(type v);                        \
95     __attribute__((noinline)) type *get_ptr_##var(void);
96 
97 /**
98  * QEMU_DEFINE_CO_TLS:
99  * @type: the variable's C type
100  * @var: the variable name
101  *
102  * Define a variable in Thread Local Storage that was previously declared from
103  * a header file with QEMU_DECLARE_CO_TLS():
104  *
105  * .. code-block:: c
106  *   :caption: Defining a variable in Thread Local Storage
107  *
108  *   QEMU_DEFINE_CO_TLS(int, my_count)
109  *
110  * This is a coroutine-safe replacement for the __thread keyword and is
111  * equivalent to the following code:
112  *
113  * .. code-block:: c
114  *   :caption: Defining a TLS variable using __thread
115  *
116  *   __thread int my_count;
117  */
118 #define QEMU_DEFINE_CO_TLS(type, var)                                        \
119     static __thread type co_tls_##var;                                       \
120     type get_##var(void) { asm volatile(""); return co_tls_##var; }          \
121     void set_##var(type v) { asm volatile(""); co_tls_##var = v; }           \
122     type *get_ptr_##var(void)                                                \
123     { type *ptr = &co_tls_##var; asm volatile("" : "+rm" (ptr)); return ptr; }
124 
125 /**
126  * QEMU_DEFINE_STATIC_CO_TLS:
127  * @type: the variable's C type
128  * @var: the variable name
129  *
130  * Define a static variable in Thread Local Storage:
131  *
132  * .. code-block:: c
133  *   :caption: Defining a static variable in Thread Local Storage
134  *
135  *   QEMU_DEFINE_STATIC_CO_TLS(int, my_count)
136  *   ...
137  *   int c = get_my_count();
138  *   set_my_count(c + 1);
139  *   *get_ptr_my_count() = 0;
140  *
141  * This is a coroutine-safe replacement for the __thread keyword and is
142  * equivalent to the following code:
143  *
144  * .. code-block:: c
145  *   :caption: Defining a static TLS variable using __thread
146  *
147  *   static __thread int my_count;
148  *   ...
149  *   int c = my_count;
150  *   my_count = c + 1;
151  *   *(&my_count) = 0;
152  */
153 #define QEMU_DEFINE_STATIC_CO_TLS(type, var)                                 \
154     static __thread type co_tls_##var;                                       \
155     static __attribute__((noinline, unused))                                 \
156     type get_##var(void)                                                     \
157     { asm volatile(""); return co_tls_##var; }                               \
158     static __attribute__((noinline, unused))                                 \
159     void set_##var(type v)                                                   \
160     { asm volatile(""); co_tls_##var = v; }                                  \
161     static __attribute__((noinline, unused))                                 \
162     type *get_ptr_##var(void)                                                \
163     { type *ptr = &co_tls_##var; asm volatile("" : "+rm" (ptr)); return ptr; }
164 
165 #endif /* QEMU_COROUTINE_TLS_H */
166