libcopp  1.1.0
USAGE

Using with cmake

  1. Add <WHERE to="" install="" libcopp>="">/lib(64)/cmake to any of CMAKE_PREFIX_PATHCMAKE_FRAMEWORK_PATHCMAKE_SYSTEM_PREFIX_PATHCMAKE_SYSTEM_FRAMEWORK_PATH
  2. Just add find_package(Libcopp) to use libcopp module.
    1 find_package(Libcopp CONFIG REQUIRED)
    2 target_include_directories(main PRIVATE ${Libcopp_INCLUDE_DIRS})
    3 target_link_libraries(main PRIVATE ${Libcotask_LIBRARIES} ${Libcopp_LIBRARIES})

See more detail on https://github.com/Microsoft/vcpkg/tree/master/ports/libcopp .

Directly use headers and libraries

Just include headers and linking library file of your platform to use libcopp.

1 LIBCOPP_PREFIX=<WHERE TO INSTALL libcopp>
2 
3 # Example command for build sample with gcc 4.9 or upper on Linux
4 for source in sample_readme_*.cpp; do
5  g++ -std=c++14 -O2 -g -ggdb -Wall -Werror -fPIC -rdynamic -fdiagnostics-color=auto -Wno-unused-local-typedefs \
6  -I$LIBCOPP_PREFIX/include -L$LIBCOPP_PREFIX/lib64 -lcopp -lcotask $source -o $source.exe;
7 done
8 
9 # Example command for build sample with clang 3.9 or upper and libc++ on Linux
10 for source in sample_readme_*.cpp; do
11  clang++ -std=c++17 -stdlib=libc++ -O2 -g -ggdb -Wall -Werror -fPIC -rdynamic \
12  -I$LIBCOPP_PREFIX/include -L$LIBCOPP_PREFIX/lib64 -lcopp -lcotask -lc++ -lc++abi \
13  $source -o $source.exe;
14 done
15 
16 # AppleClang on macOS just like those scripts upper.
17 # If you are using MinGW on Windows, it's better to add -static-libstdc++ -static-libgcc to
18 # use static linking and other scripts are just like those on Linux.
1 # Example command for build sample with MSVC 1914 or upper on Windows & powershell(Debug Mode /MDd)
2 foreach ($source in Get-ChildItem -File -Name .\sample_readme_*.cpp) {
3  cl /nologo /MP /W4 /wd"4100" /wd"4125" /EHsc /std:c++17 /Zc:__cplusplus /O2 /MDd /I$LIBCOPP_PREFIX/include $LIBCOPP_PREFIX/lib64/copp.lib $LIBCOPP_PREFIX/lib64/cotask.lib $source
4 }

Get Start & Example

coroutine_context example

This is a simple example of using basic coroutine context below:

// see https://github.com/owt5008137/libcopp/blob/v2/sample/sample_readme_1.cpp
#include <cstdio>
#include <cstring>
#include <inttypes.h>
#include <iostream>
#include <stdint.h>
// include context header file
// define a coroutine runner
int my_runner(void *) {
std::cout << "cortoutine " << addr << " is running." << std::endl;
addr->yield();
std::cout << "cortoutine " << addr << " is resumed." << std::endl;
return 1;
}
int main() {
typedef copp::coroutine_context_default coroutine_t;
// create a coroutine
copp::coroutine_context_default::ptr_t co_obj = coroutine_t::create(my_runner);
std::cout << "cortoutine " << co_obj << " is created." << std::endl;
// start a coroutine
co_obj->start();
// yield from my_runner
std::cout << "cortoutine " << co_obj << " is yield." << std::endl;
co_obj->resume();
std::cout << "cortoutine " << co_obj << " exit and return " << co_obj->get_ret_code() << "." << std::endl;
return 0;
}

Also, you can use copp::coroutine_context_container<ALLOCATOR> instead of copp::coroutine_context_default to use a different stack allocator.

coroutine task example

This is a simple example of using coroutine task with lambda expression:

// see https://github.com/owt5008137/libcopp/blob/v2/sample/sample_readme_2.cpp
#include <iostream>
// include task header file
#include <libcotask/task.h>
int main(int argc, char *argv[]) {
#if defined(UTIL_CONFIG_COMPILER_CXX_LAMBDAS) && UTIL_CONFIG_COMPILER_CXX_LAMBDAS
// create a task using factory function [with lambda expression]
std::cout << "task " << cotask::this_task::get<my_task_t>()->get_id() << " started" << std::endl;
std::cout << "task " << cotask::this_task::get<my_task_t>()->get_id() << " resumed" << std::endl;
return 0;
});
std::cout << "task " << task->get_id() << " created" << std::endl;
// start a task
task->start();
std::cout << "task " << task->get_id() << " yield" << std::endl;
task->resume();
std::cout << "task " << task->get_id() << " stoped, ready to be destroyed." << std::endl;
#else
std::cerr << "lambda not supported, this sample is not available." << std::endl;
#endif
return 0;
}

Also, you can your stack allocator or id allocator by setting different parameters in template class cotask::task<TCO_MACRO, TTASK_MACRO>

using coroutine task manager

This is a simple example of using task manager:

// see https://github.com/owt5008137/libcopp/blob/v2/sample/sample_readme_3.cpp
#include <cstdio>
#include <cstring>
#include <ctime>
#include <inttypes.h>
#include <iostream>
#include <stdint.h>
// include context header file
#include <libcotask/task.h>
// create a task manager
// If you task manager to manage timeout, it's important to call tick interval
void tick() {
// the first parameter is second, and the second is nanosecond
task_mgr->tick(time(NULL), 0);
}
int main() {
#if defined(UTIL_CONFIG_COMPILER_CXX_LAMBDAS) && UTIL_CONFIG_COMPILER_CXX_LAMBDAS
// create two coroutine task
task_ptr_type co_task = my_task_t::create([]() {
std::cout << "task " << cotask::this_task::get<my_task_t>()->get_id() << " started" << std::endl;
std::cout << "task " << cotask::this_task::get<my_task_t>()->get_id() << " resumed" << std::endl;
return 0;
});
task_ptr_type co_another_task = my_task_t::create([]() {
std::cout << "task " << cotask::this_task::get<my_task_t>()->get_id() << " started" << std::endl;
std::cout << "task " << cotask::this_task::get<my_task_t>()->get_id() << " resumed" << std::endl;
return 0;
});
int res = task_mgr->add_task(co_task, 5, 0); // add task and setup 5s for timeout
if (res < 0) {
std::cerr << "some error: " << res << std::endl;
return res;
}
res = task_mgr->add_task(co_another_task); // add task without timeout
if (res < 0) {
std::cerr << "some error: " << res << std::endl;
return res;
}
res = task_mgr->start(co_task->get_id());
if (res < 0) {
std::cerr << "start task " << co_task->get_id() << " failed, error code: " << res << std::endl;
}
res = task_mgr->start(co_another_task->get_id());
if (res < 0) {
std::cerr << "start task " << co_another_task->get_id() << " failed, error code: " << res << std::endl;
}
res = task_mgr->resume(co_task->get_id());
if (res < 0) {
std::cerr << "resume task " << co_task->get_id() << " failed, error code: " << res << std::endl;
}
res = task_mgr->kill(co_another_task->get_id());
if (res < 0) {
std::cerr << "kill task " << co_another_task->get_id() << " failed, error code: " << res << std::endl;
} else {
std::cout << "kill task " << co_another_task->get_id() << " finished." << std::endl;
}
#else
std::cerr << "lambda not supported, this sample is not available." << std::endl;
#endif
return 0;
}

using stack pool

This is a simple example of using stack pool for cotask:

// see https://github.com/owt5008137/libcopp/blob/v2/sample/sample_readme_4.cpp
#include <cstdio>
#include <cstring>
#include <ctime>
#include <inttypes.h>
#include <iostream>
#include <stdint.h>
// include context header file
#include <libcotask/task.h>
// define the stack pool type
// define how to create coroutine context
};
// create a stack pool
int main() {
#if defined(UTIL_CONFIG_COMPILER_CXX_LAMBDAS) && UTIL_CONFIG_COMPILER_CXX_LAMBDAS
global_stack_pool->set_min_stack_number(4);
std::cout << "stack pool=> used stack number: " << global_stack_pool->get_limit().used_stack_number
<< ", used stack size: " << global_stack_pool->get_limit().used_stack_size
<< ", free stack number: " << global_stack_pool->get_limit().free_stack_number
<< ", free stack size: " << global_stack_pool->get_limit().free_stack_size << std::endl;
// create two coroutine task
{
[]() {
std::cout << "task " << cotask::this_task::get<sample_task_t>()->get_id() << " started" << std::endl;
std::cout << "task " << cotask::this_task::get<sample_task_t>()->get_id() << " resumed" << std::endl;
return 0;
},
alloc);
if (!co_task) {
std::cerr << "create coroutine task with stack pool failed" << std::endl;
return 0;
}
std::cout << "stack pool=> used stack number: " << global_stack_pool->get_limit().used_stack_number
<< ", used stack size: " << global_stack_pool->get_limit().used_stack_size
<< ", free stack number: " << global_stack_pool->get_limit().free_stack_number
<< ", free stack size: " << global_stack_pool->get_limit().free_stack_size << std::endl;
// ..., then do anything you want to do with these tasks
}
std::cout << "stack pool=> used stack number: " << global_stack_pool->get_limit().used_stack_number
<< ", used stack size: " << global_stack_pool->get_limit().used_stack_size
<< ", free stack number: " << global_stack_pool->get_limit().free_stack_number
<< ", free stack size: " << global_stack_pool->get_limit().free_stack_size << std::endl;
{
[]() {
std::cout << "task " << cotask::this_task::get<sample_task_t>()->get_id() << " started" << std::endl;
std::cout << "task " << cotask::this_task::get<sample_task_t>()->get_id() << " resumed" << std::endl;
return 0;
},
alloc);
if (!co_another_task) {
std::cerr << "create coroutine task with stack pool failed" << std::endl;
return 0;
}
// ..., then do anything you want to do with these tasks
}
std::cout << "stack pool=> used stack number: " << global_stack_pool->get_limit().used_stack_number
<< ", used stack size: " << global_stack_pool->get_limit().used_stack_size
<< ", free stack number: " << global_stack_pool->get_limit().free_stack_number
<< ", free stack size: " << global_stack_pool->get_limit().free_stack_size << std::endl;
#else
std::cerr << "lambda not supported, this sample is not available." << std::endl;
#endif
return 0;
}

using then or await

This is a simple example of using then and await for cotask:

#include <assert.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <inttypes.h>
#include <stdint.h>
#include <vector>
// include manager header file
#include <libcotask/task.h>
#if defined(LIBCOTASK_MACRO_ENABLED) && defined(UTIL_CONFIG_COMPILER_CXX_LAMBDAS) && UTIL_CONFIG_COMPILER_CXX_LAMBDAS
int main(int argc, char *argv[]) {
int test_code = 128;
// create a task using lambda expression
my_task_t::ptr_t first_task = my_task_t::create([&]() {
puts("|first task running and will be yield ...");
puts("|first task resumed ...");
printf("test code already reset => %d\n", ++test_code);
});
// add many then task using lambda expression
first_task
->then([=]() {
puts("|second task running...");
printf("test code should be inited 128 => %d\n", test_code);
})
->then([&]() {
puts("|haha ... this is the third task.");
printf("test code is the same => %d\n", ++test_code);
return "return value will be ignored";
})
->then(
[&](void *priv_data) {
puts("|it's boring");
printf("test code is %d\n", ++test_code);
assert(&test_code == priv_data);
return 0;
},
&test_code);
test_code = 0;
// start a task
first_task->start();
first_task->resume();
// these code below will failed.
first_task->then([]() {
puts("this will run immediately.");
return 0;
});
my_task_t::ptr_t await_task = my_task_t::create([&]() {
puts("await for first_task.");
return 0;
});
await_task->await(first_task);
printf("|task start twice will failed: %d\n", first_task->start());
printf("|test_code end with %d\n", test_code);
return 0;
}
#else
int main() {
puts("this sample require cotask enabled and compiler support c++11");
return 0;
}
#endif