Skip to content

Commit

Permalink
移除不必要的引用计数开销,上层调用者应该保证ptr不失效。并且task manager里也有相应的保证
Browse files Browse the repository at this point in the history
v2版本的REAMDE的相关sample
  • Loading branch information
owent committed Jun 12, 2017
1 parent 1f7e515 commit 932c4cc
Show file tree
Hide file tree
Showing 13 changed files with 1,166 additions and 923 deletions.
245 changes: 120 additions & 125 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/md/000_DESCRIPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ libcopp
> Build & Run Unit Test in | Linux+OSX(Clang+GCC) | Windows(VC+MinGW) |
> -------------------------|-----------------------|-------------|
> Status | [![Build Status](https://travis-ci.org/owt5008137/libcopp.svg?branch=master)](https://travis-ci.org/owt5008137/libcopp) | [![Build status](https://ci.appveyor.com/api/projects/status/7w6dfnpeahfmgaqj?svg=true)](https://ci.appveyor.com/project/owt5008137/libcopp) |
> Compilers | linux-gcc-4.4 <br /> linux-gcc-4.6 <br /> linux-gcc-4.9 <br /> linux-gcc-7 <br /> linux-clang-3.5 <br /> osx-apple-clang-6.0 <br /> | MSVC 12(Visual Studio 2013) <br /> MSVC 14(Visual Studio 2015) <br /> MinGW64-gcc
> Compilers | linux-gcc-4.4 <br /> linux-gcc-4.6 <br /> linux-gcc-4.9 <br /> linux-gcc-7 <br /> linux-clang-3.5 <br /> osx-apple-clang-6.0 <br /> | MSVC 12(Visual Studio 2013) <br /> MSVC 14(Visual Studio 2015) <br /> MSVC 15(Visual Studio 2017) <br /> MinGW64-gcc
>

Expand Down
239 changes: 118 additions & 121 deletions docs/md/020_USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,63 +7,54 @@ Get Start & Example
-------

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

```cpp
#include <cstdio>
#include <cstring>
#include <iostream>
#include <inttypes.h>
#include <iostream>
#include <stdint.h>

// include context header file
#include <libcopp/coroutine/coroutine_context_container.h>

// define a coroutine runner
class my_runner : public copp::detail::coroutine_runnable_base
{
public:
int operator()() {
// ... your code here ...printf("cortoutine %" PRIxPTR " exit and return %d.\n", (intptr_t)&co_obj, co_obj.get_ret_code());
copp::coroutine_context_default* addr = get_coroutine_context<copp::coroutine_context_default>();
std::cout<< "cortoutine "<< addr<< " is running."<< std::endl;

addr->yield();
std::cout<< "cortoutine "<< addr<< " is resumed."<< std::endl;

return 1;
}
};
int my_runner(void *) {
copp::coroutine_context *addr = copp::this_coroutine::get_coroutine();

int main() {
// create a coroutine
copp::coroutine_context_default co_obj;
std::cout<< "cortoutine "<< &co_obj<< " is created."<< std::endl;
std::cout << "cortoutine " << addr << " is running." << std::endl;

addr->yield();

// create a runner
my_runner runner;
std::cout << "cortoutine " << addr << " is resumed." << std::endl;

// bind runner to coroutine object
co_obj.create(&runner);
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();
co_obj->start();

// yield from runner
std::cout<< "cortoutine "<< &co_obj<< " is yield."<< std::endl;
co_obj.resume();
// 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;
std::cout << "cortoutine " << co_obj << " exit and return " << co_obj->get_ret_code() << "." << std::endl;
return 0;
}
```
And then, you can custom many function such as set your stack allocator, coroutine type and etc. by set your template parameters of coroutine context.
***Notice:*** *One coroutine runner can only below to one coroutine context*

Also, you can use copp::coroutine_context_container<ALLOCATOR> instead of copp::coroutine_context_default to use a different stack allocator.
### coroutine task example
There is a simple example of using coroutine task below:
This is a simple example of using coroutine task with lambda expression:
```cpp
#include <iostream>
Expand All @@ -73,45 +64,50 @@ There is a simple example of using coroutine task below:
typedef cotask::task<> my_task_t;
int main(int argc, char* argv[]) {
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]
my_task_t::prt_t task = my_task_t::create([](){
std::cout<< "task "<< cotask::this_task::get_task()->get_id()<< " started"<< std::endl;
my_task_t::ptr_t task = my_task_t::create([]() {
std::cout << "task " << cotask::this_task::get<my_task_t>()->get_id() << " started" << std::endl;
cotask::this_task::get_task()->yield();
std::cout<< "task "<< cotask::this_task::get_task()->get_id()<< " resumed"<< 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;
std::cout << "task " << task->get_id() << " created" << std::endl;
// start a task
task->start();
std::cout<< "task "<< task->get_id()<< " yield"<< std::endl;
std::cout << "task " << task->get_id() << " yield" << std::endl;
task->resume();
std::cout<< "task "<< task->get_id()<< " stoped, ready to be destroyed."<< std::endl;

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;
}
```
And then, you can custom many functions by set your macro type of coroutine and task to do some other function.
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
There is a simple example of using coroutine context below:
This is a simple example of using task manager:

```cpp
#include <cstdio>
#include <cstring>
#include <iostream>
#include <ctime>
#include <inttypes.h>
#include <iostream>
#include <stdint.h>
#include <ctime>

// include context header file
#include <libcotask/task.h>
#include <libcotask/task_manager.h>

// create a task manager
typedef cotask::task_manager<cotask::task<>::id_t> mgr_t;
typedef cotask::task<> my_task_t;
typedef my_task_t::ptr_t task_ptr_type;
typedef cotask::task_manager<my_task_t> mgr_t;
mgr_t::ptr_t task_mgr = mgr_t::create();

// If you task manager to manage timeout, it's important to call tick interval
Expand All @@ -122,72 +118,73 @@ void tick() {
}

int main() {
// type define
typedef std::shared_ptr<cotask::task<> > task_ptr_type;
#if defined(UTIL_CONFIG_COMPILER_CXX_LAMBDAS) && UTIL_CONFIG_COMPILER_CXX_LAMBDAS
// create two coroutine task
task_ptr_type co_task = cotask::task<>::create([](){
std::cout<< "task "<< cotask::this_task::get_task()->get_id()<< " started"<< std::endl;
task_ptr_type co_task = my_task_t::create([]() {
std::cout << "task " << cotask::this_task::get<my_task_t>()->get_id() << " started" << std::endl;
cotask::this_task::get_task()->yield();
std::cout<< "task "<< cotask::this_task::get_task()->get_id()<< " resumed"<< 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 = cotask::task<>::create([](){
std::cout<< "task "<< cotask::this_task::get_task()->get_id()<< " started"<< std::endl;
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;
cotask::this_task::get_task()->yield();
std::cout<< "task "<< cotask::this_task::get_task()->get_id()<< " resumed"<< 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;
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;
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;
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;
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;
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;
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;
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
There is a simple example of using coroutine context below:
This is a simple example of using stack pool for cotask:

```cpp
#include <cstdio>
#include <cstring>
#include <iostream>
#include <ctime>
#include <inttypes.h>
#include <iostream>
#include <stdint.h>
#include <ctime>

// include context header file
#include <libcopp/stack/stack_pool.h>
Expand All @@ -198,73 +195,73 @@ typedef copp::stack_pool<copp::allocator::default_statck_allocator> stack_pool_t

// define how to create coroutine context
struct sample_macro_coroutine {
typedef copp::detail::coroutine_context_base coroutine_t;
typedef copp::allocator::stack_allocator_pool<stack_pool_t> stack_allocator_t;

typedef copp::detail::coroutine_context_container<coroutine_t, stack_allocator_t> coroutine_container_t;
typedef copp::coroutine_context_container<stack_allocator_t> coroutine_t;
};

// create a stack pool
static stack_pool_t::ptr_t global_stack_pool = stack_pool_t::create();

// define how to task allocator
struct sample_task_allocator : public cotask::macro_task::task_allocator_t {
public:
#if defined(COPP_MACRO_ENABLE_VARIADIC_TEMPLATE) && COPP_MACRO_ENABLE_VARIADIC_TEMPLATE
template <typename Ty, typename... TARGS>
static std::shared_ptr<Ty> allocate(Ty *t, TARGS COPP_MACRO_RV_REF... args) {
std::shared_ptr<Ty> ret = cotask::macro_task::task_allocator_t::allocate(t, COPP_MACRO_STD_FORWARD(TARGS, args)...);
ret->get_coroutine_context().get_allocator().attach(global_stack_pool);
return COPP_MACRO_STD_MOVE(ret);
}
#else
template <typename Ty>
static std::shared_ptr<Ty> allocate(Ty *t) {
std::shared_ptr<Ty> ret = cotask::macro_task::task_allocator_t::allocate(t);
ret->get_coroutine_context().get_allocator().attach(global_stack_pool);
return COPP_MACRO_STD_MOVE(ret);
}
#endif
};

// define how to create coroutine task
struct sample_macro_task {
typedef cotask::macro_task::id_t id_t;
typedef cotask::macro_task::id_allocator_t id_allocator_t;

typedef cotask::macro_task::action_allocator_t action_allocator_t;
typedef sample_task_allocator task_allocator_t;
};

typedef cotask::task<sample_macro_coroutine, sample_macro_task> sample_task_t;
typedef cotask::task<sample_macro_coroutine> sample_task_t;

int main() {
// create two coroutine task
sample_task_t::ptr_t co_task = cotask::task<>::create([](){
std::cout<< "task "<< cotask::this_task::get_task()->get_id()<< " started"<< std::endl;
cotask::this_task::get_task()->yield();
std::cout<< "task "<< cotask::this_task::get_task()->get_id()<< " resumed"<< std::endl;
return 0;
});
sample_task_t::ptr_t co_another_task = cotask::task<>::create([](){
std::cout<< "task "<< cotask::this_task::get_task()->get_id()<< " started"<< std::endl;
cotask::this_task::get_task()->yield();
std::cout<< "task "<< cotask::this_task::get_task()->get_id()<< " resumed"<< std::endl;
return 0;
});
#if defined(UTIL_CONFIG_COMPILER_CXX_LAMBDAS) && UTIL_CONFIG_COMPILER_CXX_LAMBDAS

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;
// create two coroutine task
{
sample_task_t::ptr_t co_task = sample_task_t::create([]() {
std::cout << "task " << cotask::this_task::get<sample_task_t>()->get_id() << " started" << std::endl;
cotask::this_task::get_task()->yield();
std::cout << "task " << cotask::this_task::get<sample_task_t>()->get_id() << " resumed" << std::endl;
return 0;
});

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
}

if (!co_another_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;

{
sample_task_t::ptr_t co_another_task = sample_task_t::create([]() {
std::cout << "task " << cotask::this_task::get<sample_task_t>()->get_id() << " started" << std::endl;
cotask::this_task::get_task()->yield();
std::cout << "task " << cotask::this_task::get<sample_task_t>()->get_id() << " resumed" << std::endl;
return 0;
});

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
}

// ..., 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;
}
```

8 changes: 8 additions & 0 deletions docs/md/070_HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
HISTORY
========
2017-05-10
------
1. [BOOST] merge boost.context 1.64.0
2. [OPTIMIZE] add stack pool manager and unit test
3. [OPTIMIZE] reduce memory fragment when allocate coroutine task and task action
4. [CI] benchmark and sample will always be run in [Travis CI](https://travis-ci.org/owt5008137/libcopp) and [Appveyor CI](https://ci.appveyor.com/project/owt5008137/libcopp)


2016-06-16
------
1. [BOOST] merge boost.context 1.61.0 and use the new jump progress(see https://owent.net/90QQw for detail)
Expand Down
Loading

0 comments on commit 932c4cc

Please sign in to comment.