File: thread_test.cc

package info (click to toggle)
seastar 25.05.0-1
  • links: PTS
  • area: main
  • in suites: sid
  • size: 7,256 kB
  • sloc: cpp: 89,250; python: 5,066; ansic: 3,452; sh: 1,272; xml: 177; makefile: 9
file content (265 lines) | stat: -rw-r--r-- 8,033 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */

/*
 * Copyright (C) 2015 Cloudius Systems, Ltd.
 */

#include <seastar/core/thread.hh>
#include <seastar/core/do_with.hh>
#include <seastar/testing/test_case.hh>
#include <seastar/testing/thread_test_case.hh>
#include <seastar/core/sstring.hh>
#include <seastar/core/semaphore.hh>
#include <seastar/core/do_with.hh>
#include <seastar/core/loop.hh>
#include <seastar/core/sleep.hh>
#include <seastar/util/assert.hh>
#include <sys/mman.h>
#include <signal.h>

#include <valgrind/valgrind.h>

using namespace seastar;
using namespace std::chrono_literals;

SEASTAR_TEST_CASE(test_thread_1) {
    return do_with(sstring(), [] (sstring& x) {
        auto t1 = new thread([&x] {
            x = "abc";
        });
        return t1->join().then([&x, t1] {
            BOOST_REQUIRE_EQUAL(x, "abc");
            delete t1;
        });
    });
}

SEASTAR_TEST_CASE(test_thread_2) {
    struct tmp {
        std::vector<thread> threads;
        semaphore sem1{0};
        semaphore sem2{0};
        int counter = 0;
        void thread_fn() {
            sem1.wait(1).get();
            ++counter;
            sem2.signal(1);
        }
    };
    return do_with(tmp(), [] (tmp& x) {
        auto n = 10;
        for (int i = 0; i < n; ++i) {
            x.threads.emplace_back(std::bind(&tmp::thread_fn, &x));
        }
        BOOST_REQUIRE_EQUAL(x.counter, 0);
        x.sem1.signal(n);
        return x.sem2.wait(n).then([&x, n] {
            BOOST_REQUIRE_EQUAL(x.counter, n);
            return parallel_for_each(x.threads.begin(), x.threads.end(), std::mem_fn(&thread::join));
        });
    });
}

SEASTAR_TEST_CASE(test_thread_async) {
    sstring x = "x";
    sstring y = "y";
    auto concat = [] (sstring x, sstring y) {
        sleep(10ms).get();
        return x + y;
    };
    return async(concat, x, y).then([] (sstring xy) {
        BOOST_REQUIRE_EQUAL(xy, "xy");
    });
}

SEASTAR_TEST_CASE(test_thread_async_immed) {
    return async([] { return 3; }).then([] (int three) {
        BOOST_REQUIRE_EQUAL(three, 3);
    });
}

SEASTAR_TEST_CASE(test_thread_async_nested) {
    return async([] {
        return async([] {
            return 3;
        }).get();
    }).then([] (int three) {
        BOOST_REQUIRE_EQUAL(three, 3);
    });
}

void compute(float& result, bool& done, uint64_t& ctr) {
    while (!done) {
        for (int n = 0; n < 10000; ++n) {
            result += 1 / (result + 1);
            ++ctr;
        }
        thread::yield();
    }
}

#if defined(SEASTAR_ASAN_ENABLED) && defined(SEASTAR_HAVE_ASAN_FIBER_SUPPORT)
volatile int force_write;
volatile void* shut_up_gcc;

[[gnu::noinline]]
void throw_exception() {
    volatile char buf[1024];
    shut_up_gcc = &buf;
    for (int i = 0; i < 1024; i++) {
        buf[i] = force_write;
    }
    throw 1;
}

[[gnu::noinline]]
void use_stack() {
    volatile char buf[2 * 1024];
    shut_up_gcc = &buf;
    for (int i = 0; i < 2 * 1024; i++) {
        buf[i] = force_write;
    }
}

SEASTAR_TEST_CASE(test_asan_false_positive) {
    return async([] {
        try {
            throw_exception();
        } catch (...) {
            use_stack();
        }
    });
}
#endif

SEASTAR_THREAD_TEST_CASE(abc, *boost::unit_test::expected_failures(2)) {
    BOOST_TEST(false);
    BOOST_TEST(false);
}

SEASTAR_TEST_CASE(test_thread_custom_stack_size) {
    sstring x = "x";
    sstring y = "y";
    auto concat = [] (sstring x, sstring y) {
        sleep(10ms).get();
        return x + y;
    };
    thread_attributes attr;
    attr.stack_size = 16384;
    return async(attr, concat, x, y).then([] (sstring xy) {
        BOOST_REQUIRE_EQUAL(xy, "xy");
    });
}

// The test case uses x86_64 specific signal handler info. The test
// fails with detect_stack_use_after_return=1. We could put it behind
// a command line option and fork/exec to run it after removing
// detect_stack_use_after_return=1 from the environment.
#if defined(SEASTAR_THREAD_STACK_GUARDS) && defined(__x86_64__) && !defined(SEASTAR_ASAN_ENABLED)
struct test_thread_custom_stack_size_failure : public seastar::testing::seastar_test {
    using seastar::testing::seastar_test::seastar_test;
    seastar::future<> run_test_case() const override;
};

static test_thread_custom_stack_size_failure test_thread_custom_stack_size_failure_instance(
    "test_thread_custom_stack_size_failure",
    __FILE__, __LINE__);
static thread_local volatile bool stack_guard_bypassed = false;

static int get_mprotect_flags(void* ctx) {
    int flags;
    ucontext_t* context = reinterpret_cast<ucontext_t*>(ctx);
    if (context->uc_mcontext.gregs[REG_ERR] & 0x2) {
        flags = PROT_READ | PROT_WRITE;
    } else {
        flags = PROT_READ;
    }
    return flags;
}

static void* pagealign(void* ptr, size_t page_size) {
    static const int pageshift = ffs(page_size) - 1;
    return reinterpret_cast<void*>(((reinterpret_cast<intptr_t>((ptr)) >> pageshift) << pageshift));
}

static thread_local struct sigaction default_old_sigsegv_handler;

static void bypass_stack_guard(int sig, siginfo_t* si, void* ctx) {
    SEASTAR_ASSERT(sig == SIGSEGV);
    int flags = get_mprotect_flags(ctx);
    stack_guard_bypassed = (flags & PROT_WRITE);
    if (!stack_guard_bypassed) {
        return;
    }
    size_t page_size = getpagesize();
    auto mp_result = mprotect(pagealign(si->si_addr, page_size), page_size, PROT_READ | PROT_WRITE);
    SEASTAR_ASSERT(mp_result == 0);
}

// This test will fail with a regular stack size, because we only probe
// around 10KiB of data, and the stack guard resides after 128'th KiB.
seastar::future<> test_thread_custom_stack_size_failure::run_test_case() const {
    if (RUNNING_ON_VALGRIND) {
        return make_ready_future<>();
    }

    sstring x = "x";
    sstring y = "y";

    // Catch segmentation fault once:
    struct sigaction sa{};
    sa.sa_sigaction = &bypass_stack_guard;
    sa.sa_flags = SA_SIGINFO;
    auto ret = sigaction(SIGSEGV, &sa, &default_old_sigsegv_handler);
    if (ret) {
        throw std::system_error(ret, std::system_category());
    }

    auto concat = [] (sstring x, sstring y) {
        sleep(10ms).get();
        // Probe the stack by writing to it in intervals of 1024,
        // until we hit a write fault. In order not to ruin anything,
        // the "write" uses data it just read from the address.
        volatile char* mem = reinterpret_cast<volatile char*>(&x);
        for (int i = 0; i < 20; ++i) {
            mem[i*-1024] = char(mem[i*-1024]);
            if (stack_guard_bypassed) {
                break;
            }
        }
        return x + y;
    };
    thread_attributes attr;
    attr.stack_size = 16384;
    return async(attr, concat, x, y).then([] (sstring xy) {
        BOOST_REQUIRE_EQUAL(xy, "xy");
        BOOST_REQUIRE(stack_guard_bypassed);
        auto ret = sigaction(SIGSEGV, &default_old_sigsegv_handler, nullptr);
        if (ret) {
            throw std::system_error(ret, std::system_category());
        }
    }).then([concat, x, y] {
        // The same function with a default stack will not trigger
        // a segfault, because its stack is much bigger than 10KiB
        return async(concat, x, y).then([] (sstring xy) {
            BOOST_REQUIRE_EQUAL(xy, "xy");
        });
    });
}
#endif // SEASTAR_THREAD_STACK_GUARDS && __x86_64__