src/bthread/stack_inl.h (211 lines of code) (raw):

// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you 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. // bthread - An M:N threading library to make applications more concurrent. // Date: Sun Sep 7 22:37:39 CST 2014 #ifndef BTHREAD_ALLOCATE_STACK_INL_H #define BTHREAD_ALLOCATE_STACK_INL_H DECLARE_int32(guard_page_size); DECLARE_int32(tc_stack_small); DECLARE_int32(tc_stack_normal); namespace bthread { #ifdef BUTIL_USE_ASAN namespace internal { BUTIL_FORCE_INLINE void ASanPoisonMemoryRegion(const StackStorage& storage) { if (NULL == storage.bottom) { return; } CHECK_GT((void*)storage.bottom, reinterpret_cast<void*>(storage.stacksize + + storage.guardsize)); BUTIL_ASAN_POISON_MEMORY_REGION( (char*)storage.bottom - storage.stacksize, storage.stacksize); } BUTIL_FORCE_INLINE void ASanUnpoisonMemoryRegion(const StackStorage& storage) { if (NULL == storage.bottom) { return; } CHECK_GT(storage.bottom, reinterpret_cast<void*>(storage.stacksize + storage.guardsize)); BUTIL_ASAN_UNPOISON_MEMORY_REGION( (char*)storage.bottom - storage.stacksize, storage.stacksize); } BUTIL_FORCE_INLINE void StartSwitchFiber(void** fake_stack_save, StackStorage& storage) { if (NULL == storage.bottom) { return; } RELEASE_ASSERT(storage.bottom > reinterpret_cast<void*>(storage.stacksize + storage.guardsize)); // Lowest address of this stack. void* asan_stack_bottom = (char*)storage.bottom - storage.stacksize; BUTIL_ASAN_START_SWITCH_FIBER(fake_stack_save, asan_stack_bottom, storage.stacksize); } BUTIL_FORCE_INLINE void FinishSwitchFiber(void* fake_stack_save) { BUTIL_ASAN_FINISH_SWITCH_FIBER(fake_stack_save, NULL, NULL); } class ScopedASanFiberSwitcher { public: ScopedASanFiberSwitcher(StackStorage& next_storage) { StartSwitchFiber(&_fake_stack, next_storage); } ~ScopedASanFiberSwitcher() { FinishSwitchFiber(_fake_stack); } DISALLOW_COPY_AND_ASSIGN(ScopedASanFiberSwitcher); private: void* _fake_stack{NULL}; }; #define BTHREAD_ASAN_POISON_MEMORY_REGION(storage) \ ::bthread::internal::ASanPoisonMemoryRegion(storage) #define BTHREAD_ASAN_UNPOISON_MEMORY_REGION(storage) \ ::bthread::internal::ASanUnpoisonMemoryRegion(storage) #define BTHREAD_SCOPED_ASAN_FIBER_SWITCHER(storage) \ ::bthread::internal::ScopedASanFiberSwitcher switcher(storage) } // namespace internal #else // If ASan are used, the annotations should be no-ops. #define BTHREAD_ASAN_POISON_MEMORY_REGION(storage) ((void)(storage)) #define BTHREAD_ASAN_UNPOISON_MEMORY_REGION(storage) ((void)(storage)) #define BTHREAD_SCOPED_ASAN_FIBER_SWITCHER(storage) ((void)(storage)) #endif // BUTIL_USE_ASAN struct MainStackClass {}; struct SmallStackClass { static int* stack_size_flag; // Older gcc does not allow static const enum, use int instead. static const int stacktype = (int)STACK_TYPE_SMALL; }; struct NormalStackClass { static int* stack_size_flag; static const int stacktype = (int)STACK_TYPE_NORMAL; }; struct LargeStackClass { static int* stack_size_flag; static const int stacktype = (int)STACK_TYPE_LARGE; }; template <typename StackClass> struct StackFactory { struct Wrapper : public ContextualStack { explicit Wrapper(void (*entry)(intptr_t)) { if (allocate_stack_storage(&storage, *StackClass::stack_size_flag, FLAGS_guard_page_size) != 0) { storage.zeroize(); context = NULL; return; } context = bthread_make_fcontext(storage.bottom, storage.stacksize, entry); stacktype = (StackType)StackClass::stacktype; // It's poisoned prior to use. BTHREAD_ASAN_POISON_MEMORY_REGION(storage); } ~Wrapper() { if (context) { context = NULL; // Unpoison to avoid affecting other allocator. BTHREAD_ASAN_UNPOISON_MEMORY_REGION(storage); deallocate_stack_storage(&storage); storage.zeroize(); } } }; static ContextualStack* get_stack(void (*entry)(intptr_t)) { ContextualStack* cs = butil::get_object<Wrapper>(entry); // Marks stack as addressable. BTHREAD_ASAN_UNPOISON_MEMORY_REGION(cs->storage); return cs; } static void return_stack(ContextualStack* cs) { // Marks stack as unaddressable. BTHREAD_ASAN_POISON_MEMORY_REGION(cs->storage); butil::return_object(static_cast<Wrapper*>(cs)); } }; template <> struct StackFactory<MainStackClass> { static ContextualStack* get_stack(void (*)(intptr_t)) { ContextualStack* s = new (std::nothrow) ContextualStack; if (NULL == s) { return NULL; } s->context = NULL; s->stacktype = STACK_TYPE_MAIN; s->storage.zeroize(); return s; } static void return_stack(ContextualStack* s) { delete s; } }; inline ContextualStack* get_stack(StackType type, void (*entry)(intptr_t)) { switch (type) { case STACK_TYPE_PTHREAD: return NULL; case STACK_TYPE_SMALL: return StackFactory<SmallStackClass>::get_stack(entry); case STACK_TYPE_NORMAL: return StackFactory<NormalStackClass>::get_stack(entry); case STACK_TYPE_LARGE: return StackFactory<LargeStackClass>::get_stack(entry); case STACK_TYPE_MAIN: return StackFactory<MainStackClass>::get_stack(entry); } return NULL; } inline void return_stack(ContextualStack* s) { if (NULL == s) { return; } switch (s->stacktype) { case STACK_TYPE_PTHREAD: assert(false); return; case STACK_TYPE_SMALL: return StackFactory<SmallStackClass>::return_stack(s); case STACK_TYPE_NORMAL: return StackFactory<NormalStackClass>::return_stack(s); case STACK_TYPE_LARGE: return StackFactory<LargeStackClass>::return_stack(s); case STACK_TYPE_MAIN: return StackFactory<MainStackClass>::return_stack(s); } } inline void jump_stack(ContextualStack* from, ContextualStack* to) { bthread_jump_fcontext(&from->context, to->context, 0/*not skip remained*/); } } // namespace bthread namespace butil { template <> struct ObjectPoolBlockMaxItem< bthread::StackFactory<bthread::LargeStackClass>::Wrapper> { static const size_t value = 64; }; template <> struct ObjectPoolBlockMaxItem< bthread::StackFactory<bthread::NormalStackClass>::Wrapper> { static const size_t value = 64; }; template <> struct ObjectPoolBlockMaxItem< bthread::StackFactory<bthread::SmallStackClass>::Wrapper> { static const size_t value = 64; }; template <> struct ObjectPoolFreeChunkMaxItem< bthread::StackFactory<bthread::SmallStackClass>::Wrapper> { inline static size_t value() { return (FLAGS_tc_stack_small <= 0 ? 0 : FLAGS_tc_stack_small); } }; template <> struct ObjectPoolFreeChunkMaxItem< bthread::StackFactory<bthread::NormalStackClass>::Wrapper> { inline static size_t value() { return (FLAGS_tc_stack_normal <= 0 ? 0 : FLAGS_tc_stack_normal); } }; template <> struct ObjectPoolFreeChunkMaxItem< bthread::StackFactory<bthread::LargeStackClass>::Wrapper> { inline static size_t value() { return 1UL; } }; template <> struct ObjectPoolValidator< bthread::StackFactory<bthread::LargeStackClass>::Wrapper> { inline static bool validate( const bthread::StackFactory<bthread::LargeStackClass>::Wrapper* w) { return w->context != NULL; } }; template <> struct ObjectPoolValidator< bthread::StackFactory<bthread::NormalStackClass>::Wrapper> { inline static bool validate( const bthread::StackFactory<bthread::NormalStackClass>::Wrapper* w) { return w->context != NULL; } }; template <> struct ObjectPoolValidator< bthread::StackFactory<bthread::SmallStackClass>::Wrapper> { inline static bool validate( const bthread::StackFactory<bthread::SmallStackClass>::Wrapper* w) { return w->context != NULL; } }; } // namespace butil #endif // BTHREAD_ALLOCATE_STACK_INL_H