Changeset 286573 in webkit
- Timestamp:
- Dec 6, 2021, 2:53:46 PM (4 years ago)
- Location:
- trunk/Source/bmalloc
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/bmalloc/ChangeLog
r286516 r286573 1 2021-12-06 Yusuke Suzuki <ysuzuki@apple.com> 2 3 [libpas] Set pthread_setspecific with marker in TLS destructor to detect TLS is destroyed 4 https://bugs.webkit.org/show_bug.cgi?id=233851 5 6 Reviewed by Mark Lam. 7 8 TLS has lifetime problem that, 9 10 1. TLS is destroyed 11 2. The other TLS is destroyed, and the destructor is called 12 3. The destructor touches (1)'s TLS, then revive TLS of (1) (e.g. libpas's thread local cache). 13 14 To handle these cases, pthread library (libc) repeatedly calls destructor PAS_THREAD_LOCAL_CACHE_DESTROYED times 15 so that we clean up revived TLS again. 16 17 By using this mechanism, we can emulate pthread_self_is_exiting_np so that we can avoid reviving TLS. 18 19 1. When destroying TLS, we set a marker (PAS_THREAD_LOCAL_CACHE_DESTROYED in this case) in TLS. 20 2. During the other destructor calls, we can detect that TLS is destroyed by checking pthread_getspecific(...) == PAS_THREAD_LOCAL_CACHE_DESTROYED. 21 3. We repeatedly calls the destructor of TLS, but every time, we set PAS_THREAD_LOCAL_CACHE_DESTROYED. 22 So after PAS_THREAD_LOCAL_CACHE_DESTROYED times, it is left, and we achieve the goal (1) offering the way to 23 detect the destroyed TLS and (2) avoiding reviving of TLS. 24 25 This patch implements it when pthread_self_is_exiting_np does not exist. 26 27 * libpas/src/libpas/pas_segregated_page_inlines.h: 28 (pas_segregated_page_switch_lock_with_mode): 29 * libpas/src/libpas/pas_thread_local_cache.c: 30 (destructor): 31 (pas_thread_local_cache_create): 32 (pas_thread_local_cache_destroy): 33 (pas_thread_local_cache_get_local_allocator_slow): 34 (pas_thread_local_cache_for_all): 35 * libpas/src/libpas/pas_thread_local_cache.h: 36 (pas_thread_local_cache_try_get_impl): 37 (pas_thread_local_cache_try_get): 38 (pas_thread_local_cache_can_set): 39 (pas_thread_local_cache_set_impl): 40 (pas_thread_local_cache_set): 41 (pas_thread_local_cache_is_guaranteed_to_destruct): Deleted. 42 * libpas/src/libpas/pas_try_reallocate.h: 43 (pas_try_reallocate): 44 * libpas/src/libpas/pas_utils.h: 45 * libpas/src/test/IsoHeapPageSharingTests.cpp: 46 (std::addAllTests): 47 * libpas/src/test/IsoHeapPartialAndBaselineTests.cpp: 48 (addIsoHeapPartialAndBaselineTests): 49 1 50 2021-12-03 Filip Pizlo <fpizlo@apple.com> 2 51 -
trunk/Source/bmalloc/libpas/src/libpas/pas_segregated_page_inlines.h
r286493 r286573 279 279 } } 280 280 PAS_ASSERT(!"Should not be reached"); 281 return true; 281 282 } 282 283 -
trunk/Source/bmalloc/libpas/src/libpas/pas_thread_local_cache.c
r286493 r286573 92 92 static void destructor(void* arg) 93 93 { 94 static const bool verbose = false; 95 94 96 pas_thread_local_cache* thread_local_cache; 95 97 96 98 thread_local_cache = (pas_thread_local_cache*)arg; 97 99 98 destroy(thread_local_cache, pas_lock_is_not_held); 100 #ifndef PAS_THREAD_LOCAL_CACHE_CAN_DETECT_THREAD_EXIT 101 /* If pthread_self_is_exiting_np does not exist, we set PAS_THREAD_LOCAL_CACHE_DESTROYED in the TLS so that 102 subsequent calls of pas_thread_local_cache_try_get() can detect whether TLS is destroyed. Since 103 PAS_THREAD_LOCAL_CACHE_DESTROYED is a non-null value, pthread will call this destructor again (up to 104 PTHREAD_DESTRUCTOR_ITERATIONS times). Each time it does, it will clear the TLS entry. Hence, we need to re-set 105 PAS_THREAD_LOCAL_CACHE_DESTROYED in the TLS each time to continue to indicate that destroy() has already been called once. */ 106 pas_thread_local_cache_set_impl((pas_thread_local_cache*)PAS_THREAD_LOCAL_CACHE_DESTROYED); 107 PAS_ASSERT(!pas_thread_local_cache_can_set()); 108 #endif 109 110 if (((uintptr_t)thread_local_cache) != PAS_THREAD_LOCAL_CACHE_DESTROYED) 111 destroy(thread_local_cache, pas_lock_is_not_held); 112 else { 113 if (verbose) 114 pas_log("[%d] Repeated destructor call for TLS %p\n", getpid(), thread_local_cache); 115 } 99 116 } 100 117 … … 175 192 pas_thread_local_cache_layout_node_construct(layout_node, thread_local_cache); 176 193 177 pas_thread_local_cache_set _impl(thread_local_cache);194 pas_thread_local_cache_set(thread_local_cache); 178 195 179 196 return thread_local_cache; … … 195 212 destroy(thread_local_cache, heap_lock_hold_mode); 196 213 197 pas_thread_local_cache_set _impl(NULL);214 pas_thread_local_cache_set(NULL); 198 215 } 199 216 … … 313 330 314 331 if (thread_local_cache != new_thread_local_cache) 315 pas_thread_local_cache_set _impl(new_thread_local_cache);332 pas_thread_local_cache_set(new_thread_local_cache); 316 333 317 334 PAS_ASSERT(desired_allocator_index < new_thread_local_cache->allocator_index_upper_bound); … … 567 584 } 568 585 569 #if def PAS_THREAD_LOCAL_CACHE_CAN_DETECT_THREAD_EXIT586 #if PAS_OS(DARWIN) 570 587 571 588 static void suspend(pas_thread_local_cache* cache) … … 741 758 } 742 759 743 #ifdef PAS_THREAD_LOCAL_CACHE_CAN_DETECT_THREAD_EXIT 744 if (!pas_thread_local_cache_is_guaranteed_to_destruct()) { 745 /* We're on a platform that can't guarantee that thread local caches are destructed. 746 Therefore, we might have a TLC that has a dangling thread pointer. So, we don't 747 attempt to do the suspend thing. */ 748 continue; 749 } 750 760 #if PAS_OS(DARWIN) 751 761 if (verbose) 752 762 pas_log("Need to suspend for allocator %p\n", scavenger_data); … … 770 780 } 771 781 772 #if def PAS_THREAD_LOCAL_CACHE_CAN_DETECT_THREAD_EXIT782 #if PAS_OS(DARWIN) 773 783 if (did_suspend) 774 784 resume(cache); -
trunk/Source/bmalloc/libpas/src/libpas/pas_thread_local_cache.h
r286493 r286573 45 45 #endif 46 46 47 PAS_BEGIN_EXTERN_C; 48 49 struct pas_magazine; 50 struct pas_thread_local_cache; 51 struct pas_thread_local_cache_node; 52 typedef struct pas_magazine pas_magazine; 53 typedef struct pas_thread_local_cache pas_thread_local_cache; 54 typedef struct pas_thread_local_cache_node pas_thread_local_cache_node; 55 56 struct pas_thread_local_cache { 57 uintptr_t deallocation_log[PAS_DEALLOCATION_LOG_SIZE]; 58 59 unsigned deallocation_log_index; 60 61 /* A dirty allocation log is one that has been flushed by the thread recently. A clean one is 62 one that hasn't been touched by the thread (except maybe by appending more things to it). */ 63 bool deallocation_log_dirty; 64 65 size_t num_logged_bytes; /* This undercounts since when we append small objects we don't 66 touch this. */ 67 68 /* This part of the TLC is allocated separately so that it does not move. */ 69 pas_thread_local_cache_node* node; 70 71 unsigned* should_stop_bitvector; 72 73 pthread_t thread; 74 75 bool should_stop_some; 76 77 unsigned allocator_index_upper_bound; 78 unsigned allocator_index_capacity; 79 uint64_t local_allocators[1]; /* This is variable-length. */ 80 }; 81 82 PAS_API extern pas_fast_tls pas_thread_local_cache_fast_tls; 83 84 #define PAS_THREAD_LOCAL_KEY __PTK_FRAMEWORK_JAVASCRIPTCORE_KEY4 85 86 static inline pas_thread_local_cache* pas_thread_local_cache_try_get(void) 87 { 88 return (pas_thread_local_cache*)PAS_FAST_TLS_GET( 89 PAS_THREAD_LOCAL_KEY, &pas_thread_local_cache_fast_tls); 90 } 47 #define PAS_THREAD_LOCAL_CACHE_DESTROYED 1 91 48 92 49 #if PAS_HAVE_PTHREAD_PRIVATE … … 98 55 #define PAS_THREAD_LOCAL_CACHE_CAN_DETECT_THREAD_EXIT 1 99 56 #endif 100 101 static inline bool pas_thread_local_cache_is_guaranteed_to_destruct(void) 102 { 103 #ifdef PAS_THREAD_LOCAL_CACHE_CAN_DETECT_THREAD_EXIT 104 return true; 105 #else 106 return false; 107 #endif 57 #endif 58 59 PAS_BEGIN_EXTERN_C; 60 61 struct pas_magazine; 62 struct pas_thread_local_cache; 63 struct pas_thread_local_cache_node; 64 typedef struct pas_magazine pas_magazine; 65 typedef struct pas_thread_local_cache pas_thread_local_cache; 66 typedef struct pas_thread_local_cache_node pas_thread_local_cache_node; 67 68 struct pas_thread_local_cache { 69 uintptr_t deallocation_log[PAS_DEALLOCATION_LOG_SIZE]; 70 71 unsigned deallocation_log_index; 72 73 /* A dirty allocation log is one that has been flushed by the thread recently. A clean one is 74 one that hasn't been touched by the thread (except maybe by appending more things to it). */ 75 bool deallocation_log_dirty; 76 77 size_t num_logged_bytes; /* This undercounts since when we append small objects we don't 78 touch this. */ 79 80 /* This part of the TLC is allocated separately so that it does not move. */ 81 pas_thread_local_cache_node* node; 82 83 unsigned* should_stop_bitvector; 84 85 pthread_t thread; 86 87 bool should_stop_some; 88 89 unsigned allocator_index_upper_bound; 90 unsigned allocator_index_capacity; 91 uint64_t local_allocators[1]; /* This is variable-length. */ 92 }; 93 94 PAS_API extern pas_fast_tls pas_thread_local_cache_fast_tls; 95 96 #define PAS_THREAD_LOCAL_KEY __PTK_FRAMEWORK_JAVASCRIPTCORE_KEY4 97 98 static PAS_ALWAYS_INLINE pas_thread_local_cache* pas_thread_local_cache_try_get_impl(void) 99 { 100 return (pas_thread_local_cache*)PAS_FAST_TLS_GET(PAS_THREAD_LOCAL_KEY, &pas_thread_local_cache_fast_tls); 101 } 102 103 static inline pas_thread_local_cache* pas_thread_local_cache_try_get(void) 104 { 105 pas_thread_local_cache* cache = pas_thread_local_cache_try_get_impl(); 106 #ifndef PAS_THREAD_LOCAL_CACHE_CAN_DETECT_THREAD_EXIT 107 if (((uintptr_t)cache) == PAS_THREAD_LOCAL_CACHE_DESTROYED) 108 return NULL; 109 #endif 110 return cache; 108 111 } 109 112 … … 113 116 return !pthread_self_is_exiting_np(); 114 117 #else 115 return true; 116 #endif 117 } 118 #else /* PAS_HAVE_PTHREAD_PRIVATE -> so !PAS_HAVE_PTHREAD_PRIVATE */ 119 static inline bool pas_thread_local_cache_is_guaranteed_to_destruct(void) 120 { 121 return false; 122 } 123 124 static inline bool pas_thread_local_cache_can_set(void) 125 { 126 return true; 127 } 128 #endif /* PAS_HAVE_PTHREAD_PRIVATE -> so end of !PAS_HAVE_PTHREAD_PRIVATE */ 118 return ((uintptr_t)pas_thread_local_cache_try_get_impl()) != PAS_THREAD_LOCAL_CACHE_DESTROYED; 119 #endif 120 } 129 121 130 122 static inline void pas_thread_local_cache_set_impl(pas_thread_local_cache* thread_local_cache) 131 123 { 124 PAS_FAST_TLS_SET(PAS_THREAD_LOCAL_KEY, &pas_thread_local_cache_fast_tls, thread_local_cache); 125 } 126 127 static inline void pas_thread_local_cache_set(pas_thread_local_cache* thread_local_cache) 128 { 132 129 PAS_ASSERT(pas_thread_local_cache_can_set() || pas_thread_local_cache_try_get()); 133 PAS_FAST_TLS_SET(PAS_THREAD_LOCAL_KEY, &pas_thread_local_cache_fast_tls,thread_local_cache);130 pas_thread_local_cache_set_impl(thread_local_cache); 134 131 } 135 132 -
trunk/Source/bmalloc/libpas/src/libpas/pas_try_reallocate.h
r286493 r286573 355 355 356 356 PAS_ASSERT(!"Should never be reached"); 357 return pas_allocation_result_create_failure(); 357 358 } 358 359 -
trunk/Source/bmalloc/libpas/src/libpas/pas_utils.h
r286493 r286573 109 109 PAS_API PAS_NO_RETURN void pas_assertion_failed(const char* filename, int line, const char* function, const char* expression); 110 110 111 #pragma clang diagnostic push 112 #pragma clang diagnostic ignored "-Wmissing-noreturn" 111 PAS_IGNORE_WARNINGS_BEGIN("missing-noreturn") 113 112 static inline void pas_assertion_failed_noreturn_silencer( 114 113 const char* filename, int line, const char* function, const char* expression) … … 116 115 pas_assertion_failed(filename, line, function, expression); 117 116 } 118 #pragma clang diagnostic pop 117 PAS_IGNORE_WARNINGS_END 119 118 120 119 #define PAS_LIKELY(x) __PAS_LIKELY(x) -
trunk/Source/bmalloc/libpas/src/test/IsoHeapPageSharingTests.cpp
r285789 r286573 4520 4520 ADD_TEST(testScavengerEventuallyReturnsMemory(128, 10000)); 4521 4521 ADD_TEST(testScavengerEventuallyReturnsMemory(8, 10000)); 4522 if (pas_thread_local_cache_is_guaranteed_to_destruct()) { 4523 ADD_TEST(testScavengerEventuallyReturnsMemoryEvenWithoutManualShrink(128, 1)); 4524 ADD_TEST(testScavengerEventuallyReturnsMemoryEvenWithoutManualShrink(128, 10000)); 4525 ADD_TEST(testScavengerEventuallyReturnsMemoryEvenWithoutManualShrink(8, 10000)); 4526 } 4522 ADD_TEST(testScavengerEventuallyReturnsMemoryEvenWithoutManualShrink(128, 1)); 4523 ADD_TEST(testScavengerEventuallyReturnsMemoryEvenWithoutManualShrink(128, 10000)); 4524 ADD_TEST(testScavengerEventuallyReturnsMemoryEvenWithoutManualShrink(8, 10000)); 4527 4525 ADD_TEST(testScavengerShutsDownEventually(64, 10000, 1, 1)); 4528 4526 } … … 4531 4529 ADD_TEST(testScavengerEventuallyReturnsMemory(128, 10000)); 4532 4530 ADD_TEST(testScavengerEventuallyReturnsMemory(8, 10000)); 4533 if (pas_thread_local_cache_is_guaranteed_to_destruct()) { 4534 ADD_TEST(testScavengerEventuallyReturnsMemoryEvenWithoutManualShrink(128, 1)); 4535 ADD_TEST(testScavengerEventuallyReturnsMemoryEvenWithoutManualShrink(128, 10000)); 4536 ADD_TEST(testScavengerEventuallyReturnsMemoryEvenWithoutManualShrink(8, 10000)); 4537 } 4531 ADD_TEST(testScavengerEventuallyReturnsMemoryEvenWithoutManualShrink(128, 1)); 4532 ADD_TEST(testScavengerEventuallyReturnsMemoryEvenWithoutManualShrink(128, 10000)); 4533 ADD_TEST(testScavengerEventuallyReturnsMemoryEvenWithoutManualShrink(8, 10000)); 4538 4534 ADD_TEST(testScavengerShutsDownEventually(64, 10000, 1, 1)); 4539 4535 } -
trunk/Source/bmalloc/libpas/src/test/IsoHeapPartialAndBaselineTests.cpp
r285789 r286573 1083 1083 1084 1084 #if PAS_ENABLE_ISO && PAS_ENABLE_ISO_TEST 1085 if (pas_thread_local_cache_is_guaranteed_to_destruct()){1085 { 1086 1086 RunScavengerFully runScavengerFully; 1087 1087 addScavengerDependentTests();
Note:
See TracChangeset
for help on using the changeset viewer.