1
0

stack.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. // bthread - A M:N threading library to make applications more concurrent.
  2. // Copyright (c) 2014 Baidu, Inc.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. // Author: Ge,Jun (gejun@baidu.com)
  16. // Date: Sun Sep 7 22:37:39 CST 2014
  17. #include <unistd.h> // getpagesize
  18. #include <sys/mman.h> // mmap, munmap, mprotect
  19. #include <algorithm> // std::max
  20. #include <stdlib.h> // posix_memalign
  21. #include "butil/macros.h" // BAIDU_CASSERT
  22. #include "butil/memory/singleton_on_pthread_once.h"
  23. #include "butil/third_party/dynamic_annotations/dynamic_annotations.h" // RunningOnValgrind
  24. #include "butil/third_party/valgrind/valgrind.h" // VALGRIND_STACK_REGISTER
  25. #include "bvar/passive_status.h"
  26. #include "bthread/types.h" // BTHREAD_STACKTYPE_*
  27. #include "bthread/stack.h"
  28. DEFINE_int32(stack_size_small, 32768, "size of small stacks");
  29. DEFINE_int32(stack_size_normal, 1048576, "size of normal stacks");
  30. DEFINE_int32(stack_size_large, 8388608, "size of large stacks");
  31. DEFINE_int32(guard_page_size, 4096, "size of guard page, allocate stacks by malloc if it's 0(not recommended)");
  32. DEFINE_int32(tc_stack_small, 32, "maximum small stacks cached by each thread");
  33. DEFINE_int32(tc_stack_normal, 8, "maximum normal stacks cached by each thread");
  34. namespace bthread {
  35. BAIDU_CASSERT(BTHREAD_STACKTYPE_PTHREAD == STACK_TYPE_PTHREAD, must_match);
  36. BAIDU_CASSERT(BTHREAD_STACKTYPE_SMALL == STACK_TYPE_SMALL, must_match);
  37. BAIDU_CASSERT(BTHREAD_STACKTYPE_NORMAL == STACK_TYPE_NORMAL, must_match);
  38. BAIDU_CASSERT(BTHREAD_STACKTYPE_LARGE == STACK_TYPE_LARGE, must_match);
  39. BAIDU_CASSERT(STACK_TYPE_MAIN == 0, must_be_0);
  40. static butil::static_atomic<int64_t> s_stack_count = BUTIL_STATIC_ATOMIC_INIT(0);
  41. static int64_t get_stack_count(void*) {
  42. return s_stack_count.load(butil::memory_order_relaxed);
  43. }
  44. static bvar::PassiveStatus<int64_t> bvar_stack_count(
  45. "bthread_stack_count", get_stack_count, NULL);
  46. int allocate_stack_storage(StackStorage* s, int stacksize_in, int guardsize_in) {
  47. const static int PAGESIZE = getpagesize();
  48. const int PAGESIZE_M1 = PAGESIZE - 1;
  49. const int MIN_STACKSIZE = PAGESIZE * 2;
  50. const int MIN_GUARDSIZE = PAGESIZE;
  51. // Align stacksize
  52. const int stacksize =
  53. (std::max(stacksize_in, MIN_STACKSIZE) + PAGESIZE_M1) &
  54. ~PAGESIZE_M1;
  55. if (guardsize_in <= 0) {
  56. void* mem = malloc(stacksize);
  57. if (NULL == mem) {
  58. PLOG_EVERY_SECOND(ERROR) << "Fail to malloc (size="
  59. << stacksize << ")";
  60. return -1;
  61. }
  62. s_stack_count.fetch_add(1, butil::memory_order_relaxed);
  63. s->bottom = (char*)mem + stacksize;
  64. s->stacksize = stacksize;
  65. s->guardsize = 0;
  66. if (RunningOnValgrind()) {
  67. s->valgrind_stack_id = VALGRIND_STACK_REGISTER(
  68. s->bottom, (char*)s->bottom - stacksize);
  69. } else {
  70. s->valgrind_stack_id = 0;
  71. }
  72. return 0;
  73. } else {
  74. // Align guardsize
  75. const int guardsize =
  76. (std::max(guardsize_in, MIN_GUARDSIZE) + PAGESIZE_M1) &
  77. ~PAGESIZE_M1;
  78. const int memsize = stacksize + guardsize;
  79. void* const mem = mmap(NULL, memsize, (PROT_READ | PROT_WRITE),
  80. (MAP_PRIVATE | MAP_ANONYMOUS), -1, 0);
  81. if (MAP_FAILED == mem) {
  82. PLOG_EVERY_SECOND(ERROR)
  83. << "Fail to mmap size=" << memsize << " stack_count="
  84. << s_stack_count.load(butil::memory_order_relaxed)
  85. << ", possibly limited by /proc/sys/vm/max_map_count";
  86. // may fail due to limit of max_map_count (65536 in default)
  87. return -1;
  88. }
  89. void* aligned_mem = (void*)(((intptr_t)mem + PAGESIZE_M1) & ~PAGESIZE_M1);
  90. if (aligned_mem != mem) {
  91. LOG_ONCE(ERROR) << "addr=" << mem << " returned by mmap is not "
  92. "aligned by pagesize=" << PAGESIZE;
  93. }
  94. const int offset = (char*)aligned_mem - (char*)mem;
  95. if (guardsize <= offset ||
  96. mprotect(aligned_mem, guardsize - offset, PROT_NONE) != 0) {
  97. munmap(mem, memsize);
  98. PLOG_EVERY_SECOND(ERROR)
  99. << "Fail to mprotect " << (void*)aligned_mem << " length="
  100. << guardsize - offset;
  101. return -1;
  102. }
  103. s_stack_count.fetch_add(1, butil::memory_order_relaxed);
  104. s->bottom = (char*)mem + memsize;
  105. s->stacksize = stacksize;
  106. s->guardsize = guardsize;
  107. if (RunningOnValgrind()) {
  108. s->valgrind_stack_id = VALGRIND_STACK_REGISTER(
  109. s->bottom, (char*)s->bottom - stacksize);
  110. } else {
  111. s->valgrind_stack_id = 0;
  112. }
  113. return 0;
  114. }
  115. }
  116. void deallocate_stack_storage(StackStorage* s) {
  117. if (RunningOnValgrind()) {
  118. VALGRIND_STACK_DEREGISTER(s->valgrind_stack_id);
  119. }
  120. const int memsize = s->stacksize + s->guardsize;
  121. if ((char*)s->bottom <= (char*)NULL + memsize) {
  122. return;
  123. }
  124. s_stack_count.fetch_sub(1, butil::memory_order_relaxed);
  125. if (s->guardsize <= 0) {
  126. free((char*)s->bottom - memsize);
  127. } else {
  128. munmap((char*)s->bottom - memsize, memsize);
  129. }
  130. }
  131. int* SmallStackClass::stack_size_flag = &FLAGS_stack_size_small;
  132. int* NormalStackClass::stack_size_flag = &FLAGS_stack_size_normal;
  133. int* LargeStackClass::stack_size_flag = &FLAGS_stack_size_large;
  134. } // namespace bthread