blob: 0cfdcd6d195c4877e91f2da4e8a7e04393d9be9e [file] [log] [blame]
/* Test basic thread_local support.
Copyright (C) 2015-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <functional>
#include <string>
#include <thread>
struct counter
{
int constructed {};
int destructed {};
void reset ();
};
void
counter::reset ()
{
constructed = 0;
destructed = 0;
}
static std::string
to_string (const counter &c)
{
char buf[128];
snprintf (buf, sizeof (buf), "%d/%d",
c.constructed, c.destructed);
return buf;
}
template <counter *Counter>
struct counting
{
counting () __attribute__ ((noinline, noclone));
~counting () __attribute__ ((noinline, noclone));
void operation () __attribute__ ((noinline, noclone));
};
template<counter *Counter>
__attribute__ ((noinline, noclone))
counting<Counter>::counting ()
{
++Counter->constructed;
}
template<counter *Counter>
__attribute__ ((noinline, noclone))
counting<Counter>::~counting ()
{
++Counter->destructed;
}
template<counter *Counter>
void __attribute__ ((noinline, noclone))
counting<Counter>::operation ()
{
// Optimization barrier.
asm ("");
}
static counter counter_static;
static counter counter_anonymous_namespace;
static counter counter_extern;
static counter counter_function_local;
static bool errors (false);
static std::string
all_counters ()
{
return to_string (counter_static)
+ ' ' + to_string (counter_anonymous_namespace)
+ ' ' + to_string (counter_extern)
+ ' ' + to_string (counter_function_local);
}
static void
check_counters (const char *name, const char *expected)
{
std::string actual{all_counters ()};
if (actual != expected)
{
printf ("error: %s: (%s) != (%s)\n",
name, actual.c_str (), expected);
errors = true;
}
}
static void
reset_all ()
{
counter_static.reset ();
counter_anonymous_namespace.reset ();
counter_extern.reset ();
counter_function_local.reset ();
}
static thread_local counting<&counter_static> counting_static;
namespace {
thread_local counting<&counter_anonymous_namespace>
counting_anonymous_namespace;
}
extern thread_local counting<&counter_extern> counting_extern;
thread_local counting<&counter_extern> counting_extern;
static void *
thread_without_access (void *)
{
return nullptr;
}
static void *
thread_with_access (void *)
{
thread_local counting<&counter_function_local> counting_function_local;
counting_function_local.operation ();
check_counters ("early in thread_with_access", "0/0 0/0 0/0 1/0");
counting_static.operation ();
counting_anonymous_namespace.operation ();
counting_extern.operation ();
check_counters ("in thread_with_access", "1/0 1/0 1/0 1/0");
return nullptr;
}
static int
do_test (void)
{
std::function<void (void *(void *))> do_pthread =
[](void *(func) (void *))
{
pthread_t thr;
int ret = pthread_create (&thr, nullptr, func, nullptr);
if (ret != 0)
{
errno = ret;
printf ("error: pthread_create: %m\n");
errors = true;
return;
}
ret = pthread_join (thr, nullptr);
if (ret != 0)
{
errno = ret;
printf ("error: pthread_join: %m\n");
errors = true;
return;
}
};
std::function<void (void *(void *))> do_std_thread =
[](void *(func) (void *))
{
std::thread thr{[func] {func (nullptr);}};
thr.join ();
};
std::array<std::pair<const char *, std::function<void (void *(void *))>>, 2>
do_thread_X
{{
{"pthread_create", do_pthread},
{"std::thread", do_std_thread},
}};
for (auto do_thread : do_thread_X)
{
printf ("info: testing %s\n", do_thread.first);
check_counters ("initial", "0/0 0/0 0/0 0/0");
do_thread.second (thread_without_access);
check_counters ("after thread_without_access", "0/0 0/0 0/0 0/0");
reset_all ();
do_thread.second (thread_with_access);
check_counters ("after thread_with_access", "1/1 1/1 1/1 1/1");
reset_all ();
}
return errors;
}
#define TEST_FUNCTION do_test ()
#include "../test-skeleton.c"