blob: 1558a826aa0dcb9ede1b3328d21a1358db1ccb73 [file] [log] [blame]
/*
* QEMU Thread Local Storage for coroutines
*
* Copyright Red Hat
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*
* It is forbidden to access Thread Local Storage in coroutines because
* compiler optimizations may cause values to be cached across coroutine
* re-entry. Coroutines can run in more than one thread through the course of
* their life, leading bugs when stale TLS values from the wrong thread are
* used as a result of compiler optimization.
*
* An example is:
*
* ..code-block:: c
* :caption: A coroutine that may see the wrong TLS value
*
* static __thread AioContext *current_aio_context;
* ...
* static void coroutine_fn foo(void)
* {
* aio_notify(current_aio_context);
* qemu_coroutine_yield();
* aio_notify(current_aio_context); // <-- may be stale after yielding!
* }
*
* This header provides macros for safely defining variables in Thread Local
* Storage:
*
* ..code-block:: c
* :caption: A coroutine that safely uses TLS
*
* QEMU_DEFINE_STATIC_CO_TLS(AioContext *, current_aio_context)
* ...
* static void coroutine_fn foo(void)
* {
* aio_notify(get_current_aio_context());
* qemu_coroutine_yield();
* aio_notify(get_current_aio_context()); // <-- safe
* }
*/
#ifndef QEMU_COROUTINE_TLS_H
#define QEMU_COROUTINE_TLS_H
/*
* To stop the compiler from caching TLS values we define accessor functions
* with __attribute__((noinline)) plus asm volatile("") to prevent
* optimizations that override noinline.
*
* The compiler can still analyze noinline code and make optimizations based on
* that knowledge, so an inline asm output operand is used to prevent
* optimizations that make assumptions about the address of the TLS variable.
*
* This is fragile and ultimately needs to be solved by a mechanism that is
* guaranteed to work by the compiler (e.g. stackless coroutines), but for now
* we use this approach to prevent issues.
*/
/**
* QEMU_DECLARE_CO_TLS:
* @type: the variable's C type
* @var: the variable name
*
* Declare an extern variable in Thread Local Storage from a header file:
*
* .. code-block:: c
* :caption: Declaring an extern variable in Thread Local Storage
*
* QEMU_DECLARE_CO_TLS(int, my_count)
* ...
* int c = get_my_count();
* set_my_count(c + 1);
* *get_ptr_my_count() = 0;
*
* This is a coroutine-safe replacement for the __thread keyword and is
* equivalent to the following code:
*
* .. code-block:: c
* :caption: Declaring a TLS variable using __thread
*
* extern __thread int my_count;
* ...
* int c = my_count;
* my_count = c + 1;
* *(&my_count) = 0;
*/
#define QEMU_DECLARE_CO_TLS(type, var) \
__attribute__((noinline)) type get_##var(void); \
__attribute__((noinline)) void set_##var(type v); \
__attribute__((noinline)) type *get_ptr_##var(void);
/**
* QEMU_DEFINE_CO_TLS:
* @type: the variable's C type
* @var: the variable name
*
* Define a variable in Thread Local Storage that was previously declared from
* a header file with QEMU_DECLARE_CO_TLS():
*
* .. code-block:: c
* :caption: Defining a variable in Thread Local Storage
*
* QEMU_DEFINE_CO_TLS(int, my_count)
*
* This is a coroutine-safe replacement for the __thread keyword and is
* equivalent to the following code:
*
* .. code-block:: c
* :caption: Defining a TLS variable using __thread
*
* __thread int my_count;
*/
#define QEMU_DEFINE_CO_TLS(type, var) \
static __thread type co_tls_##var; \
type get_##var(void) { asm volatile(""); return co_tls_##var; } \
void set_##var(type v) { asm volatile(""); co_tls_##var = v; } \
type *get_ptr_##var(void) \
{ type *ptr = &co_tls_##var; asm volatile("" : "+rm" (ptr)); return ptr; }
/**
* QEMU_DEFINE_STATIC_CO_TLS:
* @type: the variable's C type
* @var: the variable name
*
* Define a static variable in Thread Local Storage:
*
* .. code-block:: c
* :caption: Defining a static variable in Thread Local Storage
*
* QEMU_DEFINE_STATIC_CO_TLS(int, my_count)
* ...
* int c = get_my_count();
* set_my_count(c + 1);
* *get_ptr_my_count() = 0;
*
* This is a coroutine-safe replacement for the __thread keyword and is
* equivalent to the following code:
*
* .. code-block:: c
* :caption: Defining a static TLS variable using __thread
*
* static __thread int my_count;
* ...
* int c = my_count;
* my_count = c + 1;
* *(&my_count) = 0;
*/
#define QEMU_DEFINE_STATIC_CO_TLS(type, var) \
static __thread type co_tls_##var; \
static __attribute__((noinline, unused)) \
type get_##var(void) \
{ asm volatile(""); return co_tls_##var; } \
static __attribute__((noinline, unused)) \
void set_##var(type v) \
{ asm volatile(""); co_tls_##var = v; } \
static __attribute__((noinline, unused)) \
type *get_ptr_##var(void) \
{ type *ptr = &co_tls_##var; asm volatile("" : "+rm" (ptr)); return ptr; }
#endif /* QEMU_COROUTINE_TLS_H */