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

Overview

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!
Issues
  • 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.

    question 
    opened by DisableAsync 42
  • 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
    
    opened by izhengfan 25
  • 是否有意向将install后的库名以及目录名由base改为co?

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

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

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

    opened by tigerlee 23
  • macOS 平台 动态库hook不生效

    macOS 平台 动态库hook不生效

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

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

    PS:

    1. 非阻塞可通过curl_multi_perform+辅助线程/协程(也许),或者有更好的办法(基于curl)?
    2. 很多测试貌似应该用单个协程调度器,比较容易发现阻塞的问题。
    bug improvement 
    opened by shuai132 21
  • [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).

    opened by DisableAsync 18
  • 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! 该如何解决

    opened by ylsislove 17
  • 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.

    opened by meet-ai 16
  • 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。

    opened by mosxuqian 13
  • 关于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

    opened by tigerlee 12
  • 关于 `thread_ptr` 的疑问

    关于 `thread_ptr` 的疑问

    @idealvin 你好。

    按我的理解,当 thread_ptr 的对象,例如 foo,以 std::ref(foo) 的方式传进线程函数之后,子线程是无法获取父线程设置的指针值的。也就是说,任何一个线程要使用 foo,都必须现在线程内设置 foo 指向的对象才能使用。

    既然如此,为什么不在每个线程里分别使用 std::unique_ptr 呢?使用 thread_ptr 的好处在哪里?

    question 
    opened by Liam0205 12
  • base xmake错误:ambiguating new declaration of ‘__pid_t xxGettid()’

    base xmake错误:ambiguating new declaration of ‘__pid_t xxGettid()’

    error: co/impl/../../unix/thread.h:184:16: 错误:ambiguating new declaration of ‘__pid_t xxGettid()’
      184 | #define gettid xxGettid
          |                ^~~~~~~~
    co/impl/../../unix/thread.h:178:21: 附注:old declaration ‘unsigned int xxGettid()’
      178 | inline unsigned int xxGettid() {
          |                     ^~~~~~~~
    
    opened by codehz 11
  • 多次重连服务器失败,co::connect返回false, co::error()返回0

    多次重连服务器失败,co::connect返回false, co::error()返回0

    在生产环境中,我实现了一个高可用机制。在节点失效后,客户端自动重连其他节点。同时有其他守护进程会巡检节点,如果节点恢复之后就把其加入回可用节点列表。

    问题的情况是,最开始有两个服务器,节点1失效切换到节点2是没问题的。但是在第一次切换之后,节点2切换回节点1(强制杀死节点2)的过程中。客户端与节点1重新建立连接,co::connect返回false, 但是co::error()返回0。具体平台是windows 10。

    opened by neoblackcap 3
  • Release a new version

    Release a new version

    The API in JSON module described in the docs are not compatible in the latest release version(2.0.3).

    I hope there could be a new release version and upload to conan. Thank you.

    opened by tarma 1
  • add HTTP long-conn-session support

    add HTTP long-conn-session support

    添加对http long-connection session模式的支持。即一直保持http连接,并且response不断追加发送数据的工作模式。 此模式常用于需要保持连接完成大量数据发送(如发送超长文件)或者数据不定时发送(如:视频实时推流,x-mixed-replace,部分web-socket应用场景等)。 主要改动(API接口部分): 1、在Http Response类(Res)中,新增一个send_body方法。此方法可在设置完header(add_header)后使用。用户每次调用send_body将即时发送一批body数据(因此,第一次调用send_body会附加Http response header的发送)。用户可以在保证当前协程函数(on_req)不退出的情况下任意多次调用send_body,直至业务数据发送完毕或者出错(send_body返回非0)。无论出错还是正常完成,退出当前协程函数即完成本http session资源的清理。 2、因为send_body(长连接)模式与set_body(短链接)模式互斥,因此修改set_body函数返回值为bool。用户调用set_body以后将无法调用send_body,同理用户调用send_body以后也将无法调用set_body(返回false)。 实现部分改动: 在Response-ctx类实现中,增加3个成员:

    • tcp::Connection pc_; //存放本http Session socket connection的指针。短连接时用不到,仅在长连接模式下使用
    • header_send; //header是否发送的状态码。初始化为0,表示header未处理。为-1表示header已经pack但未发送(短链接模式),为1表示header已经发送(长连接模式)。
    • s_err:仅用于长连接模式,表示是否有socket_error。
    opened by ltmit 0
Releases(v2.0.3)
  • v2.0.3(Nov 13, 2021)

    New features

    • Add str::cat(...), fastream::cat(...), fastring::cat(...)
    fastring s("hello");
    s.cat(' ', 23, "xx", false);          // s -> "hello 23xxfalse"
    s = str::cat("hello", ' ', 23, true); // s -> "hello 23true"
    
    • Support writing logs to customed destinations by setting a writing callback in co/log.

    • Support daily rotation and log compression for co/log. Thanks to kuyoonjo. Compression is experimental at present.

    • Add method reset() for fastring & fastream.

    • Add method exit() for http::Server & rpc::Server.

    • Add co::maybe.

    • Add os::system().

    • Add some god-oriented programming features in co/god.h.

    Changed

    • Improve exit() method for tcp::Server.

    • Memory optimization for coroutines.

    • Improve operator<< for fastream & fastring, optimization for string literal.

    Bugs fixed

    Source code(tar.gz)
    Source code(zip)
  • v2.0.2(Oct 3, 2021)

    New features

    • Support mingw on windows.
    xmake f -p mingw
    xmake -v
    
    • Support shared library on windows.
    xmake f -k shared
    xmake -v
    
    • Support syslog header for co/log.
    xmake b log
    xmake r log -syslog -cout
    
    • Add god mode. When you feel uneasy about writing a function of several hundred lines, you may call the following api to increase your confidence:
    #include "co/god.h"
    
    god::bless_no_bugs();
    

    Changed

    • Add co/cout.h, make COUT and CLOG thread-safe.

    • Add co::thread_id(), which may replace current_thread_id() in the future.

    • co/log: add time in name of old log files.

    • co/log: improve stack trace, use libbacktrace instead of fork()+backtrace().

    • Add os::env(name, value) for setting value of environment variables.

    • Always use / as the path separator, and convert \ to / in results of os::cwd(), os::homedir(), os::exepath().

    • Improve the path library, support windows path like d:/xx.

    • Improve stability of coroutine hook.

    • tcp::Server::on_connection(tcp::Connection*) to tcp::Server::on_connection(tcp::Connection), no need to delete the connection object any more.

    • Refactor http::Client, use curl easy handle only.

    • Refactor http::Server, reduce memory copy to improve the performance.

    Bugs fixed

    • Fix RemoveVectoredExceptionHandler bug.

    • Fix crash of http::Client in #168.

    • Fix macro comment directive bug.

    • Fix bug in select hook in #176.

    • Fix a errno bug in co/flag, set errno to 0 before set_value.

    • Fix errno bug in str::to_xxx().

    • Fix hang-at-exit bug when using dll on windows.

    • Fix iterator bug in std::multimap on windows.

    • Fix: failed to hook fcntl with a different _FILE_OFFSET_BITS.

    Source code(tar.gz)
    Source code(zip)
  • v2.0.1(Aug 12, 2021)

    Documents

    New features

    • xrepo
    xrepo install -f "openssl=true,libcurl=true" co
    
    • vcpkg
    vcpkg install co:x64-windows
    
    # http & ssl support
    vcpkg install co[libcurl,openssl]:x64-windows
    
    • defer (similar to defer in golang)
    #include "co/defer.h"
    Timer t;
    defer(LOG << "time elapse: " << t.us() << "us");
    
    • channel for coroutine (similar to channel in golang)
    #include "co/co.h"
    
    DEF_main(argc, argv) {
        co::Chan<int> ch;
        go([ch]() {
            ch << 7;
        });
    
        int v = 0;
        ch >> v;
        LOG << "v: "<< v;
    
        return 0;
    }
    
    • waitgroup (similar to sync.WaitGroup in golang)
    #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;
    }
    
    • Coroutine hook for windows.

    • Create coroutines in specified scheduler(thread).

    auto s = co::next_scheduler();
    s->go(f1);
    s->go(f2);
    
    • Create coroutine in all schedulers.
    auto& s = co::all_schedulers();
    for (size_t i = 0; i <s.size(); ++i) {
        s[i]->go(f);
    }
    
    • Add void flag::init(const fastring& path);

    Changed

    • Closure to co::Closure.

    • Improve co::Event, can be used anywhere, and support copy constructor and capture by value in lambda.

    • Improve co::Mutex, co::Pool, support copy constructor and capture by value in lambda.

    • co::close() now can be called anywhere, not necessary to call it in the thread that performed the IO operations.

    • Partial support for mingw. Coroutine and coroutine-based features do not work for mingw at present.

    Bugs fixed

    • fix bug in fs::file when read/write more than 4G bytes.

    • fix connect timeout error for http::Client.

    • fix link problem in #165.

    Source code(tar.gz)
    Source code(zip)
  • v2.0.0(May 28, 2021)

    Detailed reference documents


    First of all, it is particularly emphasized that more detailed documents are provided this time:


    New features

    SSL


    Co 2.0 finally supports SSL. Users need to install openssl 1.1 or above. It has been tested on openssl and other SSL libraries have not been tested yet.

    It is recommended to use xmake as the build tool, it will prompt the user whether to install openssl, libcurl, zlib and other third-party libraries. To enable the SSL feature, you need to predefine the CO_SSL macro, which is automatically defined by xmake when openssl is detected.

    co/so/ssl.h provides a coroutine-based openssl interface, but users may not use it directly, because co has embedded the SSL feature into the TCP module, and users can use tcp::Server and tcp::Client instead.


    Important improvements

    Coroutine

    • go
      go() supports any function or class method with 0 or 1 parameter, as well as function objects or pointers of type std::function<void()>.

      void f();
      void g(int);
      void h(int, int);
      struct T {
      	void f();
      	void g(int);
      };
      
      T o;
      std::function<void()> k(std::bind(h, 3, 7));
      
      go(f);
      go(g, 7);
      go(&T::f, &o);
      go(&T::g, &o, 3);
      go(k);
      go(&k); // The user must ensure that k is alive when the coroutine is running.
      
    • Coroutine API

      • Adding the co::timeout() function, users can use it to determine whether the last I/O function like co::recv or co::send has timed out.
      • The co::coroutine_id() function returns a globally unique id. In version 1.x, coroutines in different scheduling threads may have the same id.
    • co::Event
      The signaled state is added internally to solve the problem that the synchronization signal will be lost when there is no waiting coroutines.

    • co::IoEvent
      In the 1.x version, it is only used internally, and this class is public in 2.0, so that users can coroutineize third-party network libraries by themselves.

      int recv(SSL* s, void* buf, int n, int ms) {
          CHECK(co::scheduler()) << "must be called in coroutine..";
          int r, e;
          int fd = SSL_get_fd(s);
          if (fd <0) return -1;
      
          do {
              ERR_clear_error();
              r = SSL_read(s, buf, n);
              if (r> 0) return r; // success
              if (r == 0) {
                  DLOG << "SSL_read return 0, error: "<< SSL_get_error(s, 0);
                  return 0;
              }
      
              e = SSL_get_error(s, r);
              if (e == SSL_ERROR_WANT_READ) {
                  co::IoEvent ev(fd, co::EV_read);
                  if (!ev.wait(ms)) return -1;
              } else if (e == SSL_ERROR_WANT_WRITE) {
                  co::IoEvent ev(fd, co::EV_write);
                  if (!ev.wait(ms)) return -1;
              } else {
                  DLOG << "SSL_read return "<< r << ", error:" << e;
                  return r;
              }
          } while (true);
      }
      


      The above is an example of coroutineizing the SSL_read() function in openssl. You only need to use a non-blocking socket. When openssl generates an SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE error, call the wait() method of co::IoEvent and wait for the corresponding I/O Event.


    TCP

    • Added the tcp::Connection class for the implementing TCP server. This class provides recv(), send() and other methods. Users can directly use this class to receive and send data without worrying about whether the underlying SSL is enabled.

    • tcp::Server

      void on_connection(tcp::Connection* conn);
      
      tcp::Server s;
      s.on_connection(on_connection);
      s.start("0.0.0.0", 7788); // no ssl
      s.start("0.0.0.0", 7788, "privkey.pem", "certificate.pem"); // use ssl
      

      Users can specify the SSL private key and certificate file in the start() method to enable SSL.


    • tcp::Client

      bool use_ssl = false;
      tcp::Client c("127.0.0.1", 7788, use_ssl);
      c.connect(1000);
      c.send(...);
      


      tcp::Client can enable SSL through the third parameter of the constructor.


    HTTP

    • http::Client
      In co 2.0, http::Client is implemented based on libcurl. To enable this feature, you must install libcurl and predefine the HAS_LIBCURL macro. Again, it is recommended to use xmake to build, it will automatically handle these third-party dependencies.

      http::Client c("https://github.com");
      http::Client c("http://127.0.0.1:7777");
      c.get("/");
      c.get("/idealvin/co");
      LOG << c.response_code();
      
    • http::Server

      http::Server s
      s.on_req(...);
      s.start("0.0.0.0", 7777); // http
      s.start("0.0.0.0", 7777, "privkey.pem", "certificate.pem"); // https
      

    RPC


    In co 2.0, some new features have been added to the RPC framework:

    • Support SSL.
    • Support username and password authentication, and multiple usernames and passwords can be set for rpc::Server.
    • Multiple services can be added into rpc::Server.
    • The code generator can also generate code for RPC client.

    JSON


    In the 1.x version, the JSON library uses only one json::Value class to represent a JSON. The elements in the JSON object are also json::Value. When constructing a JSON object, you need to allocate memory for each element. When the JSON object is destructed, All internal elements must call the destructor of json::Value. Implementation based on this method will cause frequent memory allocation and release, which greatly affects program performance.

    In co 2.0, the JSON object is built into a continuous memory. After the program runs stably, the parsing of JSON requires almost no memory allocation, which greatly improves the parsing speed.

    In addition, co 2.0 uses the Json class to represent a JSON, and the json::Value class to represent the elements in the JSON. json::Value is just a trivial class, only including a index position in the JSON memory block. When a JSON is destructed, the destructor of the Json class is called only once, and the destructor of json::Value will never be called.


    Others

    • Fix the nested log bug in co/log.
    • Fix some bugs caused by dependence of global static variables.
    • Add milliseconds in log time of co/log.
    • The TaskSched class is renamed to Tasked.
    • co/time.h. Add epoch::ms() and epoch::us() to obtain the time since the EPOCH.
    • co/os.h Add os::signal() method to set the signal handler function.
    • Added safe_clear() method in fastring.
    • Added safe_clear() method in Json.
    Source code(tar.gz)
    Source code(zip)
  • v2.0.0-preview(May 23, 2021)

  • v1.2.0(Jul 16, 2020)

    English

    Changes

    Bug fix

    • fix #77
    • fix #83
    • Fix bug due to Coroutine in Copool not cleared inside the coroutine library.
    • Fix assert bug in disconnect() of tcp::Client.

    coroutine

    • Refactor coroutine library to simplify internal logic.
    • Add unit test unitest/co for testing internal logic of coroutine schedulers.

    support http

    • Implement the http::Server class.
    • Implement the http::Client class.
    • Implement the so::easy(...) interface for quickly creating a static web server.

    hash

    • Add size_t murmur_hash(...) interface.

    fastring

    • Support std::hash<fastring>, now std::unordered_map can take fastring as its key.

      std::unordered_map<fastring, fastring> um;
      LruMap<fastring, fastring> lm;
      
    • Added lshift() interface for left shifting the string.

      fastring s = "hello";
      s.lshift(2); // s -> "llo";
      s.lshift(8); // s -> "";
      
    • Added shrink() interface, similar to shrink_to_fit() of std::string, for reducing the capacity of fastring.

      fastring s(4096); // cap -> 4096
      s.shrink();       // cap <4096
      

    test/unitest

    • Remove the _test suffix in the file name of the test/unitest code.

    中文

    Changes

    Bug 修复

    • fix #77
    • fix #83
    • 修复协程库内部 Copool 未清空 Coroutine 中旧数据引起的 bug.
    • 修复 tcp::Client::disconnect() 中的 assert bug.

    coroutine

    • 重构协程库,简化内部逻辑
    • 增加单元测试 unitest/co,用于测试 Scheduler 内部逻辑.

    新增 http 模块

    • 实现 http::Server 类.
    • 实现 http::Client 类.
    • 实现 so::easy(...) 接口,用于快速创建静态 web server.

    hash

    • 新增 size_t murmur_hash(...) 接口.

    fastring

    • 支持 std::hash<fastring>std::unordered_map 可以使用 fastring 作为 key.

      std::unordered_map<fastring, fastring> um;
      LruMap<fastring, fastring> lm;
      
    • 新增 lshift() 接口,将字符串左移若干字节.

      fastring s = "hello";
      s.lshift(2);  // s -> "llo";
      s.lshift(8);  // s -> "";
      
    • 新增 shrink() 接口,与 std::stringshrink_to_fit() 类似,用于缩减 fastring 的容量.

      fastring s(4096); // cap -> 4096
      s.shrink();       // cap < 4096
      

    test/unitest

    • 去掉 test/unitest 代码文件名中的 _test 后缀.
    Source code(tar.gz)
    Source code(zip)
  • v1.2.3(Oct 10, 2020)

  • v1.2.2(Jul 30, 2020)

  • v1.2.1(Jul 19, 2020)

  • v1.1(Jul 2, 2020)

    English

    github

    Changes

    Code structure adjustment

    • Put the header file in the include directory.
    • The source files are placed in the src directory.
    • rpcgen was renamed to gen.
    • Remove the unitest/base directory and put the unit test code directly under the unitest directory.
    • Support subdirectories under test directory.

    fast

    • The definition of static variables in fast.cc is put into functions, and initialization is safer.
    • fast::dtoa deprecated the implementation based on LruMap and replaced with the implementation of Milo Yip.
    • Added fast::stream class to provide basic streaming output operations.

    fastream

    • Inherited from fast::stream class.
    • Support move constructor.
    • Added empty status.
      fastream fs; // Define an empty fastream object without allocating memory
      
    • Support append itself.
      fastream fs;
      fs << "hello "<< 23;
      fs.append(fs);
      fs << fs; // <==> fs.append(fs)
      

    fastring

    • Like fastream, it inherits from fast::stream class, so it also supports streaming output operations.
      fastring s;
      s << "hello "<< 23;
      
    • The memory structure of fastring and fastream is the same, the two can be converted seamlessly.
      fastring s;
      fastream& fs = *(fastream*)&s
      
    • Removed reference counting to make fastring's copying behavior similar to std::string, which is not easy to make mistakes.
      fastring s("hello");
      fastring t(s); // Create a new string through memory copy
      
    • append operation adds inside check to fix logic bug on memory overlaps.
      fastring s("123");
      s.append(s.c_str() + 1); // s -> "12323"
      
    • Remove the clone(), it is no more needed as the reference count was removed.

    str

    • str::dbg() supports std::unordered_map and std::unordered_set.

    flag

    • Optimize the parsing order of command line parameters and configuration files, first parse the configuration file, and then parse other command line parameters.
      # First parse the configuration file xx.conf, then parse other command line parameters
      # The values ​​of x and s in the command line will override the values ​​in xx.conf for easy debugging
      ./xx -x -s="hello" -config=xx.conf
      
    • Added built-in bool flag daemon on Linux platform to support background running programs
      # add -daemon in the command line parameters
      # or set in the configuration file: daemon = true
      ./xx -daemon
      
    • Command line parameters support multiple styles, -x=y can be written as -x y or x=y
      ./xx -i=8 u=88 -s="hello world"
      ./xx -i 8 -u 88 -s "hello world"
      
    • Optimize the way to specify the configuration file when the program starts.
      ./xx config=xx.conf  # Use flag config to display the specified
      ./xx xx.conf         # The configuration file name ends with .conf or config 
                           # and is the first non-flag parameter, then config= can be omitted
      ./xx -x xx.conf      # -x is the flag, xx.conf is the first non-flag parameter
      
    • When defining the flag, you can specify the level in the comment to control the order of the flag in the configuration file.
      // Use #3 at the beginning of the comment to specify level 3
      // The supported level is 0-99, the default is 10
      // When using --mkconf to automatically generate a configuration file, the flags are sorted by level, file name, and number of lines of code
      DEF_bool(b, false, "#3 xxx");
      

    log

    • Some functions in the signal handler are changed to async-signal-safe version functions, which is safer.

    coroutine

    • Fixed bugs caused by io events registered in epoll(kevent, iocp) that were not removed in time.
    • Fix the bug that the internal iterator is not updated correctly when Scheduler::add_timer() is called.
    • Improve the implementation of co::connect, co::accept, etc. to support ipv6.
    • Added co::max_sched_num() interface to get the maximum number of scheduling threads supported, which is currently the number of CPU cores in the system.
    • Added co::sched_id() interface to get current scheduling thread id.
    • Added coroutine_id() interface to get the id of current coroutine.
    • Refactored Scheduler, the internal logic structure is clearer, and the code is more readable.
    • Modify the parameters of the co::tcp_socket(), co::udp_socket() interface to address family, deprecating the earlier way of specifying ipv4 and ipv6 with 4 and 6.
      sock_t tcp_socket(int af=AF_INET); // @af: address family, AF_INET, AF_INET6, etc.
      sock_t udp_socket(int af=AF_INET); // @af: address family, AF_INET, AF_INET6, etc.
      
    • Added co::socket() interface, which is consistent with the native API.
    • Fixed the initialization problem of some static global variables in the hook implementation.
    • Optimized the internal implementation of co::Event.
    • Refactored co::Pool:
      • Users can specify callback ccb and dcb, which are used to create and destroy an element respectively.
      • Users can specify the maximum capacity of the pool (only valid when dcb is set).
      • Register cleanup callback with Scheduler in the internal implementation to ensure the cleanup of co::Pool at the end of the scheduling thread.
    • co::Kakalot was renamed to co::PoolGuard.

    json

    • Internal reference counting, using atomic operations, copying Json objects is safe in multi-thread environment.
    • Reconstruct the internal memory model of Json, and fix the bugs caused by the internal memory changes.
    • A simple memory allocator Jalloc is added to improve the performance of Json.
    • json::parse() supports parsing of array objects.
      Json v = json::parse("[1, 2, 3]");
      
    • Added Json::dbg() interface to convert Json object to debug string (longer strings in Json objects may be truncated).
    • The log library calls Json::dbg() to output Json objects, making the output log more streamlined.

    rpc

    • Simplify, remove some unnecessary configuration items.
    • Optimize connection management, you can specify the timeout period of idle connections and the maximum number of idle connections through rpc_conn_idle_sec and rpc_max_idle_conn.

    hash

    • Modify the implementation of hash32(), the 32-bit system uses the 32-bit version of murmur 2, the 64-bit system directly takes the lower 32 bits of hash64.

    Compile

    • Removed scons compilation script.
    • Support xmake compilation.
    • Support cmake compilation (contributed by izhengfan).
    • Windows supports compilation with VS project files (automatically generated by xmake).

    中文

    github

    Changes

    代码结构调整

    • 头文件放到 include 目录.
    • 源文件放到 src 目录.
    • rpcgen 更名为 gen.
    • 移除 unitest/base 目录,单元测试代码直接放到 unitest 目录下.
    • test 目录下支持子目录.

    fast

    • fast.cc 中静态变量的定义放到函数中,初始化更安全.
    • fast::dtoa 弃用基于 LruMap 的实现,换用 Milo Yip 的实现(miloyip/dtoa-benchmark).
    • 新增 fast::stream 类,提供基本的流式输出操作.

    fastream

    • 继承于 fast::stream 类.
    • 支持 move 构造函数.
    • 增加空状态.
      fastream fs; // 定义一个空的 fastream 对象,不分配内存
      
    • 支持 append 自己.
      fastream fs;
      fs << "hello " << 23;
      fs.append(fs);
      fs << fs;     // <==> fs.append(fs)
      

    fastring

    • fastream 一样,继承于 fast::stream 类,因此也支持流式输出操作.
      fastring s;
      s << "hello " << 23;
      
    • fastring 与 fastream 的内存结构相同,二者可以无缝转换.
      fastring s;
      fastream& fs = *(fastream*)&s
      
    • 移除引用计数,使 fastring 的复制行为与 std::string 类似,使用起来不容易出错.
      fastring s("hello");
      fastring t(s);  // 通过内存拷贝创建一个新的字符串
      
    • append 操作增加 inside 检查,修复内存重叠时的逻辑漏洞.
      fastring s("123");
      s.append(s.c_str() + 1); // s -> "12323"
      
    • 删除 clone() 方法,移除引用计数后,此方法多余.

    str

    • str::dbg() 支持 std::unordered_mapstd::unordered_set.

    flag

    • 优化命令行参数与配置文件的解析顺序,先解析配置文件,再解析其他命令行参数.
      # 先解析配置文件 xx.conf,再解析其他命令行参数
      # 命令行中 x, s 的值会覆盖 xx.conf 中的值,方便调试
      ./xx -x -s="hello" -config=xx.conf
      
    • Linux 平台增加内置 bool flag daemon,以支持后台运行程序
      # 可在命令行参数中带上 -daemon
      # 也可在配置文件中设置: daemon = true
      ./xx -daemon
      
    • 命令行参数支持多种格式,-x=y 可以写成 -x y 或者 x=y
      ./xx -i=8 u=88 -s="hello world"
      ./xx -i 8 -u 88 -s "hello world"
      
    • 优化程序启动时指定配置文件的方式.
      ./xx config=xx.conf  # 用 flag config 显示指定
      ./xx xx.conf         # 配置文件名以 .conf 或 config 结尾,且是程序命令行的第一个非 flag 参数,则可省略 config=
      ./xx -x xx.conf      # -x 是 flag,xx.conf 是第一个非 flag 参数
      
    • 定义 flag 时,可以在注释中指定级别,以控制 flag 在配置文件中的顺序.
      // 在注释开头用 #3 指定级别为 3
      // 支持的级别为 0-99,默认为 10
      // 使用 --mkconf 自动生成配置文件时,flag 按级别、文件名、代码行数排序
      DEF_bool(b, false, "#3 xxx");
      

    log

    • signal handler 中部分函数修改为 async-signal-safe 版本的函数,更安全.

    协程库

    • 修复未及时移除 epoll(kevent, iocp) 中注册的 io 事件引起的 bug.
    • 修复 Scheduler 在 add_timer() 时,内部 iterator 未正确更新的 bug.
    • 改进 co::connect, co::accept 等的实现,以支持 ipv6.
    • 新增 co::max_sched_num() 接口,获取支持的最大调度线程数,目前为系统 cpu 核数.
    • 新增 co::sched_id() 接口,获取当前的调度线程 id.
    • 新增 coroutine_id() 接口,获取当前协程的 id.
    • 重构 Scheduler,内部逻辑结构更清晰,同时提高代码的可读性.
    • 修改 co::tcp_socket(), co::udp_socket() 接口的参数为 address family,弃用早期用 4 与 6 指定 ipv4 与 ipv6 的方式.
      sock_t tcp_socket(int af=AF_INET); // @af: address family, AF_INET, AF_INET6, etc.
      sock_t udp_socket(int af=AF_INET); // @af: address family, AF_INET, AF_INET6, etc.
      
    • 新增 co::socket() 接口,与原生 api 保持一致.
    • 修复 hook 实现中一些静态全局变量的初始化问题.
    • 优化 co::Event 的内部实现.
    • 重构 co::Pool:
      • 用户可以指定 callback ccbdcb,分别用于创建、销毁元素.
      • 用户可以指定 pool 的最大容量(仅在 dcb 设置时有效).
      • 内部实现中向 Scheduler 注册 cleanup callback,保证在调度线程结束时进行 co::Pool 的清理工作.
    • co::Kakalot 重命名为 co::PoolGuard.

    json

    • 内部引用计数,使用原子操作,复制 Json 对象更安全.
    • 重构 Json 内部的内存模型,修复之前因内部内存变化引起的 bug.
    • 内部增加简单的内存分配器 Jalloc,提升 Json 的性能.
    • json::parse() 支持数组对象的解析.
      Json v = json::parse("[1, 2, 3]");
      
    • 新增 Json::dbg() 接口,将 Json 对象转换为 debug string (Json 对象中较长的字符串可能被截断).
    • log 库调用 Json::dbg() 输出 Json 对象,使得输出的日志更精简.

    rpc

    • 简化,移除一些不必要的配置项.
    • 优化连接管理,可以通过 rpc_conn_idle_secrpc_max_idle_conn 指定空闲连接的超时时间、最大的空闲连接数.

    hash

    • 修改 hash32() 的实现,32 位系统使用 murmur 2 的 32 位版本,64 位系统直接取 hash64 的低 32 位.

    编译

    • 移除 scons 编译脚本.
    • 支持 xmake 编译.
    • 支持 cmake 编译 (由 izhengfan 贡献).
    • windows 支持用 vs 工程文件编译 (由 xmake 自动生成).
    Source code(tar.gz)
    Source code(zip)
Owner
Alvin
Hi, I'm Alvin. I'm working on a go-style C++ coroutine library cocoyaxi.
Alvin
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);

null 379 Jun 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

sharow 350 Aug 4, 2022
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

Eduardo Bart 309 Jul 24, 2022
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

null 17 Jul 19, 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.

null 42 Jun 10, 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

Lewis Baker 2.4k Aug 3, 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

Tim Ambrogi Saxon 58 Jul 25, 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

Bloomberg 447 Jul 25, 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

Yuchao Zhang 56 Aug 8, 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

Grady O'Connell 4 Sep 21, 2019
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

Infinit 463 Jul 29, 2022
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.

Martin Sustrik 2.6k Aug 8, 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

YoungJoong Kim 14 Aug 11, 2022
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+

null 2.7k Aug 9, 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

null 355 Jun 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

oneAPI-SRC 632 Aug 7, 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

Andrea Guzzo 387 Jul 30, 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,

Intel Corporation 17 Mar 10, 2022