libcommunism
Userspace cooperative threading library
Public Types | Public Member Functions | Static Public Member Functions | List of all members
libcommunism::Cothread Class Reference

Instance of a single cooperative thread. More...

#include <Cothread.h>

Public Types

using Entry = std::function< void()>
 Type alias for an entry point of a cothread. More...
 

Public Member Functions

 Cothread (const Entry &entry, const size_t stackSize=0)
 
 Cothread (const Entry &entry, std::span< uintptr_t > stack)
 
 ~Cothread ()
 
void switchTo ()
 
constexpr auto & getLabel () const
 
void setLabel (const std::string &newLabel)
 
size_t getStackSize () const
 
void * getStack () const
 

Static Public Member Functions

static CothreadCurrent ()
 
static void SetReturnHandler (const std::function< void(Cothread *)> &handler)
 
static void ResetReturnHandler ()
 

Detailed Description

Instance of a single cooperative thread.

Cooperative threads are threads that perform context switching in userspace, rather than relying on the kernel to do this. This has distinct performance advantages as the context switch is avoided, which costs a significant amount of clock cycles.

Definition at line 24 of file Cothread.h.

Member Typedef Documentation

◆ Entry

using libcommunism::Cothread::Entry = std::function<void()>

Type alias for an entry point of a cothread.

Definition at line 27 of file Cothread.h.

Constructor & Destructor Documentation

◆ Cothread() [1/2]

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

Allocates a new cothread without explicitly allocating its stack.

Remarks
The backing storage for the cothread is allocated internally by the platform implementation and not directly accessible to clients of this interface. Depending on the platform, it may be allocated in a special way to match how normal stacks are allocated on the platform, rather than by using a mechanism like malloc(). That is to say, you should not have any expectations on how or where the stack is allocated.
Note
If the entry point returns, the the cothread return handler is invoked; its default action is to terminate the program, as the state of the stack after return from the main thread is undefined and may result in undefined behavior.
Parameters
entryMethod to execute on entry to this cothread
stackSizeSize of the stack to be allocated, in bytes. it should be a multiple of the machine word size, or specify zero to use the platform default.
Exceptions
std::runtime_errorIf the memory for the cothread could not be allocated.
std::runtime_errorIf the provided stack size is invalid
Returns
An initialized cothread object

Definition at line 99 of file Cothread.cpp.

99  {
100  this->impl = AllocImpl(this->implBuffer, this->implBufferUsed, entry, stackSize);
101 }

◆ Cothread() [2/2]

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

Allocates a new cothread, using an existing buffer to store its stack.

Remarks
You are responsible for managing the buffer memory, i.e. freeing it after the cothread has been deallocated. See notes for important information.
Note
If the entry point returns, the the cothread return handler is invoked; its default action is to terminate the program, as the state of the stack after return from the main thread is undefined and may result in undefined behavior.
The provided buffer must remain valid for the duration of the cothread's life. If it is deallocated or otherwise reused during the lifetime of the cothread, undefined behavior results. You must not attempt to manually modify the contents of the buffer. Additionally, it must meet alignment requirements for stacks on the underlying platform. (A 64 byte alignment should be safe for most platforms.)
Parameters
entryMethod to execute on entry to this cothread
stackBuffer to use as the stack of the cothread
Exceptions
std::runtime_errorIf the provided stack is invalid
Returns
An initialized cothread object

Definition at line 103 of file Cothread.cpp.

103  {
104  this->impl = AllocImpl(this->implBuffer, this->implBufferUsed, entry, stack);
105 }

◆ ~Cothread()

Cothread::~Cothread ( )

Destroys a previously allocated cothread, and deallocates any underlying buffer memory used by it.

Note
Destroying the currently executing cothread results in undefined behavior, as it will cause its stack to be deallocated.

Definition at line 108 of file Cothread.cpp.

108  {
109  if(this->implBufferUsed) {
110  reinterpret_cast<CothreadImpl *>(this->implBuffer.data())->~CothreadImpl();
111  } else {
112  delete this->impl;
113  }
114  this->impl = nullptr;
115 }
Abstract interface for a platform implementation of cothreads.
Definition: CothreadImpl.h:18

Member Function Documentation

◆ Current()

Cothread * Cothread::Current ( )
static

Returns the cothread currently executing on the calling "physical" thread.

Remarks
If the calling thread is not executing a cothread, a special handle is returned that points to a static, per thread buffer. Its sole purpose is to store the register state of the caller that invoked the first cothread on this physical thread. Currently, that buffer is not directly accessible, aside from storing this handle before any cothreads are executed.
Returns
Handle to the current cothread

Definition at line 117 of file Cothread.cpp.

117  {
118  if(!gCurrent) {
119  auto impl = AllocKernelThreadWrapper();
120  if(!impl) {
121  std::cerr << "failed to allocate kernel cothread wrapper!" << std::endl;
122  std::terminate();
123  }
124 
125  gCurrent = new Cothread(impl);
126  }
127  return gCurrent;
128 }
Cothread(const Entry &entry, const size_t stackSize=0)
Definition: Cothread.cpp:99
CothreadImpl * AllocKernelThreadWrapper()
Definition: Common.cpp:134

◆ getLabel()

constexpr auto& libcommunism::Cothread::getLabel ( ) const
inlineconstexpr

Gets the debug label (name) associated with this cothread.

Returns
A string containing the thread's debug label

Definition at line 139 of file Cothread.h.

139  {
140  return this->label;
141  }

◆ getStack()

void * Cothread::getStack ( ) const

Get the location of the top of the cothread's stack.

Remarks
Regardless of the direction of the platform's stack growth, the top here refers to the lowest address of the stack. That is, the range of memory reserved for stack is [start, start + getStackSize()).
Note
You should never attempt to modify the stack, particularly while the cothread is executing. Its contents are highly machine and platform dependent.
Returns
Pointer to the top of the stack

Definition at line 144 of file Cothread.cpp.

144  {
145  return this->impl->getStack();
146 }
virtual void * getStack() const
Definition: CothreadImpl.h:82

◆ getStackSize()

size_t Cothread::getStackSize ( ) const

Get the size of the cothread's stack. This should be intended mainly as an advisory value rather than as a way to check against stack overflow.

Returns
Size of the stack, in bytes.

Definition at line 148 of file Cothread.cpp.

148  {
149  return this->impl->getStackSize();
150 }
virtual size_t getStackSize() const
Definition: CothreadImpl.h:72

◆ ResetReturnHandler()

void Cothread::ResetReturnHandler ( )
static

Installs the default handler for a cothread that returns from its entry point. This will terminate the program.

Definition at line 134 of file Cothread.cpp.

134  {
135  gReturnHandler = DefaultCothreadReturnedHandler;
136 }
std::function< void(libcommunism::Cothread *)> gReturnHandler
Definition: Cothread.cpp:72

◆ setLabel()

void libcommunism::Cothread::setLabel ( const std::string &  newLabel)
inline

Changes the debug label (name) associated with this cothread.

Parameters
newLabelNew string value to set as the cothread's label

Definition at line 148 of file Cothread.h.

148  {
149  this->label = newLabel;
150  }

◆ SetReturnHandler()

void Cothread::SetReturnHandler ( const std::function< void(Cothread *)> &  handler)
static

Sets the method that's invoked when a cothread returns from its entry point. The deafult action is to terminate the program when this occurs.

Remarks
It's suggested that cothreads returning from entry is treated as a fatal programming error. The state of the cothread's stack is not well defined after it returns (w.r.t. alignment;) instead design your code so that it switches to another cothread when it's done, at which point it can be deallocated.
Parameters
handlerFunction to invoke with a pointer to the cothread that returned from its main method.

Definition at line 130 of file Cothread.cpp.

130  {
131  gReturnHandler = handler;
132 }

◆ switchTo()

void Cothread::switchTo ( )

Performs a context switch to this cothread.

This method saves the context of the current cothread (registers, including the stack pointer) on top of its stack; then restores registers, stack and returns control to the destination cothread.

Note
Do not attempt to switch to a currently executing cothread, whether it is on the same physical thread or not. This will corrupt both cothreads' stacks and result in undefined behavior.

Definition at line 138 of file Cothread.cpp.

138  {
139  auto from = Current()->impl;
140  gCurrent = this;
141  this->impl->switchTo(from);
142 }
static Cothread * Current()
Definition: Cothread.cpp:117
virtual void switchTo(CothreadImpl *from)=0

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