partition_tls_win.cc 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/allocator/partition_allocator/partition_tls.h"
  5. #include <windows.h>
  6. namespace partition_alloc::internal {
  7. namespace {
  8. // Store the key as the thread destruction callback doesn't get it.
  9. PartitionTlsKey g_key;
  10. void (*g_destructor)(void*) = nullptr;
  11. void (*g_on_dll_process_detach)() = nullptr;
  12. // Static callback function to call with each thread termination.
  13. void NTAPI PartitionTlsOnThreadExit(PVOID module,
  14. DWORD reason,
  15. PVOID reserved) {
  16. if (reason != DLL_THREAD_DETACH && reason != DLL_PROCESS_DETACH)
  17. return;
  18. if (reason == DLL_PROCESS_DETACH && g_on_dll_process_detach)
  19. g_on_dll_process_detach();
  20. if (g_destructor) {
  21. void* per_thread_data = PartitionTlsGet(g_key);
  22. if (per_thread_data)
  23. g_destructor(per_thread_data);
  24. }
  25. }
  26. } // namespace
  27. bool PartitionTlsCreate(PartitionTlsKey* key, void (*destructor)(void*)) {
  28. PA_CHECK(g_destructor == nullptr); // Only one TLS key supported at a time.
  29. PartitionTlsKey value = TlsAlloc();
  30. if (value != TLS_OUT_OF_INDEXES) {
  31. *key = value;
  32. g_key = value;
  33. g_destructor = destructor;
  34. return true;
  35. }
  36. return false;
  37. }
  38. void PartitionTlsSetOnDllProcessDetach(void (*callback)()) {
  39. g_on_dll_process_detach = callback;
  40. }
  41. } // namespace partition_alloc::internal
  42. // See thread_local_storage_win.cc for details and reference.
  43. //
  44. // The callback has to be in any section between .CRT$XLA and .CRT$XLZ, as these
  45. // are sentinels used by the TLS code to find the callback array bounds. As we
  46. // don't particularly care about where we are called but would prefer to be
  47. // deinitialized towards the end (in particular after Chromium's TLS), we locate
  48. // ourselves in .CRT$XLY.
  49. // Force a reference to _tls_used to make the linker create the TLS directory if
  50. // it's not already there. (e.g. if __declspec(thread) is not used). Force a
  51. // reference to partition_tls_thread_exit_callback to prevent whole program
  52. // optimization from discarding the variable.
  53. #ifdef _WIN64
  54. #pragma comment(linker, "/INCLUDE:_tls_used")
  55. #pragma comment(linker, "/INCLUDE:partition_tls_thread_exit_callback")
  56. #else // _WIN64
  57. #pragma comment(linker, "/INCLUDE:__tls_used")
  58. #pragma comment(linker, "/INCLUDE:_partition_tls_thread_exit_callback")
  59. #endif // _WIN64
  60. // extern "C" suppresses C++ name mangling so we know the symbol name for the
  61. // linker /INCLUDE:symbol pragma above.
  62. extern "C" {
  63. // The linker must not discard partition_tls_thread_exit_callback. (We force a
  64. // reference to this variable with a linker /INCLUDE:symbol pragma to ensure
  65. // that.) If this variable is discarded, PartitionTlsOnThreadExit will never be
  66. // called.
  67. #ifdef _WIN64
  68. // .CRT section is merged with .rdata on x64 so it must be constant data.
  69. #pragma const_seg(".CRT$XLY")
  70. // When defining a const variable, it must have external linkage to be sure the
  71. // linker doesn't discard it.
  72. extern const PIMAGE_TLS_CALLBACK partition_tls_thread_exit_callback;
  73. const PIMAGE_TLS_CALLBACK partition_tls_thread_exit_callback =
  74. partition_alloc::internal::PartitionTlsOnThreadExit;
  75. // Reset the default section.
  76. #pragma const_seg()
  77. #else // _WIN64
  78. #pragma data_seg(".CRT$XLY")
  79. PIMAGE_TLS_CALLBACK partition_tls_thread_exit_callback =
  80. partition_alloc::internal::PartitionTlsOnThreadExit;
  81. // Reset the default section.
  82. #pragma data_seg()
  83. #endif // _WIN64
  84. } // extern "C"