Library / SDK
owent/libcopp avatar
owent/libcopp

libcopp is a stackful C++ coroutine library that predates C++20

cross-platform coroutine library in c++

865 stars112 forksC++MIT

At a glance

What is it?
libcopp provides its own coroutine type, its own context switching and its own stack management on top of C++11, and it works across Linux, Windows, MinGW and macOS with a CI matrix that deliberately builds the awkward configurations, including no exceptions, thread-unsafe mode and Intel TSX. The cost of that control is a build system with more options than a typical library, and an upgrade path with renamed types.
Who is it for?
libcopp fits a C++ project that needs stackful coroutines with a runtime it controls, on a platform where the standard library offers none, and that wants the same source to build on Linux, Windows and macOS. Skip it if your codebase already targets C++20 and uses the built-in coroutine machinery, or if you want a library with no context switching layer of its own.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 38 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A coroutine library with its own stacks

libcopp describes itself in three words, a cross-platform coroutine library in C++, and the details of what that means are in the build system rather than in the prose.

The central type is `cotask::task<T>`, and the library is stackful, which means each coroutine has its own stack and the library switches between them. That is a different model from the stackless coroutines that C++20 put in the standard library, and it explains the entire shape of the repository. There is a `src/` directory for the implementation, an `include/` directory for the public headers, and a `project/` directory for the build definitions.

The sample directory shows what the library is actually built for. The example files are named for what they measure, with `sample_benchmark_coroutine_malloc.cpp`, `sample_benchmark_coroutine_mem_pool.cpp` and `sample_benchmark_coroutine_stack_pool.cpp` sitting next to the equivalent `sample_benchmark_task_` variants, plus generator and reuse benchmarks for both coroutine and task flavours. A stack pool benchmark next to a malloc benchmark is a project telling you it expects you to care about where coroutine stacks come from.

There is also `sample_default_coroutine_context.cpp` and `sample_linux_gcc_splited_segment.cpp`, the second of which corresponds to the segmented stack option described later. The topics on the repository name the same concerns, with stack, pool, thread-safety, timer and lock-free among the tags.

The licence is MIT, the default branch is `v2`, and the repository is not archived, with the last push on 2026-09-15.

The CI matrix is the real specification

The README has no feature list, but it does have a table of CI jobs, and that table says more about the library than any marketing copy would.

text
| Linux | GCC | Static linking |
| Linux | GCC | Dynamic linking |
| Linux | GCC-latest |  |
| Linux | GCC-latest | No Exception |
| Linux | GCC-latest | Thread Unsafe |
| Linux | Clang-latest | With libc++ |
| MinGW64 | GCC | Dynamic linking |
| Windows | Visual Studio 2022 | Static linking |
| Windows | Visual Studio 2022 | Dynamic linking |
| macOS | AppleClang | With libc++ |

Two of those rows are the interesting ones. A build with exceptions disabled and a build in thread-unsafe mode are configurations that break most context switching implementations, because the library's own error handling has to survive an exception thrown across a stack switch. If those builds are green on every commit, the switching code is not relying on unwinding to do something it should not.

The linking rows matter for a different reason. A stackful coroutine switches stacks, and the code that runs on the other stack is in a different stack frame, which changes what the unwinder and the profiler see. Building both static and dynamic, and on three toolchain families including MinGW and AppleClang with libc++, is the project checking that its assumptions hold everywhere.

The other evidence of care is in the tooling rather than the table. The repository carries a `.clang-format` file, a `.cmake-format.yaml` file, a `.gcc-flags.json` file, a `.scan-build.enable` and a `.scan-build.disable` pair, a `codecov.yml`, and a `tools/` directory. The `ci/` directory and the `cmake_dev.sh`, `cmake_dev.bat` and `cmake_dev.ps1` scripts cover development on each platform.

The cmake options that change what gets built

libcopp uses cmake, and the README lists the options that matter, because several of them do not just add features but change the context switching implementation underneath.

text
| LIBCOPP_ENABLE_SEGMENTED_STACKS=YES\|NO | [default=NO] Enable split stack supported context.(it's only availabe in linux and gcc 4.7.0 or upper) |
| LIBCOPP_ENABLE_VALGRIND=YES\|NO | [default=YES] Enable valgrind supported context. |
| PROJECT_ENABLE_UNITTEST=YES\|NO | [default=NO] Build unit test. |
| PROJECT_ENABLE_SAMPLE=YES\|NO | [default=NO] Build samples. |
| LIBCOPP_ENABLE_MULTI_THREAD=YES\|NO | [default=YES] Enable multi-thread support. |
| LIBCOTASK_ENABLE=YES\|NO | [default=YES] Enable build libcotask. |
| LIBCOPP_FCONTEXT_USE_TSX=YES\|NO | [default=YES] Enable [Intel Transactional Synchronisation Extensions (TSX)](https://software.intel.com/en-us/node/695149). |
| LIBCOPP_MACRO_TLS_STACK_PROTECTOR=YES\|NO | [default=NO] Users need set LIBCOPP_MACRO_TLS_STACK_PROTECTOR=ON when compiling with `-fstack-protector`. Because it changes the default context switching logic. |

The segmented stack option is the classic trade-off in this space, and the README is candid that it is Linux and GCC only, at version 4.7.0 or above. Split stacks let a coroutine start with a small stack that grows on demand, which removes the fixed allocation, and the price is that the growth is invisible to the tools that normally understand stack usage.

The valgrind option exists for the same reason. A context switch that moves the instruction pointer to another stack confuses memory tools unless the library tells them what happened, and the option is on by default, which says the maintainers would rather pay for that than have every user debug a false positive.

TSX is enabled by default and is the most interesting of the set. Hardware transactional synchronisation can make a context switch cheaper, and the option lets you turn it off, which you would want on hardware where the feature is present but slow, or where it is disabled by the microcode updates that followed the speculative execution problems.

One option is a trap for the unwary. `LIBCOPP_MACRO_TLS_STACK_PROTECTOR` defaults to off, and the README says users need to set it to ON when compiling with `-fstack-protector`, because that flag changes the default context switching logic. A project that enables stack protection for good reason and forgets this option gets a library that does not behave as configured.

Building it with vcpkg or from a clone

There are two documented install paths, and the vcpkg one is the short one for Windows users in particular.

The vcpkg route starts by cloning the tool and bootstrapping it.

bash
   git clone https://github.com/Microsoft/vcpkg.git
   cd vcpkg
   PS> bootstrap-vcpkg.bootstrap
   Linux:~/$ ./bootstrap-vcpkg.sh

Then the library itself is installed as a vcpkg package.

bash
   PS> .\vcpkg install libcopp [--triplet x64-windows-static/x64-windows/x64-windows-static-md and etc...]
   Linux:~/$ ./vcpkg install libcopp

The custom build route clones the project and makes a build directory, and the clone uses a shallow, single branch checkout of the master branch.

bash
   git clone --single-branch --depth=1 -b master https://github.com/owent/libcopp.git
   mkdir libcopp/build && cd libcopp/build

Then cmake is configured with the optional parts switched on, and built.

bash
   # cmake <libcopp dir> [options...]
   cmake .. -DPROJECT_ENABLE_UNITTEST=YES -DPROJECT_ENABLE_SAMPLE=YES
bash
   cmake --build . --config RelWithDebInfo # or make [options] when using Makefile

The tests, the samples and the benchmarks are separate ctest labels, so you can run just the unit tests.

bash
   ctest -VV . -C RelWithDebInfo -L libcopp.unit_test

Consuming it from cmake is a `find_package` and a target link, where the imported target is `libcopp::cotask`.

cmake
   find_package(Libcopp CONFIG REQUIRED)
   target_link_libraries(${CUSTOM_TARGET_NAME} libcopp::cotask)

The prerequisites are spelled out as well, with a compiler supporting ISO C++ 11 or above, cmake 3.16.0 and above, binutils or llvm on Unix, masm on Windows, and optionally gtest 1.6.0 or above or Boost.Test for the test framework, with pthread required when using gtest.

The 2.x upgrade renamed the vocabulary of your task type

The README devotes two sections to upgrading, and the 1.x to 2.x list is the one that will cost you time, because it renames things you use in your own code.

For a `cotask::task<T>`, you have to add `using value_type = int;` into `T`, rename `stack_allocator_t` to `stack_allocator_type` inside `T`, and rename `coroutine_t` to `coroutine_type` inside `T`. The `libcopp::util::*` namespace becomes `copp::util::`, and the library no longer allows `libcopp::util::intrusive_ptr<cotask::impl::task_impl>`, so you use `cotask::task<T>::ptr_type` instead.

The earlier list, from 1.2.x to 1.3.x and 1.4.x, renames `cotask::task::await` into `cotask::task::await_task`, replaces `cotask::task<TCO_MACRO, TTASK_MACRO>` with `cotask::task<TCO_MACRO>` because a custom id allocator is no longer allowed, replaces `cotask::core::standard_int_id_allocator<uint64_t>` with `copp::util::uint64_id_allocator`, and raises the compiler floor to gcc 4.8 or above and MSVC 15 or above, with cmake 3.12.0 or above.

The custom id allocator removal is the substantive change rather than the spelling ones. A second template parameter that let you control how task identifiers were allocated is gone, and the allocator is now a fixed type. Anyone who used that parameter for a custom memory scheme has to restructure, not rename.

There is also an inconsistency worth noticing in the requirements. The upgrade section for 1.3.x and 1.4.x states cmake 3.12.0 or upper, while the install prerequisites state cmake 3.16.0 and upper. The higher number is the one to plan for, since that is what the current install path asks for.

Where this library is the wrong choice

The honest framing for anyone evaluating libcopp is that it occupies a specific slot, and outside that slot it is more work than the alternative.

The slot is a stackful coroutine in C++11 or above, on a platform where the standard library has no coroutine support, with the project needing to switch between them explicitly rather than through a suspension model. If that is your situation, the library is a good fit, because it has been made to work on Linux, Windows, MinGW and macOS with a CI matrix that tests the failure-prone configurations.

The alternative is the C++20 standard coroutine machinery, which is part of the language rather than a library, and which does not need a library owned stack at all. The trade is real: a stackless coroutine does not switch stacks, so it does not need context switching, does not need a stack pool, and does not need the option list above, but it also cannot do the things that require a separate stack, and the code that uses it is shaped differently.

The second case where this is the wrong tool is a project that does not want a runtime of its own. Everything the library offers around the coroutine type, the stack pools and the context implementations has to be understood, and a team that only needs a task abstraction can find something far less demanding.

A third practical point is the build surface. A library that compiles its own context switching with different compilers, and that needs an option to change the switching logic when the stack protector is on, is not a dependency you add to a project on a Friday afternoon. The CI matrix is a strength and a warning at the same time, since it means the library takes its portability seriously and expects you to take your build configuration seriously too.

A mature 2.x line with a slow release rhythm

The maintenance picture is the last thing to weigh, and it is a project at a stable point with a slow cadence.

The release history shows v2.3.1 on 2025-03-12, v2.3.2 on 2026-01-20 and v2.3.3 on 2026-09-15, with the last push on the same day as the newest tag. So the last three releases are spread over about eighteen months, which is the rhythm of a library whose API has settled rather than one that is churning.

That fits the 2.x design. The 2.x line removed the custom id allocator and renamed a set of types, which is the kind of break you make once, after a major version, and then leave alone. The `CHANGELOG.md` in the repository is where the detail lives, and the README points to the documentation site for the API reference, generated with Doxygen from `docs/libcopp.doxyfile.in` and published with MkDocs from `docs/mkdocs.yml`.

The licence is MIT, with the text in the `LICENSE` file on the v2 branch, and the repository also carries a `BOOST_LICENSE_1_0.txt`, which is a reminder that the project has a relationship with the Boost ecosystem even where Boost.Test is only an optional test backend. The optional test frameworks are gtest 1.6.0 or above and Boost.Test, with `GTEST_ROOT` and `BOOST_ROOT` available as cmake variables to point at an installed prefix.

For an adopter, the practical picture is a stable 2.x line, a wide compiler matrix, and an upgrade that mostly consists of renaming things, with one structural change, the removal of the custom id allocator, that will need an afternoon of work rather than a minute.

Editorial conclusion

libcopp fits a C++ project that needs stackful coroutines with a runtime it controls, on a platform where the standard library offers none, and that wants the same source to build on Linux, Windows and macOS. Skip it if your codebase already targets C++20 and uses the built-in coroutine machinery, or if you want a library with no context switching layer of its own. Verify first which compiler and cmake floors your build satisfies, since the README states them in more than one place, and read the 2.x upgrade notes before porting code that used the 1.x type names.

Frequently asked questions

What is libcopp and what does it provide?

libcopp is a cross-platform C++ coroutine library written in C++, providing the `cotask::task<T>` coroutine type with its own context switching and stack management, and a build system with cmake options for segmented stacks, valgrind support, thread safety and Intel TSX.

What are the build requirements for libcopp?

The install prerequisites ask for a compiler supporting ISO C++ 11 or above, cmake 3.16.0 and above, binutils or llvm on Unix, and masm on Windows. gtest 1.6.0 or above or Boost.Test are optional for the test framework, and pthread is required when using gtest.

How do I build and test libcopp from a clone?

Clone the repository, create a build directory, configure with `cmake .. -DPROJECT_ENABLE_UNITTEST=YES -DPROJECT_ENABLE_SAMPLE=YES`, build with `cmake --build . --config RelWithDebInfo`, and then run the unit tests through ctest with the label `libcopp.unit_test`.

What breaks when upgrading libcopp from 1.x to 2.x?

The README lists the changes: add `using value_type = int;` into `T`, rename `stack_allocator_t` to `stack_allocator_type` and `coroutine_t` to `coroutine_type` inside `T`, move `libcopp::util::*` to `copp::util::`, and use `cotask::task<T>::ptr_type` instead of `libcopp::util::intrusive_ptr<cotask::impl::task_impl>`.

Why do I need LIBCOPP_MACRO_TLS_STACK_PROTECTOR?

The README says users need to set it to ON when compiling with `-fstack-protector`, because that flag changes the default context switching logic. The option defaults to off, so a project enabling stack protection has to switch it on explicitly.

Official sources

  1. Issues
  2. License: MIT
  3. owent/libcopp on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/owent-libcopp.svg)](https://hysenlabs.com/projects/owent-libcopp)

Was this useful?