libcommunism
Userspace cooperative threading library
Classes | Public Member Functions | Static Public Attributes | Friends | List of all members
libcommunism::internal::SetJmp Class Referencefinal

Context switching utilizing the C library setjmp() and longjmp() methods. More...

#include <SetJmp.h>

Inheritance diagram for libcommunism::internal::SetJmp:
libcommunism::CothreadImpl

Public Member Functions

 SetJmp (const Entry &entry, const size_t stackSize=0)
 
 SetJmp (const Entry &entry, std::span< uintptr_t > stack)
 
 SetJmp (std::span< uintptr_t > stack)
 
 ~SetJmp ()
 
void switchTo (CothreadImpl *from) override
 
- Public Member Functions inherited from libcommunism::CothreadImpl
 CothreadImpl (const Cothread::Entry &entry, const size_t stackSize=0)
 
 CothreadImpl (const Cothread::Entry &entry, std::span< uintptr_t > stack)
 
 CothreadImpl (std::span< uintptr_t > stack)
 
virtual ~CothreadImpl ()=default
 
virtual size_t getStackSize () const
 
virtual void * getStack () const
 

Static Public Attributes

static constexpr const size_t kStackAlignment {64}
 
static constexpr const size_t kMainStackSize {512}
 
static constexpr const size_t kDefaultStackSize {sizeof(uintptr_t) * 0x10000}
 

Friends

CothreadImpllibcommunism::AllocKernelThreadWrapper ()
 

Additional Inherited Members

- Public Types inherited from libcommunism::CothreadImpl
using Entry = Cothread::Entry
 
- Protected Attributes inherited from libcommunism::CothreadImpl
std::span< uintptr_t > stack
 Stack used by this cothread, if any. More...
 

Detailed Description

Context switching utilizing the C library setjmp() and longjmp() methods.

The means by which this is works is based on ideas by Ralf S. Engelschall, from the 2000 paper titled Portable Multithreading. Thread stacks are set up in a portable way by making use of signal handlers, so this should be supported on basically all targets that have a functional C library and are UNIX-y.

Remarks
Since signals are a per-process resource, allocation of cothreads effectively becomes serialized to ensure safety.

Definition at line 25 of file SetJmp.h.

Constructor & Destructor Documentation

◆ SetJmp() [1/3]

SetJmp::SetJmp ( const Entry entry,
const size_t  stackSize = 0 
)

Allocates a cothread including a context region of the specified size.

This ensures there's sufficient bonus space allocated to hold the sigjmp_buf.

Definition at line 36 of file SetJmp.cpp.

36  : CothreadImpl(entry, stackSize) {
37  void *buf{nullptr};
38 
39  // round down stack size to ensure it's aligned before allocating it
40  auto allocSize = stackSize & ~(SetJmp::kStackAlignment - 1);
41  allocSize = allocSize ? allocSize : SetJmp::kDefaultStackSize;
42 
43  // then add space for ucontext
44  allocSize += sizeof(sigjmp_buf);
45  if(allocSize % SetJmp::kStackAlignment) {
46  allocSize += SetJmp::kStackAlignment - (allocSize % SetJmp::kStackAlignment);
47  }
48 
49  // and allocate it
50 #ifdef _WIN32
51  buf = _aligned_malloc(allocSize, SetJmp::kStackAlignment);
52 #else
53  int err{0};
54  err = posix_memalign(&buf, SetJmp::kStackAlignment, allocSize);
55  if(err) {
56  throw std::runtime_error("posix_memalign() failed");
57  }
58 #endif
59  if(!buf) {
60  throw std::runtime_error("failed to allocate stack");
61  }
62 
63  // create it as if we had provided the memory in the first place
64  this->stack = {reinterpret_cast<uintptr_t *>(buf), allocSize / sizeof(uintptr_t)};
65  this->ownsStack = true;
66 
67  Prepare(this, entry);
68 }
static constexpr const size_t kStackAlignment
Definition: SetJmp.h:82
static constexpr const size_t kDefaultStackSize
Definition: SetJmp.h:96
CothreadImpl(const Cothread::Entry &entry, const size_t stackSize=0)
Definition: CothreadImpl.h:29
std::span< uintptr_t > stack
Stack used by this cothread, if any.
Definition: CothreadImpl.h:88

◆ SetJmp() [2/3]

SetJmp::SetJmp ( const Entry entry,
std::span< uintptr_t >  stack 
)

Allocates a cothread with an existing region of memory to back its stack and jump buffer.

Definition at line 73 of file SetJmp.cpp.

73  : CothreadImpl(entry, stack) {
74  Prepare(this, entry);
75 }

◆ SetJmp() [3/3]

libcommunism::internal::SetJmp::SetJmp ( std::span< uintptr_t >  stack)
inline

Definition at line 31 of file SetJmp.h.

31 : CothreadImpl(stack) {}

◆ ~SetJmp()

SetJmp::~SetJmp ( )

Release the underlying stack if required

Definition at line 80 of file SetJmp.cpp.

80  {
81  if(this->ownsStack) {
82 #ifdef _WIN32
83  _aligned_free(this->stack.data());
84 #else
85  free(this->stack.data());
86 #endif
87  }
88 }

Member Function Documentation

◆ switchTo()

void SetJmp::switchTo ( CothreadImpl _from)
overridevirtual

Performs a context switch to the provided cothread.

Implements libcommunism::CothreadImpl.

Definition at line 93 of file SetJmp.cpp.

93  {
94  auto from = static_cast<SetJmp *>(_from);
95 
96  if(!sigsetjmp(*SetJmp::JmpBufFor(from), 0)) {
97  std::atomic_thread_fence(std::memory_order_release);
98  siglongjmp(*SetJmp::JmpBufFor(this), 1);
99  }
100 }
Context switching utilizing the C library setjmp() and longjmp() methods.
Definition: SetJmp.h:25

Friends And Related Function Documentation

◆ libcommunism::AllocKernelThreadWrapper

Member Data Documentation

◆ kDefaultStackSize

constexpr const size_t libcommunism::internal::SetJmp::kDefaultStackSize {sizeof(uintptr_t) * 0x10000}
staticconstexpr

Default stack size in bytes, if none was requested by the caller. Since this implementation may work on different width architectures, we define this as 64K worth of machine words.

Definition at line 96 of file SetJmp.h.

◆ kMainStackSize

constexpr const size_t libcommunism::internal::SetJmp::kMainStackSize {512}
staticconstexpr

Size of the stack buffer for the "fake" initial cothread, in machine words. This only needs to be large enough to fit the register stack frame. This must be a power of two.

It must be sufficiently large to fit an sigjmp_buf it.

Definition at line 90 of file SetJmp.h.

◆ kStackAlignment

constexpr const size_t libcommunism::internal::SetJmp::kStackAlignment {64}
staticconstexpr

Requested alignment for stack allocations.

64 bytes is the most stringent alignment requirements we should probably encounter in the real world (one cache line on most systems) and alignment doesn't result in that much overhead so this is fine.

Remarks
This must be a power of 2.

Definition at line 82 of file SetJmp.h.


The documentation for this class was generated from the following files: