diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/heap/v8_wrapper')
9 files changed, 217 insertions, 50 deletions
diff --git a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/blink_gc_memory_dump_provider.cc b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/blink_gc_memory_dump_provider.cc index dd1edb88b0d..e31f7721321 100644 --- a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/blink_gc_memory_dump_provider.cc +++ b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/blink_gc_memory_dump_provider.cc @@ -5,8 +5,13 @@ #include "third_party/blink/renderer/platform/heap/v8_wrapper/blink_gc_memory_dump_provider.h" #include <inttypes.h> +#include <ios> +#include <sstream> +#include <string> +#include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" +#include "base/trace_event/memory_allocator_dump.h" #include "base/trace_event/memory_dump_manager.h" #include "third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h" #include "v8/include/cppgc/heap-statistics.h" @@ -24,6 +29,27 @@ constexpr const char* HeapTypeString( } } +void RecordType( + std::vector<cppgc::HeapStatistics::ObjectStatsEntry>& global_object_stats, + const cppgc::HeapStatistics::ObjectStatsEntry& local_object_stats, + size_t entry_index) { + global_object_stats[entry_index].allocated_bytes += + local_object_stats.allocated_bytes; + global_object_stats[entry_index].object_count += + local_object_stats.object_count; +} + +// Use the id to generate a unique name as different types may provide the same +// string as typename. This happens in component builds when cppgc creates +// different internal types for the same C++ class when it is instantiated from +// different libraries. +std::string GetUniqueName(std::string name, size_t id) { + std::stringstream stream; + // Convert the id to hex to avoid it reading like an object count. + stream << name << " (0x" << std::hex << id << ")"; + return stream.str(); +} + } // namespace BlinkGCMemoryDumpProvider::BlinkGCMemoryDumpProvider( @@ -33,7 +59,7 @@ BlinkGCMemoryDumpProvider::BlinkGCMemoryDumpProvider( : thread_state_(thread_state), heap_type_(heap_type), dump_base_name_( - "blink_gc/" + std::string(HeapTypeString(heap_type_)) + "/heap" + + "blink_gc/" + std::string(HeapTypeString(heap_type_)) + (heap_type_ == HeapType::kBlinkWorkerThread ? "/" + base::StringPrintf( "worker_0x%" PRIXPTR, @@ -60,10 +86,11 @@ bool BlinkGCMemoryDumpProvider::OnMemoryDump( ::cppgc::HeapStatistics stats = ThreadState::Current()->cpp_heap().CollectStatistics(detail_level); - auto* heap_dump = process_memory_dump->CreateAllocatorDump(dump_base_name_); + auto* heap_dump = + process_memory_dump->CreateAllocatorDump(dump_base_name_ + "/heap"); heap_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, base::trace_event::MemoryAllocatorDump::kUnitsBytes, - stats.physical_size_bytes); + stats.resident_size_bytes); heap_dump->AddScalar("allocated_objects_size", base::trace_event::MemoryAllocatorDump::kUnitsBytes, stats.used_size_bytes); @@ -72,16 +99,19 @@ bool BlinkGCMemoryDumpProvider::OnMemoryDump( return true; } - // Detailed statistics. + // Aggregate global object stats from per page statistics. + std::vector<cppgc::HeapStatistics::ObjectStatsEntry> global_object_stats; + global_object_stats.resize(stats.type_names.size()); + + // Detailed statistics follow. for (const ::cppgc::HeapStatistics::SpaceStatistics& space_stats : stats.space_stats) { - std::string arena_dump_name = dump_base_name_ + "/" + space_stats.name; - auto* arena_dump = - process_memory_dump->CreateAllocatorDump(arena_dump_name); - arena_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, + auto* space_dump = process_memory_dump->CreateAllocatorDump( + heap_dump->absolute_name() + "/" + space_stats.name); + space_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, base::trace_event::MemoryAllocatorDump::kUnitsBytes, - space_stats.physical_size_bytes); - arena_dump->AddScalar("allocated_objects_size", + space_stats.resident_size_bytes); + space_dump->AddScalar("allocated_objects_size", base::trace_event::MemoryAllocatorDump::kUnitsBytes, space_stats.used_size_bytes); @@ -89,19 +119,42 @@ bool BlinkGCMemoryDumpProvider::OnMemoryDump( for (const ::cppgc::HeapStatistics::PageStatistics& page_stats : space_stats.page_stats) { auto* page_dump = process_memory_dump->CreateAllocatorDump( - arena_dump_name + "/pages/page_" + + space_dump->absolute_name() + "/pages/page_" + base::NumberToString(page_count++)); + page_dump->AddScalar("committed_size", + base::trace_event::MemoryAllocatorDump::kUnitsBytes, + page_stats.committed_size_bytes); page_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, base::trace_event::MemoryAllocatorDump::kUnitsBytes, - page_stats.physical_size_bytes); + page_stats.resident_size_bytes); page_dump->AddScalar("allocated_objects_size", base::trace_event::MemoryAllocatorDump::kUnitsBytes, page_stats.used_size_bytes); + + const auto& object_stats = page_stats.object_statistics; + for (size_t i = 0; i < object_stats.size(); i++) { + if (!object_stats[i].object_count) + continue; + + auto* page_class_dump = process_memory_dump->CreateAllocatorDump( + page_dump->absolute_name() + "/types/" + + GetUniqueName(stats.type_names[i], i)); + page_class_dump->AddScalar( + base::trace_event::MemoryAllocatorDump::kNameObjectCount, + base::trace_event::MemoryAllocatorDump::kUnitsObjects, + object_stats[i].object_count); + page_class_dump->AddScalar( + "allocated_objects_size", + base::trace_event::MemoryAllocatorDump::kUnitsBytes, + object_stats[i].allocated_bytes); + + RecordType(global_object_stats, object_stats[i], i); + } } const ::cppgc::HeapStatistics::FreeListStatistics& free_list_stats = space_stats.free_list_stats; - for (wtf_size_t i = 0; i < free_list_stats.bucket_size.size(); ++i) { + for (size_t i = 0; i < free_list_stats.bucket_size.size(); ++i) { constexpr size_t kDigits = 8; std::string original_bucket_size = base::NumberToString(free_list_stats.bucket_size[i]); @@ -109,28 +162,40 @@ bool BlinkGCMemoryDumpProvider::OnMemoryDump( std::string(kDigits - original_bucket_size.length(), '0') + original_bucket_size; auto* free_list_bucket_dump = process_memory_dump->CreateAllocatorDump( - arena_dump_name + "/freelist/bucket_" + padded_bucket_size); + space_dump->absolute_name() + "/freelist/bucket_" + + padded_bucket_size); free_list_bucket_dump->AddScalar( - "free_size", base::trace_event::MemoryAllocatorDump::kUnitsBytes, + "free_slot_count", + base::trace_event::MemoryAllocatorDump::kUnitsObjects, + free_list_stats.free_count[i]); + free_list_bucket_dump->AddScalar( + "free_usable_size", + base::trace_event::MemoryAllocatorDump::kUnitsBytes, free_list_stats.free_size[i]); } + } - const ::cppgc::HeapStatistics::ObjectStatistics& object_stats = - space_stats.object_stats; - for (wtf_size_t i = 1; i < object_stats.num_types; i++) { - if (object_stats.type_name[i].empty()) - continue; - - auto* class_dump = process_memory_dump->CreateAllocatorDump( - arena_dump_name + "/classes/" + object_stats.type_name[i]); - class_dump->AddScalar( - "object_count", base::trace_event::MemoryAllocatorDump::kUnitsObjects, - object_stats.type_count[i]); - class_dump->AddScalar("object_size", - base::trace_event::MemoryAllocatorDump::kUnitsBytes, - object_stats.type_bytes[i]); - } + // Populate "allocated_objects" and "blink_objects/blink_gc" dumps. + const auto* allocated_objects_dump = process_memory_dump->CreateAllocatorDump( + dump_base_name_ + "/allocated_objects"); + for (size_t i = 0; i < global_object_stats.size(); i++) { + auto* details = process_memory_dump->CreateAllocatorDump( + "blink_objects/blink_gc/" + GetUniqueName(stats.type_names[i], i)); + details->AddScalar("allocated_objects_size", + base::trace_event::MemoryAllocatorDump::kUnitsBytes, + global_object_stats[i].allocated_bytes); + details->AddScalar(base::trace_event::MemoryAllocatorDump::kNameObjectCount, + base::trace_event::MemoryAllocatorDump::kUnitsObjects, + global_object_stats[i].object_count); + details->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize, + base::trace_event::MemoryAllocatorDump::kUnitsBytes, + global_object_stats[i].allocated_bytes); + process_memory_dump->AddSuballocation( + details->guid(), dump_base_name_ + "/allocated_objects"); } + process_memory_dump->AddOwnershipEdge(allocated_objects_dump->guid(), + heap_dump->guid()); + return true; } diff --git a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/collection_support/heap_hash_table_backing.h b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/collection_support/heap_hash_table_backing.h index 2d81ed8ee91..ad05f4a5e59 100644 --- a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/collection_support/heap_hash_table_backing.h +++ b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/collection_support/heap_hash_table_backing.h @@ -14,6 +14,7 @@ #include "third_party/blink/renderer/platform/wtf/conditional_destructor.h" #include "third_party/blink/renderer/platform/wtf/hash_table.h" #include "third_party/blink/renderer/platform/wtf/hash_traits.h" +#include "third_party/blink/renderer/platform/wtf/sanitizers.h" #include "v8/include/cppgc/custom-space.h" #include "v8/include/cppgc/explicit-management.h" #include "v8/include/cppgc/object-size-trait.h" @@ -30,11 +31,16 @@ class HeapHashTableBacking final using ValueType = typename Table::ValueType; public: - static ValueType* ToArray(ClassType* backing) { + // Although the HeapHashTableBacking is fully constructed, the array resulting + // from ToArray may not be fully constructed as the elements of the array are + // not initialized and may have null vtable pointers. Null vtable pointer + // violates CFI for polymorphic types. + NO_SANITIZE_UNRELATED_CAST ALWAYS_INLINE static ValueType* ToArray( + ClassType* backing) { return reinterpret_cast<ValueType*>(backing); } - static ClassType* FromArray(ValueType* array) { + ALWAYS_INLINE static ClassType* FromArray(ValueType* array) { return reinterpret_cast<ClassType*>(array); } diff --git a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/collection_support/heap_vector_backing.h b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/collection_support/heap_vector_backing.h index 68590093c5a..e2db74b9209 100644 --- a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/collection_support/heap_vector_backing.h +++ b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/collection_support/heap_vector_backing.h @@ -12,6 +12,7 @@ #include "third_party/blink/renderer/platform/heap/trace_traits.h" #include "third_party/blink/renderer/platform/wtf/conditional_destructor.h" #include "third_party/blink/renderer/platform/wtf/container_annotations.h" +#include "third_party/blink/renderer/platform/wtf/sanitizers.h" #include "third_party/blink/renderer/platform/wtf/type_traits.h" #include "third_party/blink/renderer/platform/wtf/vector_traits.h" #include "v8/include/cppgc/allocation.h" @@ -38,11 +39,16 @@ class HeapVectorBacking final using ClassType = HeapVectorBacking<T, Traits>; public: - static T* ToArray(ClassType* backing) { + // Although the HeapVectorBacking is fully constructed, the array resulting + // from ToArray may not be fully constructed as the elements of the array are + // not initialized and may have null vtable pointers. Null vtable pointer + // violates CFI for polymorphic types. + NO_SANITIZE_UNRELATED_CAST ALWAYS_INLINE static T* ToArray( + ClassType* backing) { return reinterpret_cast<T*>(backing); } - static ClassType* FromArray(T* payload) { + ALWAYS_INLINE static ClassType* FromArray(T* payload) { return reinterpret_cast<ClassType*>(payload); } diff --git a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/heap.h b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/heap.h index e81eb2f4d80..0b452aa7a68 100644 --- a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/heap.h +++ b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/heap.h @@ -9,6 +9,7 @@ #include "third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h" #include "v8/include/cppgc/allocation.h" #include "v8/include/cppgc/garbage-collected.h" +#include "v8/include/cppgc/internal/pointer-policies.h" #include "v8/include/cppgc/liveness-broker.h" namespace blink { @@ -41,6 +42,10 @@ T* MakeGarbageCollected(AdditionalBytes additional_bytes, Args&&... args) { std::forward<Args>(args)...); } +static constexpr bool kBlinkGCHasDebugChecks = + !std::is_same<cppgc::internal::DefaultMemberCheckingPolicy, + cppgc::internal::DisabledCheckingPolicy>::value; + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_HEAP_H_ diff --git a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/heap_allocator_impl.h b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/heap_allocator_impl.h index b36b1d24e46..4179e550510 100644 --- a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/heap_allocator_impl.h +++ b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/heap_allocator_impl.h @@ -137,11 +137,11 @@ class PLATFORM_EXPORT HeapAllocator { } template <typename T> - static void TraceBackingStoreIfMarked(T** slot) { + static void TraceBackingStoreIfMarked(T* object) { HeapConsistency::WriteBarrierParams params; - if (HeapConsistency::GetWriteBarrierType(slot, *slot, params) == + if (HeapConsistency::GetWriteBarrierType(object, params) == HeapConsistency::WriteBarrierType::kMarking) { - HeapConsistency::SteeleWriteBarrier(params, *slot); + HeapConsistency::SteeleWriteBarrier(params, object); } } diff --git a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_local.h b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_local.h new file mode 100644 index 00000000000..1a0c43b0eaf --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_local.h @@ -0,0 +1,63 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_THREAD_LOCAL_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_THREAD_LOCAL_H_ + +#include "base/compiler_specific.h" +#include "build/build_config.h" +#include "third_party/blink/renderer/platform/platform_export.h" + +// On component builds, always hide the thread_local variable behind a call. +// This avoids complexity with "global-dyn" and allows to use "local-dyn" +// instead, across all platforms. On non-component (release) builds, don't hide +// the variable behind the call (to improve performance in access time), but use +// different tls models on different platforms. On Windows, since chrome is +// linked into the chrome.dll which is always linked to chrome.exe at static +// link time (DT_NEEDED in ELF terms), use "init-exec". On Android, since the +// library can be opened with "dlopen" (through JNI), use "local-dyn". On other +// systems (Linux/ChromeOS/MacOS) use the fastest "local-exec". + +// |_____component_____|___non-component___| +// ________|_tls_model__|_hide_|_tls_model__|_hide_| +// Windows | local-dyn | yes | init-exec | no | +// Android | local-dyn | yes | local-dyn | no | +// Other | local-dyn | yes | local-exec | no | + +// The call is still cheaper than multiple calls through WTF/base/pthread* +// layers. +#if defined(COMPONENT_BUILD) +#define BLINK_HEAP_HIDE_THREAD_LOCAL_IN_LIBRARY 1 +#else +#define BLINK_HEAP_HIDE_THREAD_LOCAL_IN_LIBRARY 0 +#endif + +#if BLINK_HEAP_HIDE_THREAD_LOCAL_IN_LIBRARY +#define BLINK_HEAP_THREAD_LOCAL_MODEL "local-dynamic" +#else +#if defined(OS_WIN) +#define BLINK_HEAP_THREAD_LOCAL_MODEL "initial-exec" +#elif defined(OS_ANDROID) +#define BLINK_HEAP_THREAD_LOCAL_MODEL "local-dynamic" +#else +#define BLINK_HEAP_THREAD_LOCAL_MODEL "local-exec" +#endif +#endif + +#if defined(BLINK_HEAP_HIDE_THREAD_LOCAL_IN_LIBRARY) + +#define BLINK_HEAP_DECLARE_THREAD_LOCAL_GETTER(Name, Type, Member) \ + static NOINLINE Type Name(); +#define BLINK_HEAP_DEFINE_THREAD_LOCAL_GETTER(Name, Type, Member) \ + NOINLINE Type Name() { return Member; } + +#else // !defined(BLINK_HEAP_HIDE_THREAD_LOCAL_IN_LIBRARY) + +#define BLINK_HEAP_DECLARE_THREAD_LOCAL_GETTER(Name, Type, Member) \ + static ALWAYS_INLINE Type Name() { return Member; } +#define BLINK_HEAP_DEFINE_THREAD_LOCAL_GETTER(Name, Type, Member) + +#endif // defined(BLINK_HEAP_HIDE_THREAD_LOCAL_IN_LIBRARY) + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_THREAD_LOCAL_H_ diff --git a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.cc b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.cc index 78da3ce32f9..303c2839488 100644 --- a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.cc +++ b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.cc @@ -82,14 +82,17 @@ class BlinkRootsHandler final : public v8::EmbedderRootsHandler { } // namespace -// static -base::LazyInstance<WTF::ThreadSpecific<ThreadState*>>::Leaky - ThreadState::thread_specific_ = LAZY_INSTANCE_INITIALIZER; +thread_local ThreadState* g_thread_specific_ CONSTINIT + __attribute__((tls_model(BLINK_HEAP_THREAD_LOCAL_MODEL))) = nullptr; // static alignas(ThreadState) uint8_t ThreadState::main_thread_state_storage_[sizeof(ThreadState)]; +BLINK_HEAP_DEFINE_THREAD_LOCAL_GETTER(ThreadState::Current, + ThreadState*, + g_thread_specific_) + // static ThreadState* ThreadState::AttachMainThread() { return new (main_thread_state_storage_) ThreadState(gin::V8Platform::Get()); @@ -149,7 +152,7 @@ ThreadState::ThreadState(v8::Platform* platform) allocation_handle_(cpp_heap_->GetAllocationHandle()), heap_handle_(cpp_heap_->GetHeapHandle()), thread_id_(CurrentThread()) { - *(thread_specific_.Get()) = this; + g_thread_specific_ = this; } ThreadState::~ThreadState() { diff --git a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h index 8b6b1f920e0..682a0b54c5b 100644 --- a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h +++ b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state.h @@ -6,11 +6,11 @@ #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_THREAD_STATE_H_ #include "base/compiler_specific.h" -#include "base/lazy_instance.h" +#include "build/build_config.h" #include "third_party/blink/renderer/platform/heap/blink_gc.h" +#include "third_party/blink/renderer/platform/heap/v8_wrapper/thread_local.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" -#include "third_party/blink/renderer/platform/wtf/thread_specific.h" #include "third_party/blink/renderer/platform/wtf/threading.h" #include "v8-profiler.h" #include "v8/include/cppgc/heap-consistency.h" @@ -61,13 +61,20 @@ using V8BuildEmbedderGraphCallback = void (*)(v8::Isolate*, v8::EmbedderGraph*, void*); +// Storage for all ThreadState objects. This includes the main-thread +// ThreadState as well. Keep it outside the class so that PLATFORM_EXPORT +// doesn't apply to it (otherwise, clang-cl complains). +extern thread_local ThreadState* g_thread_specific_ CONSTINIT + __attribute__((tls_model(BLINK_HEAP_THREAD_LOCAL_MODEL))); + class PLATFORM_EXPORT ThreadState final { public: class NoAllocationScope; + class GCForbiddenScope; - static ALWAYS_INLINE ThreadState* Current() { - return *(thread_specific_.Get()); - } + BLINK_HEAP_DECLARE_THREAD_LOCAL_GETTER(Current, + ThreadState*, + g_thread_specific_) static ALWAYS_INLINE ThreadState* MainThreadState() { return reinterpret_cast<ThreadState*>(main_thread_state_storage_); @@ -132,10 +139,6 @@ class PLATFORM_EXPORT ThreadState final { // Main-thread ThreadState avoids TLS completely by using a regular global. // The object is manually managed and should not rely on global ctor/dtor. static uint8_t main_thread_state_storage_[]; - // Storage for all ThreadState objects. This includes the main-thread - // ThreadState as well. - static base::LazyInstance<WTF::ThreadSpecific<ThreadState*>>::Leaky - thread_specific_; explicit ThreadState(v8::Platform*); ~ThreadState(); diff --git a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state_scopes.h b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state_scopes.h index bec9605a0c6..82e71e98598 100644 --- a/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state_scopes.h +++ b/chromium/third_party/blink/renderer/platform/heap/v8_wrapper/thread_state_scopes.h @@ -26,6 +26,22 @@ class ThreadState::NoAllocationScope final { const cppgc::subtle::DisallowGarbageCollectionScope disallow_gc_; }; +// The GCForbiddenScope class is used to prevent GC finalization +// when it is not safe to do so. +class ThreadState::GCForbiddenScope final { + STACK_ALLOCATED(); + + public: + explicit GCForbiddenScope(ThreadState* state) + : no_gc_(state->cpp_heap().GetHeapHandle()) {} + + GCForbiddenScope(const NoAllocationScope&) = delete; + GCForbiddenScope& operator=(const NoAllocationScope&) = delete; + + private: + const cppgc::subtle::NoGarbageCollectionScope no_gc_; +}; + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_V8_WRAPPER_THREAD_STATE_SCOPES_H_ |