qemu/include/qemu/coroutine-tls.h
<<
>>
Prefs
   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