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
|
||||
ranges (to replace iterators) but also various other utilities like proper
|
||||
streams, string formatting, concurrency utilities and others. It's meant
|
||||
to replace the more poorly designed parts of the C++ standard library to
|
||||
streams, string formatting, coroutines, concurrency utilities and others. It's
|
||||
meant to replace the more poorly designed parts of the C++ standard library to
|
||||
make the language easier and more convenient to use.
|
||||
|
||||
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
|
||||
|
||||
Most of OctaSTD is entirely platform independent and relies only on the
|
||||
standard library. Therefore it can be used on any operating system that
|
||||
provides the right toolchain.
|
||||
standard library. Therefore it could in theory be used on any operating
|
||||
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
|
||||
on system specific APIs. These are restricted to POSIX compliant operating
|
||||
systems and Windows, with testing done on Linux, FreeBSD, macOS and Windows -
|
||||
they should work on other POSIX compliant operating systems as well, and
|
||||
potential patches are welcome.
|
||||
OctaSTD is actively supported on Windows (x86 and x86\_64) as well as Linux,
|
||||
FreeBSD and macOS. It should also work on other POSIX systems such as the other
|
||||
BSDs or Solaris - if it doesn't, please report your problem or better, send
|
||||
patches.
|
||||
|
||||
### 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.
|
||||
*/
|
||||
|
@ -6,37 +6,14 @@
|
|||
#ifndef OSTD_INTERNAL_CONTEXT_HH
|
||||
#define OSTD_INTERNAL_CONTEXT_HH
|
||||
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <exception>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "ostd/types.hh"
|
||||
#include "ostd/platform.hh"
|
||||
#include "ostd/internal/win32.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
|
||||
#include "ostd/context_stack.hh"
|
||||
|
||||
namespace ostd {
|
||||
|
||||
struct stack_context {
|
||||
void *ptr;
|
||||
size_t size;
|
||||
#ifdef OSTD_USE_VALGRIND
|
||||
int valgrind_id;
|
||||
#endif
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
/* 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)
|
||||
);
|
||||
|
||||
stack_context context_stack_alloc(size_t ss);
|
||||
void context_stack_free(stack_context &st);
|
||||
|
||||
struct coroutine_context {
|
||||
protected:
|
||||
struct forced_unwind {
|
||||
|
@ -149,7 +123,7 @@ protected:
|
|||
}
|
||||
|
||||
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();
|
||||
|
||||
constexpr size_t salign = alignof(SA);
|
||||
|
@ -161,11 +135,10 @@ protected:
|
|||
(static_cast<byte *>(p_stack.ptr) - static_cast<byte *>(sp));
|
||||
|
||||
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 = { nullptr, 0 };
|
||||
stack_context p_stack;
|
||||
fcontext_t p_coro;
|
||||
fcontext_t p_orig;
|
||||
std::exception_ptr p_except;
|
||||
|
@ -173,181 +146,7 @@ protected:
|
|||
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 */
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
} /* namespace ostd */
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue