| /* 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 <array> |
| #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" |