Coroutine - C++11 single .h asymmetric coroutine implementation via ucontext / fiber

C++11 single .h asymmetric coroutine implementation

API

in namespace coroutine:

  • routine_t create(std::function<void()> f);
  • void destroy(routine_t id);
  • int resume(routine_t id);
  • void yield();
  • TYPE await(TYPE(*f)());
  • routine_t current();
  • class Channel with push()/pop();

OS

  • Linux
  • macOS
  • Windows

Demo

#include "coroutine.h"
#include <iostream>
#include <chrono>

coroutine::Channel<int> channel;

string async_func()
{
    std::this_thread::sleep_for(std::chrono::milliseconds(3000));
	return "22";
}

void routine_func1()
{
	int i = channel.pop();
	std::cout << i << std::endl;
	
	i = channel.pop();
	std::cout << i << std::endl;
}

void routine_func2(int i)
{
	std::cout << "20" << std::endl;
	coroutine::yield();
	
	std::cout << "21" << std::endl;

	//run function async
	//yield current routine if result not returned
	string str = coroutine::await(async_func);
	std::cout << str << std::endl;
}

void thread_func()
{
	//create routine with callback like std::function<void()>
	coroutine::routine_t rt1 = coroutine::create(routine_func1);
	coroutine::routine_t rt2 = coroutine::create(std::bind(routine_func2, 2));
	
	std::cout << "00" << std::endl;	
	coroutine::resume(rt1);

	std::cout << "01" << std::endl;
	coroutine::resume(rt2);
	
	std::cout << "02" << std::endl;
	channel.push(10);
	
	std::cout << "03" << std::endl;
	coroutine::resume(rt2);
	
	std::cout << "04" << std::endl;
	channel.push(11);
	
	std::cout << "05" << std::endl;
	
	std::this_thread::sleep_for(std::chrono::milliseconds(6000));
	coroutine::resume(rt2);

	//destroy routine, free resouce allocated
	//Warning: don't destroy routine by itself
	coroutine::destroy(rt1);
	coroutine::destroy(rt2);
}

int main()
{
	std::thread t1(thread_func);
	std::thread t2([](){
		//unsupport coordinating routine crossing threads
	});
	t1.join();
	t2.join();
	return 0;
}
Comments
  • Nested coroutines. Is it possible?

    Nested coroutines. Is it possible?

    Hi, I have a code that semantically is similar to this:

    void routine()
    {
        std::cout << "Hello" << std::endl;
        coroutine::yield();
        std::cout << "done" << std::endl;
    }
    
    void routine_nested()
    {
        coroutine::routine_t rt = coroutine::create(routine);
        coroutine::resume(rt);
        coroutine::resume(rt);
        coroutine::yield();
    }
    
    int main()
    {
        coroutine::routine_t nested_rt = coroutine::create(routine_nested);
        coroutine::resume(nested_rt);
        return 0;
    }
    

    Is it (at least conceptually, possible to make this work? This assertion currently fails:

    inline int resume(routine_t id)
    {
        assert(ordinator.current == 0);
    
  • inline missing for coroutine::yield

    inline missing for coroutine::yield

    See https://github.com/tonbit/coroutine/blob/master/coroutine.h#L366 - the method should be inline, just like all the other methods. Without that compiling with multiple sources that all use the header obviously will fail.

  • Does Ordinator need to be thread_local?

    Does Ordinator need to be thread_local?

    Hi,

    I am having problems when a routine is created in a thread and destroyed in another one.

    This is related to the fact that ordenator is thread_local.

    If we remove the thread local and we protect the function using ordenator with a mutex, do you think it it still work?

  • problem about stack size in yield

    problem about stack size in yield

    at line 358 - 360

    	char *stack_top = routine->stack + ordinator.stack_size;
    	char stack_bottom = 0;
    	assert(size_t(stack_top - &stack_bottom) <= ordinator.stack_size);
    

    routine->stack is on the heap but stack_bottom is on the stack, I think that this assertion will always false

  • Problem about destroy

    Problem about destroy

    Does calling the destroy have a null pointer for the routines before the function is executed?

    And do we need the finished field?

    I think that we can destroy every routine after it's function was executed.

  • Correctly return finished

    Correctly return finished

    Hi,

    I think this change makes the behavior of resume more semantically correct.

    Consider the following code

    void routine_func()
    {
        std::cout << "step1" << std::endl;
        coroutine::yield();
    
        std::cout << "step2" << std::endl;
        coroutine::yield();
    
        std::cout << "step3"<< std::endl;
        coroutine::yiel3();
    
         std::cout << "done" << std::endl;
    }
    
    int main()
    {
        coroutine::routine_t rt = coroutine::create(routine_func);
    
        std::cout << coroutine::resume(rt) << std::endl;
        std::cout << coroutine::resume(rt) << std::endl;
        std::cout << coroutine::resume(rt) << std::endl;
        std::cout << coroutine::resume(rt) << std::endl;
    
       return 0;
    }
    

    I think it is reasonable to expect an output like

    step1
    0
    step2
    0
    step3
    0
    done
    -2
    

    I would also suggest to add an enum like this

    enum ResumeResult{
        INVALID = -1,
        FINISHED = -2,
        YIELD = 0
    };
    

    Cheers

    Davide

  • async example

    async example

    Hi will the routine resume once it has finished the async operation using await? I tried your example but I'm unable to get any return from the async function unless I comment out the sleep_for part.

Single header asymmetric stackful cross-platform coroutine library in pure C.
Single header asymmetric stackful cross-platform coroutine library in pure C.

minicoro Minicoro is single-file library for using asymmetric coroutines in C. The API is inspired by Lua coroutines but with C use in mind. The proje

Dec 29, 2022
A hybrid thread / fiber task scheduler written in C++ 11

Marl Marl is a hybrid thread / fiber task scheduler written in C++ 11. About Marl is a C++ 11 library that provides a fluent interface for running tas

Jan 4, 2023
Lucy job system - Fiber-based job system with extremely simple API

Lucy Job System This is outdated compared to Lumix Engine. Use that instead. Fiber-based job system with extremely simple API. It's a standalone versi

Dec 21, 2022
Termite-jobs - Fast, multiplatform fiber based job dispatcher based on Naughty Dogs' GDC2015 talk.

NOTE This library is obsolete and may contain bugs. For maintained version checkout sx library. until I rip it from there and make a proper single-hea

Jan 9, 2022
Mx - C++ coroutine await, yield, channels, i/o events (single header + link to boost)

mx C++11 coroutine await, yield, channels, i/o events (single header + link to boost). This was originally part of my c++ util library kit, but I'm se

Sep 21, 2019
A fast single-producer, single-consumer lock-free queue for C++

A single-producer, single-consumer lock-free queue for C++ This mini-repository has my very own implementation of a lock-free queue (that I designed f

Jan 5, 2023
A bounded single-producer single-consumer wait-free and lock-free queue written in C++11
A bounded single-producer single-consumer wait-free and lock-free queue written in C++11

SPSCQueue.h A single producer single consumer wait-free and lock-free fixed size queue written in C++11. Example SPSCQueue<int> q(2); auto t = std::th

Dec 27, 2022
Powerful multi-threaded coroutine dispatcher and parallel execution engine

Quantum Library : A scalable C++ coroutine framework Quantum is a full-featured and powerful C++ framework build on top of the Boost coroutine library

Dec 30, 2022
:copyright: Concurrent Programming Library (Coroutine) for C11

libconcurrent tiny asymmetric-coroutine library. Description asymmetric-coroutine bidirectional communication by yield_value/resume_value native conte

Sep 2, 2022
A golang-style C++ coroutine library and more.

CO is an elegant and efficient C++ base library that supports Linux, Windows and Mac platforms. It pursues minimalism and efficiency, and does not rely on third-party library such as boost.

Jan 5, 2023
A C++20 coroutine library based off asyncio
A C++20 coroutine library based off asyncio

kuro A C++20 coroutine library, somewhat modelled on Python's asyncio Requirements Kuro requires a C++20 compliant compiler and a Linux OS. Tested on

Nov 9, 2022
C++20 Coroutine-Based Synchronous Parser Combinator Library

This library contains a monadic parser type and associated combinators that can be composed to create parsers using C++20 Coroutines.

Dec 17, 2022
Async GRPC with C++20 coroutine support

agrpc Build an elegant GRPC async interface with C++20 coroutine and libunifex (target for C++23 executor). Get started mkdir build && cd build conan

Dec 21, 2022
Cppcoro - A library of C++ coroutine abstractions for the coroutines TS

CppCoro - A coroutine library for C++ The 'cppcoro' library provides a large set of general-purpose primitives for making use of the coroutines TS pro

Dec 30, 2022
Elle - The Elle coroutine-based asynchronous C++ development framework.
Elle - The Elle coroutine-based asynchronous C++ development framework.

Elle, the coroutine-based asynchronous C++ development framework Elle is a collection of libraries, written in modern C++ (C++14). It contains a rich

Jan 1, 2023
A go-style coroutine library in C++11 and more.
A go-style coroutine library in C++11 and more.

cocoyaxi English | 简体中文 A go-style coroutine library in C++11 and more. 0. Introduction cocoyaxi (co for short), is an elegant and efficient cross-pla

Dec 27, 2022
C++14 coroutine-based task library for games

SquidTasks Squid::Tasks is a header-only C++14 coroutine-based task library for games. Full project and source code available at https://github.com/we

Nov 30, 2022
The RaftLib C++ library, streaming/dataflow concurrency via C++ iostream-like operators

RaftLib is a C++ Library for enabling stream/data-flow parallel computation. Using simple right shift operators (just like the C++ streams that you wo

Dec 24, 2022
This is a study on how to do create a queue via IPC (inter-process communication)

IPC queue This is a study on how to do create a queue via IPC (inter-process communication). Please take a look at the examples of producer and consum

Nov 28, 2022