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