| /* | 
 |  * Copyright (C) 2015 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | #include <gtest/gtest.h> | 
 | #include <stdint.h> | 
 | #include <string.h> | 
 |  | 
 | __thread int local_var = 100; | 
 | int shared_var = 200; | 
 |  | 
 | static void reset_vars() { | 
 |   local_var = 1000; | 
 |   shared_var = 2000; | 
 |   // local_var should be reset by threads | 
 | } | 
 |  | 
 | typedef void* (*MyThread)(void*); | 
 |  | 
 | static void* inc_shared_var(void* p) { | 
 |   int *data = reinterpret_cast<int*>(p); | 
 |   shared_var++; | 
 |   *data = shared_var; | 
 |   return nullptr; | 
 | } | 
 |  | 
 | static void* inc_local_var(void* p) { | 
 |   int *data = reinterpret_cast<int*>(p); | 
 |   local_var++; | 
 |   *data = local_var; | 
 |   return nullptr; | 
 | } | 
 |  | 
 | static int run_one_thread(MyThread foo) { | 
 |   pthread_t t; | 
 |   int data; | 
 |   int error = pthread_create(&t, nullptr, foo, &data); | 
 |   if (!error) | 
 |       error = pthread_join(t, nullptr); | 
 |   return error ? error : data; | 
 | } | 
 |  | 
 | TEST(thread_local_storage, shared) { | 
 |   reset_vars(); | 
 |   ASSERT_EQ(local_var, 1000); | 
 |   ASSERT_EQ(shared_var, 2000); | 
 |  | 
 |   // Update shared_var, local_var remains 1000. | 
 |   ASSERT_EQ(run_one_thread(inc_shared_var), 2001); | 
 |   ASSERT_EQ(local_var, 1000); | 
 |   ASSERT_EQ(shared_var, 2001); | 
 |  | 
 |   ASSERT_EQ(run_one_thread(inc_shared_var), 2002); | 
 |   ASSERT_EQ(local_var, 1000); | 
 |   ASSERT_EQ(shared_var, 2002); | 
 |  | 
 |   ASSERT_EQ(run_one_thread(inc_shared_var), 2003); | 
 |   ASSERT_EQ(local_var, 1000); | 
 |   ASSERT_EQ(shared_var, 2003); | 
 | } | 
 |  | 
 | TEST(thread_local_storage, local) { | 
 |   reset_vars(); | 
 |   ASSERT_EQ(local_var, 1000); | 
 |   ASSERT_EQ(shared_var, 2000); | 
 |  | 
 |   // When a child thread updates its own TLS variable, | 
 |   // this thread's local_var and shared_var are not changed. | 
 |   // TLS local_var is initialized to 100 in a thread. | 
 |   ASSERT_EQ(run_one_thread(inc_local_var), 101); | 
 |   ASSERT_EQ(local_var, 1000); | 
 |   ASSERT_EQ(shared_var, 2000); | 
 |  | 
 |   ASSERT_EQ(run_one_thread(inc_local_var), 101); | 
 |   ASSERT_EQ(local_var, 1000); | 
 |   ASSERT_EQ(shared_var, 2000); | 
 |  | 
 |   ASSERT_EQ(run_one_thread(inc_local_var), 101); | 
 |   ASSERT_EQ(local_var, 1000); | 
 |   ASSERT_EQ(shared_var, 2000); | 
 | } | 
 |  | 
 | // Test TLS initialization of more complicated type, array of struct. | 
 | struct Point { | 
 |   int x, y; | 
 | }; | 
 |  | 
 | typedef Point Triangle[3]; | 
 |  | 
 | __thread Triangle local_triangle = {{10,10}, {20,20}, {30,30}}; | 
 | Triangle shared_triangle = {{1,1}, {2,2}, {3,3}}; | 
 |  | 
 | static void reset_triangle() { | 
 |   static const Triangle t1 = {{3,3}, {4,4}, {5,5}}; | 
 |   static const Triangle t2 = {{2,2}, {3,3}, {4,4}}; | 
 |   memcpy(local_triangle, t1, sizeof(local_triangle)); | 
 |   memcpy(shared_triangle, t2, sizeof(shared_triangle)); | 
 | } | 
 |  | 
 | static void* move_shared_triangle(void* p) { | 
 |   int *data = reinterpret_cast<int*>(p); | 
 |   shared_triangle[1].y++; | 
 |   *data = shared_triangle[1].y; | 
 |   return nullptr; | 
 | } | 
 |  | 
 | static void* move_local_triangle(void* p) { | 
 |   int *data = reinterpret_cast<int*>(p); | 
 |   local_triangle[1].y++; | 
 |   *data = local_triangle[1].y; | 
 |   return nullptr; | 
 | } | 
 |  | 
 | TEST(thread_local_storage, shared_triangle) { | 
 |   reset_triangle(); | 
 |   ASSERT_EQ(local_triangle[1].y, 4); | 
 |   ASSERT_EQ(shared_triangle[1].y, 3); | 
 |  | 
 |   // Update shared_triangle, local_triangle remains 1000. | 
 |   ASSERT_EQ(run_one_thread(move_shared_triangle), 4); | 
 |   ASSERT_EQ(local_triangle[1].y, 4); | 
 |   ASSERT_EQ(shared_triangle[1].y, 4); | 
 |  | 
 |   ASSERT_EQ(run_one_thread(move_shared_triangle), 5); | 
 |   ASSERT_EQ(local_triangle[1].y, 4); | 
 |   ASSERT_EQ(shared_triangle[1].y, 5); | 
 |  | 
 |   ASSERT_EQ(run_one_thread(move_shared_triangle), 6); | 
 |   ASSERT_EQ(local_triangle[1].y, 4); | 
 |   ASSERT_EQ(shared_triangle[1].y, 6); | 
 | } | 
 |  | 
 | TEST(thread_local_storage, local_triangle) { | 
 |   reset_triangle(); | 
 |   ASSERT_EQ(local_triangle[1].y, 4); | 
 |   ASSERT_EQ(shared_triangle[1].y, 3); | 
 |  | 
 |   // Update local_triangle, parent thread's | 
 |   // shared_triangle and local_triangle are unchanged. | 
 |   ASSERT_EQ(run_one_thread(move_local_triangle), 21); | 
 |   ASSERT_EQ(local_triangle[1].y, 4); | 
 |   ASSERT_EQ(shared_triangle[1].y, 3); | 
 |  | 
 |   ASSERT_EQ(run_one_thread(move_local_triangle), 21); | 
 |   ASSERT_EQ(local_triangle[1].y, 4); | 
 |   ASSERT_EQ(shared_triangle[1].y, 3); | 
 |  | 
 |   ASSERT_EQ(run_one_thread(move_local_triangle), 21); | 
 |   ASSERT_EQ(local_triangle[1].y, 4); | 
 |   ASSERT_EQ(shared_triangle[1].y, 3); | 
 | } | 
 |  | 
 | // Test emutls runtime data structures and __emutls_get_address function. | 
 | typedef unsigned int gcc_word __attribute__((mode(word))); | 
 | typedef unsigned int gcc_pointer __attribute__((mode(pointer))); | 
 | struct gcc_emutls_object {  // for libgcc | 
 |   gcc_word size; | 
 |   gcc_word align; | 
 |   union { | 
 |     gcc_pointer offset; | 
 |     void* ptr; | 
 |   } loc; | 
 |   void* templ; | 
 | }; | 
 |  | 
 | typedef struct __emutls_control {  // for clang/llvm | 
 |   size_t size; | 
 |   size_t align; | 
 |   union { | 
 |     uintptr_t index; | 
 |     void* address; | 
 |   } object; | 
 |   void* value; | 
 | } __emutls_control; | 
 |  | 
 | TEST(thread_local_storage, type_size) { | 
 |   static_assert(sizeof(size_t) == sizeof(gcc_word), | 
 |                 "size_t != gcc_word"); | 
 |   static_assert(sizeof(uintptr_t) == sizeof(gcc_pointer), | 
 |                 "uintptr_t != gcc_pointer"); | 
 |   static_assert(sizeof(uintptr_t) == sizeof(void*), | 
 |                 "sizoeof(uintptr_t) != sizeof(void*)"); | 
 |   static_assert(sizeof(__emutls_control) == sizeof(struct gcc_emutls_object), | 
 |                 "sizeof(__emutls_control) != sizeof(struct gcc_emutls_object)"); | 
 | } | 
 |  | 
 | extern "C" void* __emutls_get_address(__emutls_control*); | 
 |  | 
 | TEST(thread_local_storage, init_value) { | 
 |   char tls_value1[] = "123456789"; | 
 |   char tls_value2[] = "abcdefghi"; | 
 |   constexpr size_t num_saved_values = 10; | 
 |   __emutls_control tls_var[num_saved_values]; | 
 |   size_t prev_index = 0; | 
 |   void* saved_gap[num_saved_values]; | 
 |   void* saved_p[num_saved_values]; | 
 |   ASSERT_TRUE(strlen(tls_value2) <= strlen(tls_value1)); | 
 |   __emutls_control c = | 
 |       {strlen(tls_value1) + 1, 1, {0}, tls_value1}; | 
 |   for (size_t n = 0; n < num_saved_values; n++) { | 
 |     memcpy(&tls_var[n], &c, sizeof(c)); | 
 |     tls_var[n].align = (1 << n); | 
 |   } | 
 |   for (size_t n = 0; n < num_saved_values; n++) { | 
 |     // Try to mess up malloc space so that the next malloc will not have the | 
 |     // required alignment, but __emutls_get_address should still return an | 
 |     // aligned address. | 
 |     saved_gap[n] = malloc(1); | 
 |     void* p = __emutls_get_address(&tls_var[n]); | 
 |     saved_p[n] = p; | 
 |     ASSERT_TRUE(p != nullptr); | 
 |     ASSERT_TRUE(tls_var[n].object.index != 0); | 
 |     // check if p is a new object. | 
 |     if (n > 0) { | 
 |       // In single-thread environment, object.address == p. | 
 |       // In multi-threads environment, object.index is increased. | 
 |       ASSERT_TRUE(prev_index + 1 == tls_var[n].object.index || | 
 |                   p == tls_var[n].object.address); | 
 |       ASSERT_TRUE(p != saved_p[n - 1]); | 
 |     } | 
 |     prev_index = tls_var[n].object.index; | 
 |     // check if p is aligned | 
 |     uintptr_t align = (1 << n); | 
 |     uintptr_t address= reinterpret_cast<uintptr_t>(p); | 
 |     ASSERT_EQ((address & ~(align - 1)), address); | 
 |     // check if *p is initialized | 
 |     ASSERT_STREQ(tls_value1, static_cast<char*>(p)); | 
 |     // change value in *p | 
 |     memcpy(p, tls_value2, strlen(tls_value2) + 1); | 
 |   } | 
 |   for (size_t n = 0; n < num_saved_values; n++) { | 
 |     free(saved_gap[n]); | 
 |   } | 
 |   for (size_t n = 0; n < num_saved_values; n++) { | 
 |     void* p = __emutls_get_address(&tls_var[n]); | 
 |     ASSERT_EQ(p, saved_p[n]); | 
 |     // check if *p has the new value | 
 |     ASSERT_STREQ(tls_value2, static_cast<char*>(p)); | 
 |   } | 
 | } |