Is it possible to force the thread_local variable to be reloaded after migration through kernel threads?

I implement user threads on top of the kernel and threads and notice that when a user thread migrates between kernel threads, the variables are thread_localread from the previous kernel location, even when the variables are also marked as volatile.

Because the compiler simply sees the swapcontextuser level as a function call, the following example demonstrates the problem with a simple function call.

#include <stdio.h>

struct Foo {
    int x;
    int y;
};

__thread Foo* volatile foo;

void bar() {
    asm("nop");
}
void f() {
    foo->x = 5;
    bar();
    asm volatile("":::"memory");
    // We desire a second computation of the address of foo here as an offset
    // from the FS register.
    foo->y = 7;
}

int main(){
    foo = new Foo;
    f();
    delete foo;
}

. , -fPIC , , . , TL.cc

g++ -std=c++11 -O3  -fPIC  -Wall -g TL.cc   -o TL 
objdump -d TL

f().

  400760:       53                      push   %rbx
  # Notice this computation happens only once.
  400761:       64 48 8b 04 25 00 00    mov    %fs:0x0,%rax
  400768:       00 00 
  40076a:       48 8d 80 f8 ff ff ff    lea    -0x8(%rax),%rax
  400771:       48 89 c3                mov    %rax,%rbx
  400774:       48 8b 00                mov    (%rax),%rax
  400777:       c7 00 05 00 00 00       movl   $0x5,(%rax)
  40077d:       e8 ce ff ff ff          callq  400750 <_Z3barv>
  # Observe that the value of rbx came from before the function call,
  # so if the function bar() actually returned on a different kernel
  # thread, we would be referencing the original kernel thread 
  # version of foo, instead of the new kernel thread version.
  400782:       48 8b 03                mov    (%rbx),%rax
  400785:       c7 40 04 07 00 00 00    movl   $0x7,0x4(%rax)
  40078c:       5b                      pop    %rbx
  40078d:       c3                      retq   
  40078e:       66 90                   xchg   %ax,%ax

, rax , bar().

fs?

gcc- , .

g++ --version

g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+4
1

, TLS , , , .

#define SafeTLS(TypeName, name) \
struct name##_SafeClass { \
    name##_SafeClass& \
    __attribute__ ((noinline)) \
    operator=(const TypeName& other) { \
        asm (""); \
        name = const_cast<TypeName&>(other); \
        return *this; \
    } \
    TypeName& \
    __attribute__ ((noinline)) \
    operator->() { \
       asm (""); \
        return get(); \
    } \
    operator TypeName() { return get(); } \
    TypeName& \
    __attribute__ ((noinline)) \
    get() { \
        asm (""); \
        return name; \
    } \
   \
    TypeName* \
    operator&() { \
        asm (""); \
        return &name; \
    } \
} name##_Safe

, .

#include <stdio.h>
#include "TLS.h"

struct Foo {
    int x;
    int y;
};

__thread Foo* volatile foo;
__thread int bar;

SafeTLS(Foo* volatile, foo);
SafeTLS(int, bar);

void f2() {
    asm("nop");
}
void f() {
    foo_Safe->x = 5;
    f2();
    asm volatile("":::"memory");
    // We desire a second computation of the address of foo here as an offset
    // from the FS register.
    (*foo_Safe).y = 7;
    bar = 7;
    printf("%d\n", bar);
    printf("%d %d\n", foo->x, foo->y);

    bar = 8;
    printf("%d\n", bar_Safe.get());
}

int main(){
    foo = new Foo;
    f();
    delete foo;
}
0

Source: https://habr.com/ru/post/1673450/


All Articles