Coverage Report

Created: 2026-01-09 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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