stack.cpp 5.98 KB
Newer Older
gejun's avatar
gejun committed
1
// bthread - A M:N threading library to make applications more concurrent.
gejun's avatar
gejun committed
2
// Copyright (c) 2014 Baidu, Inc.
gejun's avatar
gejun committed
3 4 5 6 7 8 9 10 11 12 13 14
// 
// Licensed 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.
gejun's avatar
gejun committed
15 16 17 18 19 20 21 22

// Author: Ge,Jun (gejun@baidu.com)
// Date: Sun Sep  7 22:37:39 CST 2014

#include <unistd.h>                               // getpagesize
#include <sys/mman.h>                             // mmap, munmap, mprotect
#include <algorithm>                              // std::max
#include <stdlib.h>                               // posix_memalign
23 24 25 26
#include "butil/macros.h"                          // BAIDU_CASSERT
#include "butil/memory/singleton_on_pthread_once.h"
#include "butil/third_party/dynamic_annotations/dynamic_annotations.h" // RunningOnValgrind
#include "butil/third_party/valgrind/valgrind.h"   // VALGRIND_STACK_REGISTER
gejun's avatar
gejun committed
27
#include "bvar/passive_status.h"
gejun's avatar
gejun committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
#include "bthread/types.h"                        // BTHREAD_STACKTYPE_*
#include "bthread/stack.h"

DEFINE_int32(stack_size_small, 32768, "size of small stacks");
DEFINE_int32(stack_size_normal, 1048576, "size of normal stacks");
DEFINE_int32(stack_size_large, 8388608, "size of large stacks");
DEFINE_int32(guard_page_size, 4096, "size of guard page, allocate stacks by malloc if it's 0(not recommended)");
DEFINE_int32(tc_stack_small, 32, "maximum small stacks cached by each thread");
DEFINE_int32(tc_stack_normal, 8, "maximum normal stacks cached by each thread");

namespace bthread {

BAIDU_CASSERT(BTHREAD_STACKTYPE_PTHREAD == STACK_TYPE_PTHREAD, must_match);
BAIDU_CASSERT(BTHREAD_STACKTYPE_SMALL == STACK_TYPE_SMALL, must_match);
BAIDU_CASSERT(BTHREAD_STACKTYPE_NORMAL == STACK_TYPE_NORMAL, must_match);
BAIDU_CASSERT(BTHREAD_STACKTYPE_LARGE == STACK_TYPE_LARGE, must_match);
BAIDU_CASSERT(STACK_TYPE_MAIN == 0, must_be_0);

46
static butil::static_atomic<int64_t> s_stack_count = BUTIL_STATIC_ATOMIC_INIT(0);
gejun's avatar
gejun committed
47
static int64_t get_stack_count(void*) {
48
    return s_stack_count.load(butil::memory_order_relaxed);
gejun's avatar
gejun committed
49
}
gejun's avatar
gejun committed
50 51 52 53 54 55 56 57 58 59 60 61
static bvar::PassiveStatus<int64_t> bvar_stack_count(
    "bthread_stack_count", get_stack_count, NULL);

int allocate_stack_storage(StackStorage* s, int stacksize_in, int guardsize_in) {
    const static int PAGESIZE = getpagesize();
    const int PAGESIZE_M1 = PAGESIZE - 1;
    const int MIN_STACKSIZE = PAGESIZE * 2;
    const int MIN_GUARDSIZE = PAGESIZE;

    // Align stacksize
    const int stacksize =
        (std::max(stacksize_in, MIN_STACKSIZE) + PAGESIZE_M1) &
gejun's avatar
gejun committed
62 63
        ~PAGESIZE_M1;

gejun's avatar
gejun committed
64
    if (guardsize_in <= 0) {
gejun's avatar
gejun committed
65 66
        void* mem = malloc(stacksize);
        if (NULL == mem) {
gejun's avatar
gejun committed
67 68 69
            PLOG_EVERY_SECOND(ERROR) << "Fail to malloc (size="
                                     << stacksize << ")";
            return -1;
gejun's avatar
gejun committed
70
        }
71
        s_stack_count.fetch_add(1, butil::memory_order_relaxed);
gejun's avatar
gejun committed
72 73 74 75 76 77 78 79 80 81
        s->bottom = (char*)mem + stacksize;
        s->stacksize = stacksize;
        s->guardsize = 0;
        if (RunningOnValgrind()) {
            s->valgrind_stack_id = VALGRIND_STACK_REGISTER(
                s->bottom, (char*)s->bottom - stacksize);
        } else {
            s->valgrind_stack_id = 0;
        }
        return 0;
gejun's avatar
gejun committed
82
    } else {
gejun's avatar
gejun committed
83 84 85 86 87
        // Align guardsize
        const int guardsize =
            (std::max(guardsize_in, MIN_GUARDSIZE) + PAGESIZE_M1) &
            ~PAGESIZE_M1;

gejun's avatar
gejun committed
88 89 90 91 92 93
        const int memsize = stacksize + guardsize;
        void* const mem = mmap(NULL, memsize, (PROT_READ | PROT_WRITE),
                               (MAP_PRIVATE | MAP_ANONYMOUS), -1, 0);

        if (MAP_FAILED == mem) {
            PLOG_EVERY_SECOND(ERROR) 
gejun's avatar
gejun committed
94
                << "Fail to mmap size=" << memsize << " stack_count="
95
                << s_stack_count.load(butil::memory_order_relaxed)
gejun's avatar
gejun committed
96
                << ", possibly limited by /proc/sys/vm/max_map_count";
gejun's avatar
gejun committed
97
            // may fail due to limit of max_map_count (65536 in default)
gejun's avatar
gejun committed
98
            return -1;
gejun's avatar
gejun committed
99 100
        }

gejun's avatar
gejun committed
101 102 103 104 105 106
        void* aligned_mem = (void*)(((intptr_t)mem + PAGESIZE_M1) & ~PAGESIZE_M1);
        if (aligned_mem != mem) {
            LOG_ONCE(ERROR) << "addr=" << mem << " returned by mmap is not "
                "aligned by pagesize=" << PAGESIZE;
        }
        const int offset = (char*)aligned_mem - (char*)mem;
gejun's avatar
gejun committed
107 108 109
        if (guardsize <= offset ||
            mprotect(aligned_mem, guardsize - offset, PROT_NONE) != 0) {
            munmap(mem, memsize);
gejun's avatar
gejun committed
110 111 112 113 114 115
            PLOG_EVERY_SECOND(ERROR) 
                << "Fail to mprotect " << (void*)aligned_mem << " length="
                << guardsize - offset; 
            return -1;
        }

116
        s_stack_count.fetch_add(1, butil::memory_order_relaxed);
gejun's avatar
gejun committed
117 118 119 120 121 122 123 124
        s->bottom = (char*)mem + memsize;
        s->stacksize = stacksize;
        s->guardsize = guardsize;
        if (RunningOnValgrind()) {
            s->valgrind_stack_id = VALGRIND_STACK_REGISTER(
                s->bottom, (char*)s->bottom - stacksize);
        } else {
            s->valgrind_stack_id = 0;
gejun's avatar
gejun committed
125
        }
gejun's avatar
gejun committed
126
        return 0;
gejun's avatar
gejun committed
127 128 129
    }
}

gejun's avatar
gejun committed
130 131 132 133 134
void deallocate_stack_storage(StackStorage* s) {
    if (RunningOnValgrind()) {
        VALGRIND_STACK_DEREGISTER(s->valgrind_stack_id);
    }
    const int memsize = s->stacksize + s->guardsize;
135
    if ((uintptr_t)s->bottom <= (uintptr_t)memsize) {
gejun's avatar
gejun committed
136 137
        return;
    }
138
    s_stack_count.fetch_sub(1, butil::memory_order_relaxed);
gejun's avatar
gejun committed
139 140
    if (s->guardsize <= 0) {
        free((char*)s->bottom - memsize);
gejun's avatar
gejun committed
141
    } else {
gejun's avatar
gejun committed
142
        munmap((char*)s->bottom - memsize, memsize);
gejun's avatar
gejun committed
143 144 145 146 147 148 149 150
    }
}

int* SmallStackClass::stack_size_flag = &FLAGS_stack_size_small;
int* NormalStackClass::stack_size_flag = &FLAGS_stack_size_normal;
int* LargeStackClass::stack_size_flag = &FLAGS_stack_size_large;

}  // namespace bthread