Локальное хранилище потоков не работает с использованием потоков, созданных с помощью clone () - PullRequest
2 голосов
/ 06 июля 2019

Программа ниже создает десять потоков, используя системный вызов Linux clone().Статическая переменная tls имеет атрибут C11 thread_local.Потоки выполняют функцию child_func, которая просто увеличивает переменную tls и сохраняет увеличенное значение в месте, указанном аргументом arg.Приращенные значения сохраняются, по одному для каждого потока, в массиве tlsvals.Функция main ожидает окончания потоков и затем печатает значения десяти экземпляров переменной tls.(Я пропустил проверку ошибок и освобождение выделенных стеков в этой версии кода, чтобы сохранить пример кратким.)

#define _GNU_SOURCE
#include <sched.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <threads.h>        /* For thread_local. */

#define NTHREADS 10

static thread_local int tls = 0;

static int child_func(void *arg)
{
  tls++;
  *((int *)arg) = tls;
  return 0;
}

int main(int argc, char** argv)
{
  int tlsvals[NTHREADS];
  for (int n = 0; n < NTHREADS; n++) {
    const int STACK_SIZE = 65536;
    if (clone(child_func, malloc(STACK_SIZE) + STACK_SIZE, CLONE_VM | SIGCHLD, tlsvals+n) == -1) {
      perror("clone");
      exit(1);
    }
  }

  int ret, status;
  while ((ret = wait(&status)) != -1)
    ;

  for (int *p = tlsvals; p < tlsvals + NTHREADS; p++)
    printf("The value of tls is %d\n", *p);

  return 0;
}

Поскольку переменная tls помечена thread_local, я ожидалпрограмма для печати десяти строк The value of tls is 1, поскольку каждый поток увеличивает переменную один раз от своего начального нулевого значения.Вместо этого я получаю следующий вывод:

The value of tls is 1
The value of tls is 3
The value of tls is 2
The value of tls is 4
The value of tls is 5
The value of tls is 6
The value of tls is 7
The value of tls is 8
The value of tls is 9
The value of tls is 10

Так что, похоже, переменная tls вовсе не является локальной для потока, а используется всеми потоками, и каждый поток увеличивает одну и ту же переменную.

Код был скомпилирован с GCC версии 9.1.0 на x86_64 с использованием следующей командной строки:

gcc -O2 -Wall -std=c11 tfoo.c -o tfooc

Я также попытался использовать специфический для GCC атрибут __thread вместо thread_local, с тем жерезультат.

Глядя на сборку, созданную GCC, я вижу, что переменная доступна через регистр %fs, как и должно быть:

child_func:
.LFB24:
    .cfi_startproc
    movl    %fs:tls@tpoff, %eax
    addl    $1, %eax
    movl    %eax, %fs:tls@tpoff
    movl    %eax, (%rdi)
    xorl    %eax, %eax
    ret
    .cfi_endproc

Что я делаю неправильно, или есть ошибка в реализации thread_local в Gcc?

...