A go-style coroutine library in C++11 and more.

cocoyaxi

English | 简体中文

Linux Build Windows Build Mac Build Release License: MIT

A go-style coroutine library in C++11 and more.

0. Introduction

cocoyaxi (co for short), is an elegant and efficient cross-platform C++ base library. It contains a series of high-quality base components, such as go-style coroutine, coroutine-based network programming framework, command line and config file parser, high-performance log library, unit testing framework, JSON library, etc.

It was said that about 23 light-years from the Earth, there is a planet named Namake. Namake has three suns, a large one and two small ones. The Namakians make a living by programming. They divide themselves into nine levels according to their programming level, and the three lowest levels will be sent to other planets to develop programming technology. These wandering Namakians must collect at least 10,000 stars through a project before they can return to Namake.

Several years ago, two Namakians, ruki and alvin, were dispatched to the Earth. In order to go back to the Namake planet as soon as possible, ruki has developed a nice build tool xmake, whose name is taken from Namake. At the same time, alvin has developed a go-style C++ coroutine library cocoyaxi, whose name is taken from the Cocoyaxi village where ruki and alvin live on Namake.

1. Sponsor

cocoyaxi needs your help. If you are using it or like it, you may consider becoming a sponsor. Thank you very much!

Special Sponsors

cocoyaxi is specially sponsored by the following companies, thank you very much!

2. Documents

3. Core features

3.1 Coroutine

co has implemented a go-style coroutine, which has the following features:

  • Multi-thread scheduling, the default number of threads is the number of system CPU cores.
  • Shared stack, coroutines in the same thread share several stacks (the default size is 1MB), and the memory usage is low. Simple test on Linux shows that 10 millions of coroutines only take 2.8G of memory (for reference only).
  • There is a flat relationship between coroutines, and new coroutines can be created from anywhere (including in coroutines).
  • Support system API hook (Windows/Linux/Mac), you can directly use third-party network library in coroutine.
  • Coroutineized socket API.
  • Coroutine synchronization event co::Event.
  • Coroutine lock co::Mutex.
  • Coroutine pool co::Pool.
  • channel co::Chan.
  • waitgroup co::WaitGroup.

3.1.1 Create a coroutine

go(ku);           // void ku();
go(f, 7);         // void f(int);
go(&T::f, &o);    // void T::f(); T o;
go(&T::f, &o, 7); // void T::f(int); T o;
go([](){
    LOG << "hello go";
});

The above is an example of creating coroutines with go(). go() is a function that accepts 1 to 3 parameters. The first parameter f is any callable object, as long as f(), (*f)(), f(p), (*f)(p), (o->*f)() or (o->*f)(p) can be called.

The coroutines created by go() will be evenly distributed to different scheduling threads. If you want to create coroutines in specified scheduling thread, you can create coroutines in the following way:

auto s = co::next_scheduler();
s->go(f1);
s->go(f2);

If users want to create coroutine in all scheduling threads, the following way can be used:

auto& s = co::all_schedulers();
for (size_t i = 0; i < s.size(); ++i) {
    s[i]->go(f);
}

3.1.2 channel

co::Chan, similar to the channel in golang, can be used to transfer data between coroutines.

#include "co/co.h"

DEF_main(argc, argv) {
    co::Chan<int> ch;
    go([ch]() { /* capture by value, rather than reference */
        ch << 7;
    });

    int v = 0;
    ch >> v;
    LOG << "v: " << v;

    return 0;
}

When creating a channel, we can add a timeout as follows:

co::Chan<int> ch(8, 1000);

After read or write operation, we can call co::timeout() to determine whether it has timed out. This method is simpler than the select-based implementation in golang. For detailed usage, see Document of co::Chan.

3.1.3 waitgroup

co::WaitGroup, similar to sync.WaitGroup in golang, can be used to wait for the exit of coroutines or threads.

#include "co/co.h"

DEF_main(argc, argv) {
    FLG_cout = true;

    co::WaitGroup wg;
    wg.add(8);

    for (int i = 0; i < 8; ++i) {
        go([wg]() {
            LOG << "co: " << co::coroutine_id();
            wg.done();
        });
    }

    wg.wait();
    return 0;
}

3.2 network programming

co provides a set of coroutineized socket APIs, most of them are consistent with the native socket APIs in form, with which, you can easily write high-performance network programs in a synchronous manner.

In addition, co has also implemented higher-level network programming components, including TCP, HTTP and RPC framework based on JSON, they are IPv6-compatible and support SSL at the same time, which is more convenient than socket APIs. Here is just a brief demonstration of the usage of HTTP, and the rest can be seen in the documents.

3.2.1 Static web server

#include "co/flag.h"
#include "co/log.h"
#include "co/so.h"

DEF_string(d, ".", "root dir"); // Specify the root directory of the web server

int main(int argc, char** argv) {
    flag::init(argc, argv);
    log::init();

    so::easy(FLG_d.c_str()); // mum never have to worry again

    return 0;
}

3.2.2 HTTP server

http::Server serv;

serv.on_req(
    [](const http::Req& req, http::Res& res) {
        if (req.is_method_get()) {
            if (req.url() == "/hello") {
                res.set_status(200);
                res.set_body("hello world");
            } else {
                res.set_status(404);
            }
        } else {
            res.set_status(405); // method not allowed
        }
    }
);

serv.start("0.0.0.0", 80);                                    // http
serv.start("0.0.0.0", 443, "privkey.pem", "certificate.pem"); // https

3.2.3 HTTP client

void f() {
    http::Client c("https://github.com");

    c.get("/");
    LOG << "response code: "<< c.response_code();
    LOG << "body size: "<< c.body_size();
    LOG << "Content-Length: "<< c.header("Content-Length");
    LOG << c.header();

    c.post("/hello", "data xxx");
    LOG << "response code: "<< c.response_code();
}

go(f);

3.3 co/flag

co/flag is a command line and config file parser similar to google gflags, but more simple and easier to use. Some components in co use it to define config items.

co/flag provides a default value for each config item. Without config parameters, the program can run with the default config. Users can also pass in config parameters from command line or config file. When a config file is needed, users can run ./exe -mkconf to generate a config file.

// xx.cc
#include "co/flag.h"
#include "co/cout.h"

DEF_bool(x, false, "bool x");
DEF_bool(y, false, "bool y");
DEF_uint32(u32, 0, "...");
DEF_string(s, "hello world", "string");

int main(int argc, char** argv) {
    flag::init(argc, argv);

    COUT << "x: "<< FLG_x;
    COUT << "y: "<< FLG_y;
    COUT << "u32: "<< FLG_u32;
    COUT << FLG_s << "|" << FLG_s.size();

    return 0;
}

The above is an example of using co/flag. The macro at the beginning of DEF_ in the code defines 4 config items. Each config item is equivalent to a global variable. The variable name is FLG_ plus the config name. After the above code is compiled, it can be run as follows:

./xx                  # Run with default configs
./xx -xy -s good      # single letter named bool flags, can be set to true together
./xx -s "I'm ok"      # string with spaces
./xx -u32 8k          # Integers can have units: k,m,g,t,p, not case sensitive

./xx -mkconf          # Automatically generate a config file: xx.conf
./xx xx.conf          # run with a config file
./xx -config xx.conf  # Same as above

3.4 co/log

co/log is a high-performance and memory-friendly local log library, which nearly needs no memory allocation. Some components in co will use it to print logs.

co/log divides the log into five levels: debug, info, warning, error, and fatal. Printing a fatal level log will terminate the program. Users can print logs of different levels as follows:

DLOG << "hello " << 23; // debug
LOG << "hello " << 23;  // info
WLOG << "hello " << 23; // warning
ELOG << "hello " << 23; // error
FLOG << "hello " << 23; // fatal

co/log also provides a series of CHECK macros, which can be regarded as an enhanced version of assert, and they will not be cleared in debug mode.

void* p = malloc(32);
CHECK(p != NULL) << "malloc failed..";
CHECK_NE(p, NULL) << "malloc failed..";

When the CHECK assertion failed, co/log will print the function call stack information, and then terminate the program. On linux and macosx, make sure you have installed libbacktrace on your system.

stack

co/log is very fast. The following are some test results, for reference only:

  • co/log vs glog (single thread)

    platform google glog co/log
    win2012 HHD 1.6MB/s 180MB/s
    win10 SSD 3.7MB/s 560MB/s
    mac SSD 17MB/s 450MB/s
    linux SSD 54MB/s 1023MB/s
  • co/log vs spdlog (Windows)

    threads total logs co/log time(seconds) spdlog time(seconds)
    1 1000000 0.103619 0.482525
    2 1000000 0.202246 0.565262
    4 1000000 0.330694 0.722709
    8 1000000 0.386760 1.322471
  • co/log vs spdlog (Linux)

    threads total logs co/log time(seconds) spdlog time(seconds)
    1 1000000 0.096445 2.006087
    2 1000000 0.142160 3.276006
    4 1000000 0.181407 4.339714
    8 1000000 0.303968 4.700860

3.5 co/unitest

co/unitest is a simple and easy-to-use unit test framework. Many components in co use it to write unit test code, which guarantees the stability of co.

#include "co/unitest.h"
#include "co/os.h"

namespace test {
    
DEF_test(os) {
    DEF_case(homedir) {
        EXPECT_NE(os::homedir(), "");
    }

    DEF_case(cpunum) {
        EXPECT_GT(os::cpunum(), 0);
    }
}
    
} // namespace test

The above is a simple example. The DEF_test macro defines a test unit, which is actually a function (a method in a class). The DEF_case macro defines test cases, and each test case is actually a code block. Multiple test units can be put in the same C++ project, the main function is simple as below:

#include "co/unitest.h"

int main(int argc, char** argv) {
    flag::init(argc, argv);
    unitest::run_all_tests();
    return 0;
}

unitest contains the unit test code in cocoyaxi. Users can run unitest with the following commands:

xmake r unitest -a   # Run all test cases
xmake r unitest -os  # Run test cases in the os unit

4. Code composition

  • include

    Header files of co.

  • src

    Source files of co, built as libco.

  • test

    Some test code, each .cc file will be compiled into a separate test program.

  • unitest

    Some unit test code, each .cc file corresponds to a different test unit, and all code will be compiled into a single test program.

  • gen

    A code generator for the RPC framework.

5. Building

5.1 Compilers required

To build co, you need a compiler that supports C++11:

5.2 Build with xmake

co recommends using xmake as the build tool.

5.2.1 Quick start

# All commands are executed in the root directory of co (the same below)
xmake      # build libco by default
xmake -a   # build all projects (libco, gen, test, unitest)

5.2.2 Build shared library

xmake f -k shared
xmake -v

5.2.3 Build with mingw on Windows

xmake f -p mingw
xmake -v

5.2.4 Enable HTTP & SSL features

xmake f --with_libcurl=true --with_openssl=true
xmake -a

5.2.5 Install libco

# Install header files and libco by default.
xmake install -o pkg         # package related files to the pkg directory
xmake i -o pkg               # the same as above
xmake install -o /usr/local  # install to the /usr/local directory

5.2.6 Install libco from xmake repo

xrepo install -f "openssl=true,libcurl=true" cocoyaxi

5.3 Build with cmake

izhengfan helped to provide cmake support:

5.3.1 Build libco by default

mkdir build && cd build
cmake ..
make -j8

5.3.2 Build all projects

mkdir build && cd build
cmake .. -DBUILD_ALL=ON
make -j8

5.3.3 Enable HTTP & SSL features

mkdir build && cd build
cmake .. -DBUILD_ALL=ON -DCMAKE_INSTALL_PREFIX=/usr/local
make -j8
make install

5.3.4 Build shared library

cmake .. -DBUILD_SHARED_LIBS=ON

5.3.5 Install libco from vcpkg

vcpkg install cocoyaxi:x64-windows

# HTTP & SSL support
vcpkg install cocoyaxi[libcurl,openssl]:x64-windows

5.3.6 Install libco from conan

conan install cocoyaxi

6. License

The MIT license. cocoyaxi contains codes from some other projects, which have their own licenses, see details in LICENSE.md.

7. Special thanks

  • The code of co/context is from tbox by ruki, special thanks!
  • The early English documents of co are translated by Leedehai and daidai21, special thanks!
  • ruki has helped to improve the xmake building scripts, thanks in particular!
  • izhengfan provided cmake building scripts, thank you very much!
Owner
Alvin
Hi, I'm Alvin. I'm working on a go-style C++ coroutine library cocoyaxi.
Alvin
Comments
  • It's possible to coroutinize blocking sockets (?)

    It's possible to coroutinize blocking sockets (?)

    co_recv() is taken from co's doc and simplified for system sockets. It requires the socket to be set in non-blocking mode. co_recv_r() is basically co_recv() but with read/wait ordering switched. And it works on both blocking and non-blocking sockets?

    // doc's approach
    // https://idealvin.github.io/en/co/coroutine/#coroutineization
    int co_recv(int socket, void* buf, int n, int ms = 10000)
    {
        do
        {
            /* ============ READ FIRST ============ */
            if (const auto r = co::recv(socket, buf, n); r > 0)
                return r;
            else if (r == 0)
                return 0;
    
            /* ============ WAIT AFTER ============ */
            co::IoEvent ev(socket, co::ev_read);
            if (!ev.wait(ms))
                return -1;
        }
        while (true);
    }
    // Supports both blocking and non-blocking sockets, but are there pitfalls?
    int co_recv_r(int socket, void* buf, int n, int ms = 10000)
    {
        do
        {
            /* ============ WAIT FIRST ============ */
            co::IoEvent ev(socket, co::ev_read);
            if (!ev.wait(ms))
                return -1;
    
            /* ============ READ AFTER ============ */
            if (const auto r = co::recv(socket, buf, n); r > 0)
                return r;
            else if (r == 0)
                return 0;
        }
        while (true);
    }
    

    Full code for a simple test:

    #include "co/all.h"
    
    // doc's approach
    // https://idealvin.github.io/en/co/coroutine/#coroutineization
    int co_recv(int socket, void* buf, int n, int ms = 10000)
    {
        do
        {
            /* ============ READ FIRST ============ */
            if (const auto r = co::recv(socket, buf, n); r > 0)
                return r;
            else if (r == 0)
                return 0;
    
            /* ============ WAIT AFTER ============ */
            co::IoEvent ev(socket, co::ev_read);
            if (!ev.wait(ms))
                return -1;
        }
        while (true);
    }
    // Supports both blocking and non-blocking sockets, but are there pitfalls?
    int co_recv_r(int socket, void* buf, int n, int ms = 10000)
    {
        do
        {
            /* ============ WAIT FIRST ============ */
            co::IoEvent ev(socket, co::ev_read);
            if (!ev.wait(ms))
                return -1;
    
            /* ============ READ AFTER ============ */
            if (const auto r = co::recv(socket, buf, n); r > 0)
                return r;
            else if (r == 0)
                return 0;
        }
        while (true);
    }
    void handle1(tcp::Connection* conn)
    {
        std::cout << std::time(nullptr) << ' ' << __PRETTY_FUNCTION__ << " Start " << std::endl;
        uint8_t buffer[256]{};
        // while (const auto bytes = co_recv(conn->socket(), buffer, 256))
        while (const auto bytes = co_recv_r(conn->socket(), buffer, 256))
            conn->send(buffer, bytes);
        delete conn;
        std::cout << std::time(nullptr) << ' ' << __PRETTY_FUNCTION__ << " End " << std::endl;
    }
    void handle2(tcp::Connection*)
    {
        std::cout << std::time(nullptr) << ' ' << __PRETTY_FUNCTION__ << std::endl;
    }
    void on_connection(tcp::Connection* conn)
    {
        fcntl(conn->socket(), F_SETFL, fcntl(conn->socket(), F_GETFL) & ~O_NONBLOCK);
        if (fcntl(conn->socket(), F_GETFL) & O_NONBLOCK)
            throw std::runtime_error(std::string(__PRETTY_FUNCTION__) + ": the socket is still in non-blocking mode. ");
        auto* s = co::next_scheduler();
        s->go(handle1, conn);
        s->go(handle2, conn);
    }
    void client()
    {
        tcp::Client c("127.1", 8899);
        if (!c.connect(3000))
            throw std::runtime_error(std::string(__PRETTY_FUNCTION__) + ": failed to connect. ");
        for (int i = 0; i < 3; ++i)
        {
            c.send("hello", 5);
            char buffer[6]{};
            if (c.recv(buffer, 5))
            {
                //std::cout << __PRETTY_FUNCTION__ << ": " << buffer << std::endl;
            }
            sleep::sec(1);
        }
    }
    int main(int, char**)
    {
        tcp::Server serv;
        serv.on_connection(on_connection);
        serv.start("0.0.0.0", 8899);
    
        sleep::ms(200);
        go(client);
    
        while (true)
            sleep::sec(1024);
    }
    
    

    With co_recv_r() handle2() will be executed between handle1()'s beginning and ending, no matter whether the socket's blocking flag is set or not. But with co_recv() it only happens if the socket is set in non-blocking.

    I wonder if there is any pitfall in this code? Or is there serious performance penalty using co::IoEvent? Why isn't this the example in documents?

    PS: I have zero knowledge about SSL sockets, not clue if we have to do SSL_read() before SSL_get_error(). I only tested with POCO's SecureStreamSocket and it worked as well.

  • cannot build with msvc x86

    cannot build with msvc x86

    Under co/base/:

    $ xmake f -a x86
    checking for the Microsoft Visual Studio (x86) version ... 2019
    
    $ xmake
    [ 23%]: compiling.release co\impl\epoll.cc
    [ 26%]: compiling.release co\impl\co_win.cpp
    [ 29%]: compiling.release stack_trace\stack_trace_win.cpp
    [ 33%]: compiling.release win\time.cpp
    [ 39%]: compiling.release fast.cc
    [  6%]: compiling.release fastring.cc
    [  9%]: compiling.release hash\base64.cc
    [ 16%]: compiling.release co\impl\co_unix.cc
    [ 19%]: compiling.release json.cc
    [ 42%]: compiling.release co\impl\scheduler.cc
    [ 46%]: compiling.release unix\time.cc
    [ 49%]: compiling.release win\os.cpp
    [ 52%]: compiling.release str.cc
    error: time.cpp
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(46): error C3861: '_InterlockedIncrement64': identifier not found
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(62): error C3861: '_InterlockedDecrement64': identifier not found
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(78): error C3861: '_InterlockedExchangeAdd64': identifier not found
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(94): error C3861: '_InterlockedExchangeAdd64': identifier not found
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(110): error C3861: '_InterlockedExchangeAdd64': identifier not found
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(126): error C3861: '_InterlockedExchangeAdd64': identifier not found
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(142): error C3861: '_InterlockedExchangeAdd64': identifier not found
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(158): error C3861: '_InterlockedExchangeAdd64': identifier not found
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(174): error C3861: '_InterlockedOr64': identifier not found
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(190): error C3861: '_InterlockedAnd64': identifier not found
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(206): error C3861: '_InterlockedXor64': identifier not found
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(222): error C3861: '_InterlockedOr64': identifier not found
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(238): error C3861: '_InterlockedAnd64': identifier not found
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(254): error C3861: '_InterlockedXor64': identifier not found
    C:\Users\fzheng\source\repos\co\base\win/atomic.h(270): error C3861: '_InterlockedExchange64': identifier not found
    
  • 是否有意向将install后的库名以及目录名由base改为co?

    是否有意向将install后的库名以及目录名由base改为co?

    目前执行xmake install后,会将头文件放在base目录下,静态库被命名为libbase.abase这个名字太普通了,不够将库与程序内部的名字区分开。既然项目名叫co,何不将头文件放在include/co目录下,且库名为libco.alibco.so

    这个动静有点大,但从长远来看,我觉得有必要来执行这个改动。

  • macOS 平台 动态库hook不生效

    macOS 平台 动态库hook不生效

    目前的http::Client使用curl_easy_perform,这是一个阻塞接口。 使用单个协程调度器时,http请求将阻塞其他协程。

    http::Client应该设计成非阻塞协程的,这才能体现协程的优势。推广到其他API,作为协程库里的实现,应当都设计为非阻塞的~

    PS:

    1. 非阻塞可通过curl_multi_perform+辅助线程/协程(也许),或者有更好的办法(基于curl)?
    2. 很多测试貌似应该用单个协程调度器,比较容易发现阻塞的问题。
  • vcpkg conan 都找不到coost这个包,xmake编译出来也用不了

    vcpkg conan 都找不到coost这个包,xmake编译出来也用不了

    如题,我看readme里面写了这几种使用方式

    vcpkg search coost
    conan search coost -r all
    

    都找不到这个包

    另外,xmake.lua 直接引入coost后,

    add_requires("fmt", "coost", "conan::pugixml/1.12.1")
    add_packages("fmt", "coost", "conan::pugixml/1.12.1")
    

    在 ubuntu 编译示例代码也会报错

    #include "co/all.h"
    
    DEF_string(s, "nice", "");
    
    int main(int argc, char** argv) {
        flag::init(argc, argv);
        LOG << FLG_s;
        return 0;
    }
    

    会产生下面的错误

    [email protected] /m/c/m/c/cpp_practical_util> x
    [ 88%]: linking.debug test
    error: /usr/bin/ld: /home/cat/.xmake/packages/c/coost/v3.0.0/e7a64508b634445f93e6b7f099ab5e52/lib/libco.a(stack_trace.cc.o): in function `___::log::StackTraceImpl::dump_stack(void*, int)':
    stack_trace.cc:(.text+0xb8): undefined reference to `backtrace_create_state'
    /usr/bin/ld: stack_trace.cc:(.text+0xcf): undefined reference to `backtrace_full'
    collect2: error: ld returned 1 exit status
    
  • [help] co::go(): any easy and efficient way to send notices to all schedulers and/or all coroutines?

    [help] co::go(): any easy and efficient way to send notices to all schedulers and/or all coroutines?

    i have a program like this

    server.listen();
    while (server.poll())
        go([socket = socket.acceptConnection()]() mutable {
            char bytes[1024];
            while (int size = socket.receive(bytes, 1024))
            {
                // do something and write to socket
            }
        });
    

    this works perfectly for request-response pattern. but in situations where server push is also desired (like in subscribe-publish pattern, but work with request-response at the same time. ), what can i do if i dont wanna open up another connection and do different things? i dont think frequently sending "check" commands to server is a good idea.

    ps: im not using so for networking becuz it doesnt have built-in ssl support (yet).

  • unitest xmake错误:error: unknown target(base) for unitest.deps!

    unitest xmake错误:error: unknown target(base) for unitest.deps!

    我根据文档上的xmake编译步骤,编译co/unitest时报错 checking for the architecture ... x64 checking for the Microsoft Visual Studio (x64) version ... 2017 error: unknown target(base) for unitest.deps! 该如何解决

  • libcurl 版本冲突导致 http get 没有发送请求

    libcurl 版本冲突导致 http get 没有发送请求

        string URL = "http://github.com";
        auto req_lambda3 = [](shared_ptr<LambdaParams2> p)
        {
            http::Client c("http://github.com"); // https client to github.com:443
            std::cout << "1--nothing---------" << std::endl;
            c.get("/");
            std::cout << c.body() << c.strerror() << "2--nothing---------" << std::endl;
            std::cout << "3--nothing---------" << std::endl;
        };
        co::go(&req_lambda3, make_shared<LambdaParams2>(make_shared<http::Client>(URL.c_str()), make_shared<string>("asdk")));
        std::cout << "4--nothing---------" << std::endl;
        std::this_thread::sleep_for(10000ms);
    

    输出 4--nothing--------- │ 1--nothing---------

    抓包没有发送请求

    预期 应当 get 成功发送请求

    环境

    ubuntu18 amd64. g++ 5.5. c++14. libcurl 7.73.0.

  • arm7协程中opencv resize触发Bus error

    arm7协程中opencv resize触发Bus error

    #include <opencv2/imgproc.hpp>
    #include <opencv2/imgcodecs.hpp>
    #include <co/co.h>
    #include <co/log.h>
    
    DEC_bool(cout);
    
    int main(int argc, char** argv)
    {
        co::init();
        log::init();
    
        FLG_cout = true;
    
        co::WaitGroup wg;
    
        auto mat = cv::imread("./bar.jpeg", cv::IMREAD_UNCHANGED);
        cv::Mat tmp;
        cv::resize(mat, tmp, cv::Size(100, 100));
    
        LOG << "resize " << tmp.cols << " " << tmp.rows;
    
        go([wg, mat]{
            wg.add();
            cv::Mat tmp;
            cv::resize(mat, tmp, cv::Size(200, 200));
    
            LOG << "resize2 " << tmp.cols << " " << tmp.rows;
    
            wg.done();
        });
    
        wg.wait();
    
        return 0;
    }
    

    堆栈

    #0  cv::hal_baseline::v_int16x8::v_int16x8 (this=0x9fafd01c, v=...)
        at /Volumes/Docs/git/cppframe/build-armeabihf-v7a-Debug/_deps/opencv-src/modules/core/include/opencv2/core/hal/intrin_neon.hpp:206
    #1  0xa4b53fd8 in cv::hal_baseline::v_load (ptr=0x9fb1e780)
        at /Volumes/Docs/git/cppframe/build-armeabihf-v7a-Debug/_deps/opencv-src/modules/core/include/opencv2/core/hal/intrin_neon.hpp:1277
    #2  0xa4e1bcf0 in cv::HResizeLinearVecU8_X4::operator() (this=0x9fafd688, src=0x9fafd78c, 
        dst=0x9fafd74c, count=2, xofs=0x9fb1db00, alpha=0x9fb1e780, dmax=600, cn=3, xmax=600)
        at /Volumes/Docs/git/cppframe/build-armeabihf-v7a-Debug/_deps/opencv-src/modules/imgproc/src/resize.cpp:1744
    #3  0xa4e4bd78 in cv::HResizeLinear<unsigned char, int, short, 2048, cv::HResizeLinearVecU8_X4>::operator() (this=0x9fafdbf8, src=0x9fafd78c, dst=0x9fafd74c, count=2, xofs=0x9fb1db00, 
        alpha=0x9fb1e780, swidth=3240, dwidth=600, cn=3, xmin=0, xmax=600)
        at /Volumes/Docs/git/cppframe/build-armeabihf-v7a-Debug/_deps/opencv-src/modules/imgproc/src/resize.cpp:1830
    #4  0xa4e4357c in cv::resizeGeneric_Invoker<cv::HResizeLinear<unsigned char, int, short, 2048, cv::HResizeLinearVecU8_X4>, cv::VResizeLinear<unsigned char, int, short, cv::FixedPtCast<int, unsigned char, 22>, cv::VResizeLinearVec_32s8u> >::operator() (this=0x9fafdcfc, range=...)
        at /Volumes/Docs/git/cppframe/build-armeabihf-v7a-Debug/_deps/opencv-src/modules/imgproc/src/resize.cpp:2167
    #5  0xa490fc04 in cv::parallel_for_impl (range=..., body=..., nstripes=0.6103515625)
        at /Volumes/Docs/git/cppframe/build-armeabihf-v7a-Debug/_deps/opencv-src/modules/core/src/parallel.cpp:540
    #6  0xa490f9a8 in cv::parallel_for_ (range=..., body=..., nstripes=0.6103515625)
        at /Volumes/Docs/git/cppframe/build-armeabihf-v7a-Debug/_deps/opencv-src/modules/core/src/parallel.cpp:506
    #7  0xa4e21438 in cv::resizeGeneric_<cv::HResizeLinear<unsigned char, int, short, 2048, cv::HResizeLinearVecU8_X4>, cv::VResizeLinear<unsigned char, int, short, cv::FixedPtCast<int, unsigned char, 22>, cv::VResizeLinearVec_32s8u> > (src=..., dst=..., xofs=0x9fb1db00, _alpha=0x9fb1e780, 
        yofs=0x9fb1e460, _beta=0x9fb1f0e0, xmin=0, xmax=600, ksize=2)
        at /Volumes/Docs/git/cppframe/build-armeabihf-v7a-Debug/_deps/opencv-src/modules/imgproc/src/resize.cpp:2204
    #8  0xa4e20268 in cv::hal::resize (src_type=16, 
    --Type <RET> for more, q to quit, c to continue without paging--c
        src_data=0x9ff25040 "4MQ-FJ)BF*CG0FK1GL3IN5KP6MO7OO2JJ1JF3NJ5QK5RI:VP9TQ0JJ7NP;RT0FK)?D.DJ2HN4GN5HO5HP2EM.AI.AI4GO9NV1HP+EL+CI.FL-EK)AG(@F,DJ.GK/HL/HL-FJ+DH+DH-FJ0IM/IO3MS5MS4LR6KS1FN0CK4GO0CK(;C/BI5HO,@E,@E2HM2IK3GH/DB,A"..., src_step=3240, src_width=1080, src_height=720, dst_data=0x9fb005c0 "", dst_step=600, dst_width=200, dst_height=200, inv_scale_x=0.18518518518518517, inv_scale_y=0.27777777777777779, interpolation=1) at /Volumes/Docs/git/cppframe/build-armeabihf-v7a-Debug/_deps/opencv-src/modules/imgproc/src/resize.cpp:4036
    #9  0xa4e209d8 in cv::resize (_src=..., _dst=..., dsize=..., inv_scale_x=0.18518518518518517, inv_scale_y=0.27777777777777779, interpolation=1) at /Volumes/Docs/git/cppframe/build-armeabihf-v7a-Debug/_deps/opencv-src/modules/imgproc/src/resize.cpp:4089
    #10 0x000126c0 in <lambda()>::operator()(void) const (__closure=0x340d4) at /Volumes/Docs/git/cppframe/frameworks/wuyi_core/main.cpp:26
    #11 0x00014004 in co::xx::Function0<main(int, char**)::<lambda()> >::run(void) (this=0x340d0) at /Volumes/Docs/git/cppframe/build-armeabihf-v7a-Debug/install/wuyi_util/include/co/closure.h:24
    #12 0xa6477230 in co::SchedulerImpl::main_func (from=...) at /Volumes/Docs/git/cppframe/build-armeabihf-v7a-Debug/_deps/libco-src/src/co/scheduler.cc:38
    #13 0xa64b5b44 in tb_context_make () at /Volumes/Docs/git/cppframe/build-armeabihf-v7a-Debug/_deps/libco-src/src/co/context/context_arm.S:131
    
  • udpsocket  co::close无法让co::recvfrom跳出等待

    udpsocket co::close无法让co::recvfrom跳出等待

    co::close 触发了del_event(ev) 但是co::recvfrom等待的EV_read 消息订阅被删除,但是recvfrom仍然在等待。

    解决方法: 1、或许可以加入EV_close/EV_ignore之类的事件。 2、IoEvent加入多事件绑定,同时绑定EV_read和EV_close/EV_ignore。

  • 关于Json::Value::operator[]的疑问

    关于Json::Value::operator[]的疑问

    https://github.com/idealvin/co/blob/110eadde2c89dffa259a161c100cd4161253441f/base/json.cc#L15

    正常访问一个objectmember时,应该先调用has_member()来进行判断。但看上面函数的代码,如果直接使用[]来访问相关member,这里的逻辑似乎是打算支持这种行为?

    如果不支持的话,21&22行的意义何在呢? 如果支持的话,接下来访问其它member或者调用str()就会出错了。如以下代码:

    #include "co/json.h"
    
    int main() {
      auto v = json::parse("{\"name\": \"tiger\"}");
      v["n"];
      printf("%s\n", v["name"].str().c_str());
      return 0;
    }
    

    输出结果是null

  • SylixOS嵌入式实时操作系统上移植coost

    SylixOS嵌入式实时操作系统上移植coost

    编译器采用gcc-arm-none-eabi

    在编译context_arm.S时,出现如下问题

    make all arm-sylixos-eabi-gcc -mcpu=cortex-a9 -mno-unaligned-access -O0 -g3 -gdwarf-2 -Wall -fmessage-length=0 -fsigned-char -fno-short-enums -fno-strict-aliasing -x assembler-with-cpp -DSYLIXOS -D__linux__ -D__ARM_ARCH_7A__ -DSYLIXOS_LIB -I"D:\SylixOS_WS\workspace\coost/include" -I"D:/SylixOS_WS/workspace/ArmLite/libsylixos/SylixOS" -I"D:/SylixOS_WS/workspace/ArmLite/libsylixos/SylixOS/include" -I"D:/SylixOS_WS/workspace/ArmLite/libsylixos/SylixOS/include/network" -MMD -MP -MF ./Debug/dep/libcoost.so/src/co/context/context_arm.d -c src/co/context/context_arm.S -o Debug/obj/libcoost.so/src/co/context/context_arm.o src/co/context/context_arm.S: Assembler messages: src/co/context/context_arm.S:102: Error: bad instructionfunction tb_context_make,export=1' src/co/context/context_arm.S:142: Error: bad instruction endfunc' src/co/context/context_arm.S:152: Error: bad instructionfunction tb_context_jump,export=1' src/co/context/context_arm.S:204: Error: bad instruction endfunc' make: *** [Debug/obj/libcoost.so/src/co/context/context_arm.o] Error 1

    无法识别汇编代码中的 function 以及 endfunc

  • 有支持格式化字符串吗?

    有支持格式化字符串吗?

    支持格式化字符串,而不再需要用sprintf,例如folly库中的这种用法:

    using folly::format;
    using folly::sformat;
    
    // Objects produced by format() can be streamed without creating
    // an intermediary string; {} yields the next argument using default
    // formatting.
    std::cout << format("The answers are {} and {}", 23, 42);
    // => "The answers are 23 and 42"
    
    // If you just want the string, though, you're covered.
    std::string result = sformat("The answers are {} and {}", 23, 42);
    // => "The answers are 23 and 42"
    
    // To insert a literal '{' or '}', just double it.
    std::cout << format("{} {{}} {{{}}}", 23, 42);
    // => "23 {} {42}"
    
    // Arguments can be referenced out of order, even multiple times
    std::cout << format("The answers are {1}, {0}, and {1} again", 23, 42);
    // => "The answers are 42, 23, and 42 again"
    
    // It's perfectly fine to not reference all arguments
    std::cout << format("The only answer is {1}", 23, 42);
    // => "The only answer is 42"
    
    // Values can be extracted from indexable containers
    // (random-access sequences and integral-keyed maps), and also from
    // string-keyed maps
    std::vector<int> v {23, 42};
    std::map<std::string, std::string> m { {"what", "answer"} };
    std::cout << format("The only {1[what]} is {0[1]}", v, m);
    // => "The only answer is 42"
    
    // format works with pairs and tuples
    std::tuple<int, std::string, int> t {42, "hello", 23};
    std::cout << format("{0} {2} {1}", t);
    // => "42 23 hello"
    
    // Format supports width, alignment, arbitrary fill, and various
    // format specifiers, with meanings similar to printf
    // "X<10": fill with 'X', left-align ('<'), width 10
    std::cout << format("{:X<10} {}", "hello", "world");
    // => "helloXXXXX world"
    
    // Field width may be a runtime value rather than part of the format string
    int x = 6;
    std::cout << format("{:-^*}", x, "hi");
    // => "--hi--"
    
    // Explicit arguments work with dynamic field width, as long as indexes are
    // given for both the value and the field width.
    std::cout << format("{2:+^*0}",
    9, "unused", 456); // => "+++456+++"
    
    // Format supports printf-style format specifiers
    std::cout << format("{0:05d} decimal = {0:04x} hex", 42);
    // => "00042 decimal = 002a hex"
    
    // Formatter objects may be written to a string using folly::to or
    // folly::toAppend (see folly/Conv.h), or by calling their appendTo(),
    // and str() methods
    std::string s = format("The only answer is {}", 42).str();
    std::cout << s;
    // => "The only answer is 42"
    
    // Decimal precision usage
    std::cout << format("Only 2 decimals is {:.2f}", 23.34134534535);
    // => "Only 2 decimals is 23.34"
    
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);

Dec 20, 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
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 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
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
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
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
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
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
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
Go-style concurrency in C

LIBMILL Libmill is a library that introduces Go-style concurrency to C. Documentation For the documentation check the project website: http://libmill.

Dec 31, 2022
Cpp-concurrency - cpp implementation of golang style concurrency

cpp-concurrency C++ implementation of golang style concurrency Usage Use existing single header concurrency.hpp or run script to merge multiple header

Aug 11, 2022
Libgo - Go-style concurrency in C++11
Libgo - Go-style concurrency in C++11

libgo libgo -- a coroutine library and a parallel Programming Library Libgo is a stackful coroutine library for collaborative scheduling written in C+

Dec 26, 2022
Bolt is a C++ template library optimized for GPUs. Bolt provides high-performance library implementations for common algorithms such as scan, reduce, transform, and sort.

Bolt is a C++ template library optimized for heterogeneous computing. Bolt is designed to provide high-performance library implementations for common

Dec 27, 2022
oneAPI DPC++ Library (oneDPL) https://software.intel.com/content/www/us/en/develop/tools/oneapi/components/dpc-library.html

oneAPI DPC++ Library (oneDPL) The oneAPI DPC++ Library (oneDPL) aims to work with the oneAPI DPC++ Compiler to provide high-productivity APIs to devel

Dec 29, 2022
Simple and fast C library implementing a thread-safe API to manage hash-tables, linked lists, lock-free ring buffers and queues

libhl C library implementing a set of APIs to efficiently manage some basic data structures such as : hashtables, linked lists, queues, trees, ringbuf

Dec 3, 2022
OOX: Out-of-Order Executor library. Yet another approach to efficient and scalable tasking API and task scheduling.

OOX Out-of-Order Executor library. Yet another approach to efficient and scalable tasking API and task scheduling. Try it Requirements: Install cmake,

Oct 25, 2022