<commit_before>/*
 * 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 "thread.hh"
#include "posix.hh"
#include <ucontext.h>
#include <algorithm>

/// \cond internal

namespace seastar {

thread_local jmp_buf_link g_unthreaded_context;
thread_local jmp_buf_link* g_current_context;

thread_context::thread_context(thread_attributes attr, std::function<void ()> func)
        : _attr(std::move(attr))
        , _func(std::move(func)) {
    setup();
    _all_threads.push_front(*this);
}

thread_context::~thread_context() {
#ifdef SEASTAR_THREAD_STACK_GUARDS
    auto mp_result = mprotect(_stack.get(), getpagesize(), PROT_READ | PROT_WRITE);
    assert(mp_result == 0);
#endif
    _all_threads.erase(_all_threads.iterator_to(*this));
}

std::unique_ptr<char[]>
thread_context::make_stack() {
#ifdef SEASTAR_THREAD_STACK_GUARDS
    auto stack = std::unique_ptr<char[]>(new (with_alignment(getpagesize())) char[_stack_size]);
#else
    auto stack = std::make_unique<char[]>(_stack_size);
#endif
#ifdef ASAN_ENABLED
    // Avoid ASAN false positive due to garbage on stack
    std::fill_n(stack.get(), _stack_size, 0);
#endif
    return stack;
}

void
thread_context::setup() {
    // use setcontext() for the initial jump, as it allows us
    // to set up a stack, but continue with longjmp() as it's
    // much faster.
    ucontext_t initial_context;
    auto q = uint64_t(reinterpret_cast<uintptr_t>(this));
    auto main = reinterpret_cast<void (*)()>(&thread_context::s_main);
    auto r = getcontext(&initial_context);
    throw_system_error_on(r == -1);
#ifdef SEASTAR_THREAD_STACK_GUARDS
    size_t page_size = getpagesize();
    assert(align_up(_stack.get(), page_size) == _stack.get());
    assert(_stack_size > page_size * 4 && "Stack guard would take too much portion of the stack");
    auto mp_status = mprotect(_stack.get(), page_size, PROT_READ);
    throw_system_error_on(mp_status != 0, "mprotect");
#endif
    initial_context.uc_stack.ss_sp = _stack.get();
    initial_context.uc_stack.ss_size = _stack_size;
    initial_context.uc_link = nullptr;
    makecontext(&initial_context, main, 2, int(q), int(q >> 32));
    auto prev = g_current_context;
    _context.link = prev;
    _context.thread = this;
    g_current_context = &_context;
#ifdef ASAN_ENABLED
    swapcontext(&prev->context, &initial_context);
#else
    if (setjmp(prev->jmpbuf) == 0) {
        setcontext(&initial_context);
    }
#endif
}

void
thread_context::switch_in() {
    auto prev = g_current_context;
    g_current_context = &_context;
    _context.link = prev;
    if (_attr.scheduling_group) {
        _attr.scheduling_group->account_start();
        _context.yield_at = thread_clock::now() + _attr.scheduling_group->_this_period_remain;
    } else {
        _context.yield_at = {};
    }
#ifdef ASAN_ENABLED
    swapcontext(&prev->context, &_context.context);
#else
    if (setjmp(prev->jmpbuf) == 0) {
        longjmp(_context.jmpbuf, 1);
    }
#endif
}

void
thread_context::switch_out() {
    if (_attr.scheduling_group) {
        _attr.scheduling_group->account_stop();
    }
    g_current_context = _context.link;
#ifdef ASAN_ENABLED
    swapcontext(&_context.context, &g_current_context->context);<commit_msg>core/thread: Make jmp_buf_link::yield_at use the same time point as thread_scheduling_group<commit_after>/*
 * 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 "thread.hh"
#include "posix.hh"
#include <ucontext.h>
#include <algorithm>

/// \cond internal

namespace seastar {

thread_local jmp_buf_link g_unthreaded_context;
thread_local jmp_buf_link* g_current_context;

thread_context::thread_context(thread_attributes attr, std::function<void ()> func)
        : _attr(std::move(attr))
        , _func(std::move(func)) {
    setup();
    _all_threads.push_front(*this);
}

thread_context::~thread_context() {
#ifdef SEASTAR_THREAD_STACK_GUARDS
    auto mp_result = mprotect(_stack.get(), getpagesize(), PROT_READ | PROT_WRITE);
    assert(mp_result == 0);
#endif
    _all_threads.erase(_all_threads.iterator_to(*this));
}

std::unique_ptr<char[]>
thread_context::make_stack() {
#ifdef SEASTAR_THREAD_STACK_GUARDS
    auto stack = std::unique_ptr<char[]>(new (with_alignment(getpagesize())) char[_stack_size]);
#else
    auto stack = std::make_unique<char[]>(_stack_size);
#endif
#ifdef ASAN_ENABLED
    // Avoid ASAN false positive due to garbage on stack
    std::fill_n(stack.get(), _stack_size, 0);
#endif
    return stack;
}

void
thread_context::setup() {
    // use setcontext() for the initial jump, as it allows us
    // to set up a stack, but continue with longjmp() as it's
    // much faster.
    ucontext_t initial_context;
    auto q = uint64_t(reinterpret_cast<uintptr_t>(this));
    auto main = reinterpret_cast<void (*)()>(&thread_context::s_main);
    auto r = getcontext(&initial_context);
    throw_system_error_on(r == -1);
#ifdef SEASTAR_THREAD_STACK_GUARDS
    size_t page_size = getpagesize();
    assert(align_up(_stack.get(), page_size) == _stack.get());
    assert(_stack_size > page_size * 4 && "Stack guard would take too much portion of the stack");
    auto mp_status = mprotect(_stack.get(), page_size, PROT_READ);
    throw_system_error_on(mp_status != 0, "mprotect");
#endif
    initial_context.uc_stack.ss_sp = _stack.get();
    initial_context.uc_stack.ss_size = _stack_size;
    initial_context.uc_link = nullptr;
    makecontext(&initial_context, main, 2, int(q), int(q >> 32));
    auto prev = g_current_context;
    _context.link = prev;
    _context.thread = this;
    g_current_context = &_context;
#ifdef ASAN_ENABLED
    swapcontext(&prev->context, &initial_context);
#else
    if (setjmp(prev->jmpbuf) == 0) {
        setcontext(&initial_context);
    }
#endif
}

void
thread_context::switch_in() {
    auto prev = g_current_context;
    g_current_context = &_context;
    _context.link = prev;
    if (_attr.scheduling_group) {
        _attr.scheduling_group->account_start();
        _context.yield_at = _attr.scheduling_group->_this_run_start + _attr.scheduling_group->_this_period_remain;
    } else {
        _context.yield_at = {};
    }
#ifdef ASAN_ENABLED
    swapcontext(&prev->context, &_context.context);
#else
    if (setjmp(prev->jmpbuf) == 0) {
        longjmp(_context.jmpbuf, 1);
    }
#endif
}

void
thread_context::switch_out() {
    if (_attr.scheduling_group) {
        _attr.scheduling_group->account_stop();
    }
    g_current_context = _context.link;
#ifdef ASAN_ENABLED
    swapcontext(&_context.context, &g_current_context->context);
{'fraction_non_alphanumeric': 0.11784553861534616, 'fraction_numerical': 0.005248687828042989, 'mean_word_length': 4.2273024167210975, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 42, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6667283', 'n_tokens_mistral': 2633, 'n_tokens_neox': 2593, 'n_words': 646}