separate stack stuff into its own file, add support for segmented stacks on POSIX+gcc/clang
parent
81feac59d1
commit
8e97f7fdfd
40
README.md
40
README.md
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
OctaSTD is an extension of the C++17 standard library which mainly provides
|
OctaSTD is an extension of the C++17 standard library which mainly provides
|
||||||
ranges (to replace iterators) but also various other utilities like proper
|
ranges (to replace iterators) but also various other utilities like proper
|
||||||
streams, string formatting, concurrency utilities and others. It's meant
|
streams, string formatting, coroutines, concurrency utilities and others. It's
|
||||||
to replace the more poorly designed parts of the C++ standard library to
|
meant to replace the more poorly designed parts of the C++ standard library to
|
||||||
make the language easier and more convenient to use.
|
make the language easier and more convenient to use.
|
||||||
|
|
||||||
It is not feature complete right now, as most things are still being worked on.
|
It is not feature complete right now, as most things are still being worked on.
|
||||||
|
@ -44,11 +44,33 @@ beyond the minimum supported compiler are necessary to use the library.
|
||||||
## Supported operating systems
|
## Supported operating systems
|
||||||
|
|
||||||
Most of OctaSTD is entirely platform independent and relies only on the
|
Most of OctaSTD is entirely platform independent and relies only on the
|
||||||
standard library. Therefore it can be used on any operating system that
|
standard library. Therefore it could in theory be used on any operating
|
||||||
provides the right toolchain.
|
system that provides the right toolchain. However, to make things easier
|
||||||
|
to deal with, it currently assumes either Windows or POSIX environment.
|
||||||
|
Some parts (such as filesystem and context/coroutines) also use platform
|
||||||
|
specific code that assumes these two.
|
||||||
|
|
||||||
There are certain parts (currently the filesystem module) that however do rely
|
OctaSTD is actively supported on Windows (x86 and x86\_64) as well as Linux,
|
||||||
on system specific APIs. These are restricted to POSIX compliant operating
|
FreeBSD and macOS. It should also work on other POSIX systems such as the other
|
||||||
systems and Windows, with testing done on Linux, FreeBSD, macOS and Windows -
|
BSDs or Solaris - if it doesn't, please report your problem or better, send
|
||||||
they should work on other POSIX compliant operating systems as well, and
|
patches.
|
||||||
potential patches are welcome.
|
|
||||||
|
### Coroutine platform support
|
||||||
|
|
||||||
|
Coroutines work on POSIX and Windows systems. Context switching is done with
|
||||||
|
platform specific assembly taken from Boost.Context, the provided assembly
|
||||||
|
code should be enough for all supported platforms, but you need to compile
|
||||||
|
the correct ones.
|
||||||
|
|
||||||
|
There is also support for stack allocators inspired again by the Boost.Context
|
||||||
|
library, with fixed size protected and unprotected allocators available on all
|
||||||
|
platforms and segmented stacks available on POSIX platforms with GCC and Clang.
|
||||||
|
In order to use segmented stacks, there are 2 things you have to do:
|
||||||
|
|
||||||
|
* Enable `OSTD_USE_SEGMENTED_STACKS`
|
||||||
|
* Build with `-fsplit-stack -static-libgcc`
|
||||||
|
|
||||||
|
Segmented stacks are used by default when enabled, otherwise unprotected fixed
|
||||||
|
size stacks are used (on Windows the latter is always used by default).
|
||||||
|
|
||||||
|
There is also Valgrind support, enabled with `OSTD_USE_VALGRIND`.
|
|
@ -0,0 +1,303 @@
|
||||||
|
/* Stack allocation for coroutine contexts.
|
||||||
|
* API more or less compatible with the Boost.Context library.
|
||||||
|
*
|
||||||
|
* This file is part of OctaSTD. See COPYING.md for futher information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OSTD_CONTEXT_STACK_HH
|
||||||
|
#define OSTD_CONTEXT_STACK_HH
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <exception>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "ostd/types.hh"
|
||||||
|
#include "ostd/platform.hh"
|
||||||
|
#include "ostd/internal/win32.hh"
|
||||||
|
|
||||||
|
#if !defined(OSTD_PLATFORM_POSIX) && !defined(OSTD_PLATFORM_WIN32)
|
||||||
|
# error "Unsupported platform"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* we can do this as we only support clang 4+ and gcc 7+
|
||||||
|
* which always have support for segmented stacks
|
||||||
|
*/
|
||||||
|
#ifdef OSTD_USE_SEGMENTED_STACKS
|
||||||
|
# if !defined(OSTD_PLATFORM_POSIX) || \
|
||||||
|
(!defined(OSTD_TOOLCHAIN_GNU) && !defined(OSTD__TOOLCHAIN_CLANG))
|
||||||
|
# error "compiler/toolchain does not support segmented_stack stacks"
|
||||||
|
# endif
|
||||||
|
# define OSTD_CONTEXT_SEGMENTS 10
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef OSTD_PLATFORM_POSIX
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <sys/mman.h>
|
||||||
|
# include <sys/resource.h>
|
||||||
|
# include <sys/time.h>
|
||||||
|
# include <signal.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef OSTD_USE_VALGRIND
|
||||||
|
# include <valgrind/valgrind.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ostd {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
#ifdef OSTD_PLATFORM_POSIX
|
||||||
|
# if defined(MAP_ANON) || defined(MAP_ANONYMOUS)
|
||||||
|
constexpr bool CONTEXT_USE_MMAP = true;
|
||||||
|
# ifdef MAP_ANON
|
||||||
|
constexpr auto CONTEXT_MAP_ANON = MAP_ANON;
|
||||||
|
# else
|
||||||
|
constexpr auto CONTEXT_MAP_ANON = MAP_ANONYMOUS;
|
||||||
|
# endif
|
||||||
|
# else
|
||||||
|
constexpr bool CONTEXT_USE_MMAP = false;
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} /* namespace detail */
|
||||||
|
|
||||||
|
struct stack_context {
|
||||||
|
#ifdef OSTD_USE_SEGMENTED_STACKS
|
||||||
|
using segments_context = void *[OSTD_CONTEXT_SEGMENTS];
|
||||||
|
#endif
|
||||||
|
void *ptr = nullptr;
|
||||||
|
size_t size = 0;
|
||||||
|
#ifdef OSTD_USE_SEGMENTED_STACKS
|
||||||
|
segments_context segments_ctx = {};
|
||||||
|
#endif
|
||||||
|
#ifdef OSTD_USE_VALGRIND
|
||||||
|
int valgrind_id = 0;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
inline void ctx_pagesize(size_t *s) noexcept {
|
||||||
|
#if defined(OSTD_PLATFORM_WIN32)
|
||||||
|
SYSTEM_INFO si;
|
||||||
|
GetSystemInfo(&si);
|
||||||
|
*s = size_t(si.dwPageSize);
|
||||||
|
#elif defined(OSTD_PLATFORM_POSIX)
|
||||||
|
*s = size_t(sysconf(_SC_PAGESIZE));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef OSTD_PLATFORM_POSIX
|
||||||
|
inline void ctx_rlimit_get(rlimit *l) noexcept {
|
||||||
|
getrlimit(RLIMIT_STACK, l);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline rlimit ctx_rlimit() noexcept {
|
||||||
|
static rlimit l;
|
||||||
|
static std::once_flag fl;
|
||||||
|
std::call_once(fl, ctx_rlimit_get, &l);
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stack_traits {
|
||||||
|
static bool is_unbounded() noexcept {
|
||||||
|
#if defined(OSTD_PLATFORM_WIN32)
|
||||||
|
return true;
|
||||||
|
#elif defined(OSTD_PLATFORM_POSIX)
|
||||||
|
return detail::ctx_rlimit().rlim_max == RLIM_INFINITY;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t page_size() noexcept {
|
||||||
|
static size_t size = 0;
|
||||||
|
static std::once_flag fl;
|
||||||
|
std::call_once(fl, detail::ctx_pagesize, &size);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t minimum_size() noexcept {
|
||||||
|
#if defined(OSTD_PLATFORM_WIN32)
|
||||||
|
/* no func on windows, sane default */
|
||||||
|
return sizeof(void *) * 1024;
|
||||||
|
#elif defined(OSTD_PLATFORM_POSIX)
|
||||||
|
return SIGSTKSZ;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t maximum_size() noexcept {
|
||||||
|
#if defined(OSTD_PLATFORM_WIN32)
|
||||||
|
/* no func on windows either */
|
||||||
|
return 1024 * 1024 * 1024;
|
||||||
|
#elif defined(OSTD_PLATFORM_POSIX)
|
||||||
|
return size_t(detail::ctx_rlimit().rlim_max);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t default_size() noexcept {
|
||||||
|
#if defined(OSTD_PLATFORM_WIN32)
|
||||||
|
/* no func on windows either */
|
||||||
|
return sizeof(void *) * minimum_size();
|
||||||
|
#elif defined(OSTD_PLATFORM_POSIX)
|
||||||
|
size_t r = sizeof(void *) * minimum_size();
|
||||||
|
if (is_unbounded()) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
size_t m = maximum_size();
|
||||||
|
if (r > m) {
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename TR, bool Protected>
|
||||||
|
struct basic_fixedsize_stack {
|
||||||
|
using traits_type = TR;
|
||||||
|
|
||||||
|
basic_fixedsize_stack(size_t ss = 0) noexcept:
|
||||||
|
p_size(ss ? ss : TR::default_size())
|
||||||
|
{}
|
||||||
|
|
||||||
|
stack_context allocate() {
|
||||||
|
size_t ss = p_size;
|
||||||
|
|
||||||
|
ss = std::clamp(ss, TR::minimum_size(), TR::maximum_size());
|
||||||
|
size_t pgs = TR::page_size();
|
||||||
|
size_t npg = std::max(ss / pgs, size_t(size_t(Protected) + 1));
|
||||||
|
size_t asize = npg * pgs;
|
||||||
|
|
||||||
|
#if defined(OSTD_PLATFORM_WIN32)
|
||||||
|
void *p = VirtualAlloc(0, asize, MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
if (!p) {
|
||||||
|
throw std::bad_alloc{}
|
||||||
|
}
|
||||||
|
DWORD oo;
|
||||||
|
if constexpr(Protected) {
|
||||||
|
VirtualProtect(p, pgs, PAGE_READWRITE | PAGE_GUARD, &oo);
|
||||||
|
}
|
||||||
|
#elif defined(OSTD_PLATFORM_POSIX)
|
||||||
|
void *p = nullptr;
|
||||||
|
if constexpr(detail::CONTEXT_USE_MMAP) {
|
||||||
|
void *mp = mmap(
|
||||||
|
0, asize, PROT_READ | PROT_WRITE,
|
||||||
|
MAP_PRIVATE | detail::CONTEXT_MAP_ANON, -1, 0
|
||||||
|
);
|
||||||
|
if (mp != MAP_FAILED) {
|
||||||
|
p = mp;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p = std::malloc(asize);
|
||||||
|
}
|
||||||
|
if (!p) {
|
||||||
|
throw std::bad_alloc{};
|
||||||
|
}
|
||||||
|
if constexpr(Protected) {
|
||||||
|
mprotect(p, pgs, PROT_NONE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
stack_context ret{static_cast<byte *>(p) + ss, ss};
|
||||||
|
|
||||||
|
#ifdef OSTD_USE_VALGRIND
|
||||||
|
ret.valgrind_id = VALGRIND_STACK_REGISTER(ret.ptr, p);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(stack_context &st) noexcept {
|
||||||
|
if (!st.ptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef OSTD_USE_VALGRIND
|
||||||
|
VALGRIND_STACK_DEREGISTER(st.valgrind_id);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto p = static_cast<byte *>(st.ptr) - st.size;
|
||||||
|
#if defined(OSTD_PLATFORM_WIN32)
|
||||||
|
VirtualFree(p, 0, MEM_RELEASE);
|
||||||
|
#elif defined(OSTD_PLATFORM_POSIX)
|
||||||
|
if constexpr(detail::CONTEXT_USE_MMAP) {
|
||||||
|
munmap(p, st.size);
|
||||||
|
} else {
|
||||||
|
std::free(p);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
st.ptr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t p_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
using fixedsize_stack = basic_fixedsize_stack<stack_traits, false>;
|
||||||
|
using protected_fixedsize_stack = basic_fixedsize_stack<stack_traits, true>;
|
||||||
|
|
||||||
|
#ifdef OSTD_USE_SEGMENTED_STACKS
|
||||||
|
namespace detail {
|
||||||
|
extern "C" {
|
||||||
|
/* from libgcc */
|
||||||
|
void *__splitstack_makecontext(
|
||||||
|
size_t st_size, void *ctx[OSTD_CONTEXT_SEGMENTS], size_t *size
|
||||||
|
);
|
||||||
|
void __splitstack_releasecontext(void *ctx[OSTD_CONTEXT_SEGMENTS]);
|
||||||
|
void __splitstack_resetcontext(void *ctx[OSTD_CONTEXT_SEGMENTS]);
|
||||||
|
void __splitstack_block_signals_context(
|
||||||
|
void *ctx[OSTD_CONTEXT_SEGMENTS], int *new_val, int *old_val
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TR>
|
||||||
|
struct basic_segmented_stack {
|
||||||
|
using traits_type = TR;
|
||||||
|
|
||||||
|
basic_segmented_stack(size_t ss = 0) noexcept:
|
||||||
|
p_size(ss ? ss : TR::default_size())
|
||||||
|
{}
|
||||||
|
|
||||||
|
stack_context allocate() {
|
||||||
|
size_t ss = p_size;
|
||||||
|
|
||||||
|
stack_context ret;
|
||||||
|
void *p = detail::__splitstack_makecontext(
|
||||||
|
ss, ret.segments_ctx, &ret.size
|
||||||
|
);
|
||||||
|
if (!p) {
|
||||||
|
throw std::bad_alloc{};
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.ptr = static_cast<byte *>(p) + ret.size;
|
||||||
|
|
||||||
|
int off = 0;
|
||||||
|
detail::__splitstack_block_signals_context(ret.segments_ctx, &off, 0);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(stack_context &st) noexcept {
|
||||||
|
detail::__splitstack_releasecontext(st.segments_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t p_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
using segmented_stack = basic_segmented_stack<stack_traits>;
|
||||||
|
#endif /* OSTD_USE_SEGMENTED_STACKS */
|
||||||
|
|
||||||
|
#ifdef OSTD_USE_SEGMENTED_STACKS
|
||||||
|
using default_stack = segmented_stack;
|
||||||
|
#else
|
||||||
|
using default_stack = fixedsize_stack;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} /* namespace ostd */
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,4 +1,4 @@
|
||||||
/* Context switching and stack allocation.
|
/* Context switching for coroutines.
|
||||||
*
|
*
|
||||||
* This file is part of OctaSTD. See COPYING.md for futher information.
|
* This file is part of OctaSTD. See COPYING.md for futher information.
|
||||||
*/
|
*/
|
||||||
|
@ -6,37 +6,14 @@
|
||||||
#ifndef OSTD_INTERNAL_CONTEXT_HH
|
#ifndef OSTD_INTERNAL_CONTEXT_HH
|
||||||
#define OSTD_INTERNAL_CONTEXT_HH
|
#define OSTD_INTERNAL_CONTEXT_HH
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <memory>
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <algorithm>
|
#include <utility>
|
||||||
|
|
||||||
#include "ostd/types.hh"
|
#include "ostd/types.hh"
|
||||||
#include "ostd/platform.hh"
|
#include "ostd/platform.hh"
|
||||||
#include "ostd/internal/win32.hh"
|
#include "ostd/context_stack.hh"
|
||||||
|
|
||||||
#ifdef OSTD_PLATFORM_POSIX
|
|
||||||
# include <unistd.h>
|
|
||||||
# include <sys/mman.h>
|
|
||||||
# include <sys/resource.h>
|
|
||||||
# include <sys/time.h>
|
|
||||||
# include <signal.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef OSTD_USE_VALGRIND
|
|
||||||
# include <valgrind/valgrind.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace ostd {
|
namespace ostd {
|
||||||
|
|
||||||
struct stack_context {
|
|
||||||
void *ptr;
|
|
||||||
size_t size;
|
|
||||||
#ifdef OSTD_USE_VALGRIND
|
|
||||||
int valgrind_id;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
/* from boost.fcontext */
|
/* from boost.fcontext */
|
||||||
|
@ -62,9 +39,6 @@ transfer_t OSTD_CDECL ostd_ontop_fcontext(
|
||||||
fcontext_t const to, void *vp, transfer_t (*fn)(transfer_t)
|
fcontext_t const to, void *vp, transfer_t (*fn)(transfer_t)
|
||||||
);
|
);
|
||||||
|
|
||||||
stack_context context_stack_alloc(size_t ss);
|
|
||||||
void context_stack_free(stack_context &st);
|
|
||||||
|
|
||||||
struct coroutine_context {
|
struct coroutine_context {
|
||||||
protected:
|
protected:
|
||||||
struct forced_unwind {
|
struct forced_unwind {
|
||||||
|
@ -149,7 +123,7 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename SA>
|
template<typename SA>
|
||||||
void make_context(SA sa, void (*callp)(transfer_t)) {
|
void make_context(SA &sa, void (*callp)(transfer_t)) {
|
||||||
p_stack = sa.allocate();
|
p_stack = sa.allocate();
|
||||||
|
|
||||||
constexpr size_t salign = alignof(SA);
|
constexpr size_t salign = alignof(SA);
|
||||||
|
@ -161,11 +135,10 @@ protected:
|
||||||
(static_cast<byte *>(p_stack.ptr) - static_cast<byte *>(sp));
|
(static_cast<byte *>(p_stack.ptr) - static_cast<byte *>(sp));
|
||||||
|
|
||||||
p_coro = ostd_make_fcontext(sp, asize, callp);
|
p_coro = ostd_make_fcontext(sp, asize, callp);
|
||||||
p_sa = new (sp) SA(sa);
|
p_sa = new (sp) SA(std::move(sa));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: new'ing the stack is sub-optimal */
|
stack_context p_stack;
|
||||||
stack_context p_stack = { nullptr, 0 };
|
|
||||||
fcontext_t p_coro;
|
fcontext_t p_coro;
|
||||||
fcontext_t p_orig;
|
fcontext_t p_orig;
|
||||||
std::exception_ptr p_except;
|
std::exception_ptr p_except;
|
||||||
|
@ -173,181 +146,7 @@ protected:
|
||||||
void *p_sa;
|
void *p_sa;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* stack allocator */
|
|
||||||
|
|
||||||
#if defined(OSTD_PLATFORM_WIN32)
|
|
||||||
|
|
||||||
inline size_t context_get_page_size() {
|
|
||||||
SYSTEM_INFO si;
|
|
||||||
GetSystemInfo(&si);
|
|
||||||
return size_t(si.dwPageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t context_stack_get_min_size() {
|
|
||||||
/* no func on windows, sane default */
|
|
||||||
return sizeof(void *) * 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t context_stack_get_max_size() {
|
|
||||||
return 1024 * 1024 * 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t context_stack_get_def_size() {
|
|
||||||
return sizeof(void *) * context_stack_get_min_size();
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(OSTD_PLATFORM_POSIX)
|
|
||||||
|
|
||||||
inline size_t context_get_page_size() {
|
|
||||||
return size_t(sysconf(_SC_PAGESIZE));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t context_stack_get_min_size() {
|
|
||||||
return SIGSTKSZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t context_stack_get_max_size() {
|
|
||||||
rlimit l;
|
|
||||||
getrlimit(RLIMIT_STACK, &l);
|
|
||||||
return size_t(l.rlim_max);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline size_t context_stack_get_def_size() {
|
|
||||||
rlimit l;
|
|
||||||
size_t r = sizeof(void *) * SIGSTKSZ;
|
|
||||||
|
|
||||||
getrlimit(RLIMIT_STACK, &l);
|
|
||||||
if ((l.rlim_max != RLIM_INFINITY) && (l.rlim_max < r)) {
|
|
||||||
return size_t(l.rlim_max);
|
|
||||||
}
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(MAP_ANON) || defined(MAP_ANONYMOUS)
|
|
||||||
constexpr bool CONTEXT_USE_MMAP = true;
|
|
||||||
# ifdef MAP_ANON
|
|
||||||
constexpr auto CONTEXT_MAP_ANON = MAP_ANON;
|
|
||||||
# else
|
|
||||||
constexpr auto CONTEXT_MAP_ANON = MAP_ANONYMOUS;
|
|
||||||
# endif
|
|
||||||
#else
|
|
||||||
constexpr bool CONTEXT_USE_MMAP = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else /* OSTD_PLATFORM_POSIX */
|
|
||||||
# error "Unsupported platform"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template<bool Protected>
|
|
||||||
inline stack_context context_stack_alloc(size_t ss) {
|
|
||||||
if (!ss) {
|
|
||||||
ss = context_stack_get_def_size();
|
|
||||||
} else {
|
|
||||||
ss = std::clamp(
|
|
||||||
ss, context_stack_get_min_size(), context_stack_get_max_size()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
size_t pgs = context_get_page_size();
|
|
||||||
size_t npg = std::max(ss / pgs, size_t(size_t(Protected) + 1));
|
|
||||||
size_t asize = npg * pgs;
|
|
||||||
|
|
||||||
#if defined(OSTD_PLATFORM_WIN32)
|
|
||||||
void *p = VirtualAlloc(0, asize, MEM_COMMIT, PAGE_READWRITE);
|
|
||||||
if (!p) {
|
|
||||||
throw std::bad_alloc{}
|
|
||||||
}
|
|
||||||
DWORD oo;
|
|
||||||
if constexpr(Protected) {
|
|
||||||
VirtualProtect(p, pgs, PAGE_READWRITE | PAGE_GUARD, &oo);
|
|
||||||
}
|
|
||||||
#elif defined(OSTD_PLATFORM_POSIX)
|
|
||||||
void *p = nullptr;
|
|
||||||
if constexpr(CONTEXT_USE_MMAP) {
|
|
||||||
void *mp = mmap(
|
|
||||||
0, asize, PROT_READ | PROT_WRITE,
|
|
||||||
MAP_PRIVATE | CONTEXT_MAP_ANON, -1, 0
|
|
||||||
);
|
|
||||||
if (mp != MAP_FAILED) {
|
|
||||||
p = mp;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
p = std::malloc(asize);
|
|
||||||
}
|
|
||||||
if (!p) {
|
|
||||||
throw std::bad_alloc{};
|
|
||||||
}
|
|
||||||
if constexpr(Protected) {
|
|
||||||
mprotect(p, pgs, PROT_NONE);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
stack_context ret{static_cast<byte *>(p) + ss, ss};
|
|
||||||
|
|
||||||
#ifdef OSTD_USE_VALGRIND
|
|
||||||
ret.valgrind_id = VALGRIND_STACK_REGISTER(ret.ptr, p);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void context_stack_free(stack_context &st) {
|
|
||||||
if (!st.ptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef OSTD_USE_VALGRIND
|
|
||||||
VALGRIND_STACK_DEREGISTER(st.valgrind_id);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto p = static_cast<byte *>(st.ptr) - st.size;
|
|
||||||
#if defined(OSTD_PLATFORM_WIN32)
|
|
||||||
VirtualFree(p, 0, MEM_RELEASE);
|
|
||||||
#elif defined(OSTD_PLATFORM_POSIX)
|
|
||||||
if constexpr(CONTEXT_USE_MMAP) {
|
|
||||||
munmap(p, st.size);
|
|
||||||
} else {
|
|
||||||
std::free(p);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
st.ptr = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
} /* namespace detail */
|
} /* namespace detail */
|
||||||
|
} /* namespace ostd */
|
||||||
struct protected_fixedsize_stack {
|
|
||||||
protected_fixedsize_stack(size_t ss = 0): p_size(ss) {}
|
|
||||||
|
|
||||||
stack_context allocate() {
|
|
||||||
return detail::context_stack_alloc<true>(p_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate(stack_context &st) {
|
|
||||||
detail::context_stack_free(st);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
size_t p_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct fixedsize_stack {
|
|
||||||
fixedsize_stack(size_t ss = 0): p_size(ss) {}
|
|
||||||
|
|
||||||
stack_context allocate() {
|
|
||||||
return detail::context_stack_alloc<false>(p_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void deallocate(stack_context &st) {
|
|
||||||
detail::context_stack_free(st);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
size_t p_size;
|
|
||||||
};
|
|
||||||
|
|
||||||
using default_stack = fixedsize_stack;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue