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

Implementation of context switching that uses the C library's setcontext() methods. More...

#include <UContext.h>

Inheritance diagram for libcommunism::internal::UContext:
libcommunism::CothreadImpl

Public Member Functions

 UContext (const Entry &entry, const size_t stackSize=0)
 
 UContext (const Entry &entry, std::span< uintptr_t > stack)
 
 UContext (std::span< uintptr_t > stack)
 
 ~UContext ()
 
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 {128}
 
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

Implementation of context switching that uses the C library's setcontext() methods.

This is intended mostly to be a "test" platform that can be used to verify that the core library works, without relying on assembly other such fun stuff. It's not particularly fast so other platform backends should always be used in preference.

Since we need a place to store the context in addition to the stack, it's stored at the very top of the allocated stack. When allocating the stack internally, this is taken account and some extra space at the top is reserved for it; but this must be kept in mind when using an externally allocated stack, as less (roughly sizeof(ucontext_t) and alignment) space than provided will be actually be available as stack.

Note
Since ucontext has been deprecated since the 2008 revision of POSIX, this may stop working (or not even be supported to begin with) on any given platform in the future.

Definition at line 33 of file UContext.h.

Constructor & Destructor Documentation

◆ UContext() [1/3]

UContext::UContext ( 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 ucontext.

Definition at line 37 of file UContext.cpp.

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

◆ UContext() [2/3]

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

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

Definition at line 74 of file UContext.cpp.

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

◆ UContext() [3/3]

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

Definition at line 39 of file UContext.h.

39 : CothreadImpl(stack) {}

◆ ~UContext()

UContext::~UContext ( )

Deallocates a cothread. This releases the underlying stack if we allocated it.

Definition at line 81 of file UContext.cpp.

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

Member Function Documentation

◆ switchTo()

void UContext::switchTo ( CothreadImpl from)
overridevirtual

Performs a context switch to the provided cothread.

The state of the caller is stored on the stack of the currently active thread.

Implements libcommunism::CothreadImpl.

Definition at line 180 of file UContext.cpp.

180  {
181  swapcontext(UContext::ContextFor(from), UContext::ContextFor(this));
182 }

Friends And Related Function Documentation

◆ libcommunism::AllocKernelThreadWrapper

Member Data Documentation

◆ kDefaultStackSize

constexpr const size_t libcommunism::internal::UContext::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 100 of file UContext.h.

◆ kMainStackSize

constexpr const size_t libcommunism::internal::UContext::kMainStackSize {128}
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 ucontext_t in it.

Definition at line 94 of file UContext.h.

◆ kStackAlignment

constexpr const size_t libcommunism::internal::UContext::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 86 of file UContext.h.


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