Git Product home page Git Product logo

co_context's Introduction

简中 | English

co_context

License Platform Language Ubuntu 22.04 (gcc) Ubuntu 22.04 (clang)

co_context 是一个协程并发框架,提供可靠的性能和强易用性,让 C++ 初学者也能轻松写出高并发程序。 co_context 基于 Linux io_uring,其性能通常优于 epoll。

不了解协程?

我在 Bilibili 上传了协程系列视频,请多指教!

已有功能

  1. 支持 read write accept timeout 等 io_uring 提供的所有系统调用,总计 74 个功能。
  2. 并发支持: any, some, all, mutex, semaphore, condition_variable, channel
  3. 调度提示: yield, resume_on
  4. 取消 IO/协程:timeout, timeout_at, stop_token

编译和运行

依赖

  1. GCC 11.3.0 或 Clang 14 或更高。
  2. [可选] mimalloc 从包管理器或源代码安装。
  3. Linux 内核版本 >= 5.6,建议 >= 5.11,越新越好。
    • 运行 uname -r 即可查看你的内核版本。
    • 由于开发环境是 Linux 6.0,在其他版本下可能出现兼容性错误。如遇问题,请将报错发到issue,非常感谢!
    • docker 将继承宿主机的 Linux 内核版本。 因此,docker 无法解决 Linux 内核版本过低的问题。

编译命令

cmake -B build && cmake --build build -j
build/example/timer # 跑一个小示例

代码示例

基础用法

创建一个 io_context,用于运行协程:

    using namespace co_context;
    io_context context;

使用 task<> 定义一个 socket 监听协程。task<> 就像一个普通的 void 函数,但可以在里面使用 co_await

task<> server(uint16_t port) {
    acceptor ac{inet_address{port}};
    for (int sockfd; (sockfd = co_await ac.accept()) >= 0;) {// 异步接受 client
        co_spawn(session(co_context::socket{sockfd})); // 每个连接生成一个 worker 任务
    }
}

继续使用 task<T> 描述业务逻辑,例如读取 socket 的内容并输出到 stdout:

task<> session(co_context::socket sock) {
    char buf[8192];
    int nr = co_await sock.recv(buf);

    // 不断接收字节流并打印到stdout
    while (nr > 0) {
        co_await lazy::write(STDOUT_FILENO, {buf, (size_t)nr});
        nr = co_await sock.recv(buf);
    }
}

task<T> 和普通 T 函数一样可以任意嵌套:

task<int> the_answer() {
    co_return 42;
}
task<> universe() {
    printf("The answer is %d\n", co_await the_answer());
    co_return;
}

如何写一个 main()

int main(int argc, const char *argv[]) {
    if (argc < 3) {
        printf("Usage:\n  %s hostname port\n  %s -l port\n", argv[0], argv[0]);
        return 0;
    }

    io_context context; // 1. 定义一个 io_context

    int port = atoi(argv[2]);
    if (strcmp(argv[1], "-l") == 0) {
        context.co_spawn(server(port)); // 2. 至少创建一个 task<>
    } else {
        context.co_spawn(client(argv[1], port));
    }

    context.start(); // 3. 启动 io_context 线程
    context.join();  // 4. 需要时等待 io_context 线程

    return 0;
}

更多特性示例

一秒触发器

task<> my_clock() {
    for (int cnt = 0;;) {
        printf("Time = %d\n", cnt++);
        co_await timeout(1s);
    }
}

网络超时

秒懂的超时控制。

task<> session(co_context::socket peer) {
    char buf[8192];
    int nr = co_await timeout(peer.recv(buf), 3s); // 限时3秒

    while (nr > 0) {
        co_await lazy::write(STDOUT_FILENO, {buf, (size_t)nr});
        nr = co_await timeout(peer.recv(buf), 3s); // 限时3秒
    }

    log_error(-nr);
}

void log_error(int err) {
    switch (err) {
        case ECANCELED:
            log::e("timeout!\n");
            break;
        default:
            log::e("%s\n", strerror(err));
            break;
    }
}

负载均衡

每个 io_context 代表一个线程。要负载均衡,只需将 task<> 分配到合理的 io_context

示例:echo_server_MT.cpp

借助 resume_on(),可以在运行时任意切换线程。

示例:resume_on.cpp

Generator

同步执行的生成器,可配合std::range需要 g++

co_context::generator<int> gen_iota(int x) {
    while (true)
        co_yield x++;
}

int main() {
    using namespace std::views;

    for (int x : gen_iota(1) | drop(5) | take(3)) {
        std::cout << x << " ";
    }
    // 输出 6 7 8

    return 0;
}

链接 IO

&& 来链接两个 I/O。链接 I/O 可以减少重入内核态和调度器,增强性能表现。(默认只返回最后一个返回值。)

nr = co_await (
    peer.send({buf, (size_t)nr})
    && peer.recv(buf)
);

此例子利用 link_io 大幅增强 echo_server 的性能

channel(实验性)

借鉴自 Go 语言的阻塞队列。

示例:channel.cpp

Draft

性能

co_context 在开发过程中表现出惊人的性能。早期测试见我的博客。下一个开发周期将进行更多测试。

协程方案的局限场景

由于内置动态内存分配,基于协程的异步框架可能不是性能的最优解,如果你正处于类似 30ns 延迟的极端性能场景,且不在乎编程复杂度,推荐关注 sender/receiver model,而无需尝试协程。

协程方案的适用场景

如果你希望异步框架能够最佳地平衡「开发、维护成本」和「项目质量、性能」,从而最大化经济效益,推荐你关注协程方案。感性理解:协程 + 内核态 I/O 的性能类似于 Redis 的网络模块。

关于缓存友好问题

co_context 竭尽所能避免缓存问题:

  1. co_context 的主线程和任意 worker 的数据交换中没有使用互斥锁,极少使用原子变量。
  2. co_context 的数据结构保证「可能频繁读写」的 cacheline 最多被两个线程访问,无论并发强度有多大。这个保证本身也不经过互斥锁或原子变量。(若使用原子变量,高竞争下性能损失约 33%~70%)
  3. 对于可能被多于两个线程读写的 cacheline,co_context 保证乒乓缓存问题最多发生常数次。
  4. 在 AMD-5800X,3200 Mhz-ddr4 环境下,若绕过 io_uring,co_context 的线程交互频率可达 1.25 GHz。
  5. 在一个本地测试中(I7-8550U 移动端),单线程的协程切换的平均延迟为 4.6 ns,代码于 test/benchmark/lazy_yield
  6. 在一个本地测试中(R7-5800X 桌面端),跨线程的协程切换的平均延迟为 37 ns,代码于 test/ctx_swtch.cpp
  7. 协程自身的缓存不友好问题(主要由 operator new 引起),需要借助其他工具来解决,例如 mimalloc

协程存在的问题

弱点

  1. 除非编译器优化,每个协程都需要通过 operator new 来分配 frame:
    • 多线程高频率动态内存分配可能引发性能问题;
    • 在嵌入式或异构(例如 GPU)环境下,缺乏动态内存分配能力,难以工作。
  2. 除非编译器优化,协程的可定制点太多,需要大量间接调用/跳转(而不是内联),同样引起性能问题。
    • 目前,编译器通常难以内联协程
    • HALO 优化理论:P0981R0
  3. 动态分配间接调用的存在,导致协程暂时无法成为异步框架的最优方法。

拆分子协程?

  • 出于性能考虑,不要将大协程拆分为几个小协程,因为会增加动态内存分配次数。
    • 可以做 placement new 吗?

与异步框架高度耦合

  1. 暂停和恢复都需要通过异步框架。
  2. 表达式模板的潜力不如 sender/receiver 模型:
    • 协程是顺序/分支/循环结构,s/r是表达式。

draft

  • 研究 liburingcxx 如何支持多生产者,多消费者并行(线程池中每个线程同时是 IO 生产者和消费者)
  • Coroutine 解决内联和动态内存分配问题
  • 表达式模板解决 task && ||
  • std::execution 能否兼容

线程池实现

  • 一个内核线程 polling,一个主线程收集提交、收割推送I/O,其他固定 worker 线程,thread bind core
  • 节能模式:信号量表示允许的 idle worker 线程数量。低延模式:每个 worker 都 polling
  • 每个 worker 自带两条任务队列(一个sqe,一个cqe),固定长度,原子变量,cacheline友好。sqe放不下就放 std::queue,等有空位再放入共享cache。
  • 主线程cqe推送满了就切换到提交sqe
  • 主线程sqe提交满了就切换到推送cqe

eager_io

一种激进的 IO awaiter,在构造函数中初始化 IO 请求并提交。

在被 co_await 时,若 IO 早已完成,则无需让出。否则,需要等待 IO 完成后由调度器唤醒。

eager_io 的动机

  1. 可以轻易部署并行化的 IO 请求,且对于 caller 协程来说是非阻塞的。还可进化出可取消的协程。
  2. 尽早提交 IO 请求,可能带来更低的延迟。

eager_io 的缺点

涉及多线程并行,需要同步 IO 的状态(未完成、已完成)。至少要保证:调度器必须确保 「eager_io 已经知悉 IO 已完成」,否则可能丢数据。

eager_io 的实现

TODO: 改用原子变量,弃用检查队列

co_context 假设大多数 eager_io 会陷入「等待状态」,以此为优化立足点

  1. eager_io 的 coroutine state(promise) 是调度器负责决定由谁销毁(由调度器或者由协程自己)。
  2. eager_io 发起 IO 前,自我标记为「初始状态」「无结果」「无权销毁」,然后发起 IO。
  3. eager_io 在「初始状态」下被 co_await,检查结果:
    1. 为「无结果」,则自我标记为「等待状态」「有权销毁」「有结果」,让出执行权
    2. 为「有结果」,自我标记为「IO 后状态」(保持「无权销毁」),继续执行。
    3. 析构时,「有权销毁」则销毁协程,否则自我标记为「待销毁」。
  4. 调度器收割 IO 时,检查协程的标记:
    1. 为「等待状态」,则将协程加入调度队列,令其自行销毁。
    2. 为「初始状态」(初始、等待叠加态),向协程标记「有结果」,随后将协程加入检查队列
  5. 调度器完成一轮提交/收割后,轮询检查队列:
    1. 若协程为「等待状态」,则弹出检查队列,并加入调度队列,令其自行销毁。
    2. 若协程为「初始状态」或者「IO 后状态」,不管它。
    3. 若协程为「待销毁」,销毁它,弹出检查队列。

xxx <-> is_detached is_waiting is_ready

manager:

  • ready: xx0 to xx1
    • 1x1 : manager delete task_info, do not resume.
    • 001 : worker will delete task_info, do not resume.
    • 011 : worker will delete task_info, resume

worker:

  • wait: x0x to x1x
    • 11x : wait after detached, logic error
    • 010 : suspend, worker will delete task_info
    • 011 : do not suspend, worker will delete task_info
  • detach: 0xx to 1xx
    • 1x1 : worker will delete task_info
    • 100 : manager will delete task_info
    • 110 : detach after waited, logic error

此实现中可能的漏洞:

  1. 未反省协程发生异常时的内存模型
  2. 等你来发现……

lazy_io

一种懒惰的 IO awaiter,在,在构造函数时什么都不做。

在被 co_await 时暂停,并发起 IO 请求,未来等待由调度器唤醒。当前线程轮询可以切入的协程。

lazy_io 的实现

  1. lazy_io 返回一个 awaiter,其中的 await_suspend 负责主要逻辑:
    1. 提交一个 IO 请求。
    2. 找到一个已收割的 IO 请求,恢复它
  2. awaiterawait_resume 返回特定结果。
  3. 析构时,销毁协程。

semaphore

仅运行在用户态 co_context 的信号量

semaphore 的动机

限制 co_spawn 和同类活跃协程的并发量

semaphore 的实现

  1. 参考 std::semaphore,优化 binary_semaphore 的原子变量
  2. 链表栈模拟无锁队列,均摊O(1)
  3. acquire 分别在栈上创建 awaiter,形成等待链表
  4. release 时放出一个release请求,由io_context处理(强制单消费者),放入某个reap_swap

co_context's People

Contributors

codesire-deng avatar sunflower-knight avatar tchaikov avatar tianpingan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

co_context's Issues

sender/receiver model 是如何玩?

"由于内置动态内存分配,基于协程的异步框架可能不是性能的最优解,如果你正处于类似 30ns 延迟的极端性能场景,且不在乎编程复杂度,推荐关注 sender/receiver model,而无需尝试协程”, 老师,我目前的场景就是极致的最求性能,那么这个 sender/receiver model应该是怎么玩呢?有没有对应的事例呢。谢谢

在qemu下无法进行端口转发

我正确实现了我的httpd,并在x86下编译成功,使用 #88 提到的交叉编译的方法,将编译出的服务程序放到qemu模拟器中的linux下运行发现无法实现端口转发。我使用libevent实现的服务器是可以实现端口转发的。

我在服务器中同时实现了client, client可以与server进行通信,唯一的问题是,无法将服务端口转发到qemu外面。
在qemu中 同时运行client和server,成功
image
使用curl在qemu外连接服务器失败
image

环境

  • host: ubuntu 22.04 6.2.0-36-generic
  • qemu: 6.2.94
  • inux kernel for qemu-riscv64: 5.19
  • riscv-toolchain gcc : 12.2.0
# riscv编译器环境
docker pull registry.cn-hangzhou.aliyuncs.com/loongenclave/riscv-basic:12.2.0

qemu启动命令

qemu-system-riscv64 \
-machine virt -nographic -m 2048 -smp 4 \
-bios /usr/lib/riscv64-linux-gnu/opensbi/generic/fw_jump.bin \
-kernel /usr/lib/u-boot/qemu-riscv64_smode/uboot.elf \
-device virtio-net-device,netdev=eth0 -netdev user,id=eth0,hostfwd=tcp::2222-:22,hostfwd=tcp::8080-:8080,hostfwd=tcp::8081-:8081 \
-device virtio-rng-pci \
-drive file=ubuntu-22.04.3-preinstalled-server-riscv64+unmatched.img,format=raw,if=virtio

ssh上传文件的脚本

#!/bin/bash


SSH_OPTIONS=""
USER=ubuntu
PORT=2222

upload_folder_to_qemu() {
    echo "Uploading \"$1\" folder to QEMU ..."
    scp ${SSH_OPTIONS} -P ${PORT} -r $1 ${USER}@localhost:.
}

upload_to_qemu() {
    echo "Uploading \"$(basename $1)\" to QEMU ..."
    scp ${SSH_OPTIONS} -P ${PORT} $1 ${USER}@localhost:.
}

run_in_qemu() {
    echo "Running \"$1\" in QEMU ..."
    ssh ${SSH_OPTIONS} -p ${PORT} ${USER}@localhost "$1"
}

upload_to_qemu /home/wang/Documents/keystone/co-server/build/cross/riscv64/release/coroutine

编译报错fatal error: linux/time_types.h: No such file or directory

What happened + What you expected to happen

编译环境

# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
# uname -r
5.10.0-2.el7.ucloud.x86_64

编译报错

In file included from /root/co_context/include/co_context/config.hpp:3,
                 from /root/co_context/include/co_context/detail/io_context_meta.hpp:3,
                 from /root/co_context/include/co_context/co/mutex.hpp:4,
                 from /root/co_context/include/co_context/co/condition_variable.hpp:3,
                 from /root/co_context/lib/co_context/co/condition_variable.cpp:1:
/root/co_context/include/uring/io_uring.h:20:10: fatal error: linux/time_types.h: No such file or directory
   20 | #include <linux/time_types.h>
      |          ^~~~~~~~~~~~~~~~~~~~
compilation terminated.

参考
axboe/liburing#708

co_context中没有检查是否需要设置UAPI_LINUX_IO_URING_H_SKIP_LINUX_TIME_TYPES_H

多线程通信

场景描述

我在同一个进程里面设计了两个线程A和B,其中A负责加密运算,B负责处理HTTP请求。
B是使用协程实现的高并发WEB服务器,A是一个死循环,一直等待B的数据。B在最终将数据发送给客户端之前,都需要将数据发送给A线程,由A线程进行加密。由于A是计算密集型的,因此使用协程反而会导致效率下降。A线程在加密完成之后再将数据发给B线程,由B线程将数据发送给客户端。
请问在这种情况下该如何实现呢?

test下示例编译出错

  1. 对于liburingcxx::uring模板类,没有一个可以由整数构造的构造函数,导致以下代码出错:
    liburingcxx::uring<0> ring{4};
  1. 在rand4kr_callback.cpp中,包含了不存在的头文件"co_context/utility/buffer.hpp"
  2. config::workers_number不存在

编译错误

gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 11.4.0-1ubuntu122.04' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-11 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-11-XeT9lY/gcc-11-11.4.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-11-XeT9lY/gcc-11-11.4.0/debian/tmp-gcn/usr --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1
22.04)

uname -a

Linux aaa-virtual-machine 6.2.0-35-generic #35~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Oct 6 10:23:26 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

What happened + What you expected to happen

cmake -B build && cmake --build build -j

                                    ;

:3:
/home/aaa/co_context/include/co_context/co/when_all.hpp:94:24: error: missing 'typename' prior to dependent type name 'meta_type::value_type'
using value_type = meta_type::value_type;
^~~~~~~~~~~~~~~~~~~~~
typename
:104:65: error: use of undeclared identifier 'variant_type'
std::conditional_t<is_all_void, index_type, index_value<variant_type>>;
^
In file included from In file included from In file included from /home/aaa/co_context/example/echo_server_MT.cpp:2:
In file included from /home/aaa/co_context/include/co_context/net.hpp:4:
/home/aaa/co_context/include/co_context/co/when_any.hpp:106:32: error: use of undeclared identifier 'variant_type'
using meta_type = any_meta<variant_type>;
^
In file included from /home/aaa/co_context/example/when_all.cpp:1:
In file included from /home/aaa/co_context/include/co_context/all.hpp:10:
In file included from /home/aaa/co_context/include/co_context/net.hpp:3:
/home/aaa/co_context/include/co_context/co/when_all.hpp:69:/home/aaa/co_context/example/echo_server.cpp:1:
In file included from /home/aaa/co_context/include/co_context/net.hpp:4:
fatal error: /home/aaa/co_context/include/co_context/co/when_any.hpp:104:65: error: use of undeclared identifier 'variant_type'
std::conditional_t<is_all_void, index_type, index_value<variant_type>>;
^

Reproduction way

Anything else

如何实现交叉编译

我观察到cmake/Platform.cmake里面有检查Linux内核的代码。我现在想在riscv的内核上面跑,我的开发环境是x86,因此需要使用riscv的toolchain进行交叉编译,请问该如何实现呢?
另外,我自己编译的是linux 5.15的内核,请问在编译内核时需要特殊的编译选项吗?
开发环境内核:6.2
riscv主板内核:5.15
riscv-toolchain-gcc: 12.2.0

关于本地编译使用的咨询

大佬,您好。从b站看到您的视频介绍,找到这里,想学习使用一下。

了解到这个工程 需要依赖 mimalloc v2.0以上 find_package(mimalloc 2.0 REQUIRED)
我在自己本地的 mac-m1笔记本上,通过 brew install mimalloc 无法安装 v2或以上的版本

image

能否提供一些一下关于这方面的支持,比如将mimalloc降低到v1.7.6-stable,或者其他快速引入 mimalloc的方式,

以便我们快速在本地,跑起来 example里的内容。感谢

有没有支持协程间通信机制的实现计划?

就是类似 goroutine 的 channel 和 skynet lua 层的消息队列这种?
还是说目前并发工具包已经能提供足够的功能。

另外与通信相似的一个 cancellation 的问题有没有支持的计划?
目前大部分的对于 io_uring 的封装对 cancellation 的支持都不太理想。
谢谢。

How about refactor this project using `Modules`?

I think your project do well. And it has the newest feature both in C++ and the OS. But one important feature modules in C++20 is missed. I think it's a nice feature. If you would , I could help you to do it .

如何使用mimalloc静态库

What happened + What you expected to happen

co_context使用的mimalloc动态库

if (CO_CONTEXT_USE_MIMALLOC)
    target_link_libraries(co_context PUBLIC mimalloc)
endif()

在一些生产环境的机器上安装mimalloc动态库比较困难,可以增加使用静态库的编译方式吗

我尝试直接将cmake改为mimalloc-static没有编译成功

if (CO_CONTEXT_USE_MIMALLOC)
    target_link_libraries(co_context PUBLIC mimalloc-static)
endif()

when_any when_all 代码有错误

    spawn_all(std::index_sequence_for<task_types...>{});
    co_await lazy::forget();  // std::suspend_always;

co_await lazy::forget(); 基于这个挂起肯定是错误的, spawn_all可能立即恢复, 恢复一个没有挂起的协程.

关于协程调度的问题

问题背景

我正在学习协程的内容,我参照 example 中的例子尝试用 co_context 库写了一个 echo_client 和 echo_server,其中 echo_client 借鉴了 muduo 中的例子,并想基于此和 muduo 进行性能对比,但程序运行结果和预想的不一致。

问题描述

我的运行环境为:阿里云 ECS 云服务器,2核(vCPU) 2GiB,Ubuntu 22.04,g++ 11.4.0

muduo 的实现代码链接为 https://github.com/chenshuo/muduo/tree/master/examples/pingpong

使用 muduo 中提供的 脚本 直接运行,测试结果为:

pingpong_server: no process found
Bufsize: 16384 Threads: 1 Sessions: 1
20240423 08:05:48.429949Z 630711 INFO pid = 630711, tid = 630711 - server.cc:38
20240423 08:05:49.430760Z 630713 INFO pid = 630713, tid = 630713 - client.cc:197
20240423 08:05:49.431014Z 630713 WARN all connected - client.cc:122
20240423 08:05:59.430831Z 630713 WARN stop - client.cc:158
20240423 08:05:59.431107Z 630713 WARN all disconnected - client.cc:130
20240423 08:05:59.431125Z 630713 WARN 4261986304 total bytes read - client.cc:139
20240423 08:05:59.431132Z 630713 WARN 260131 total messages read - client.cc:140
20240423 08:05:59.431138Z 630713 WARN 16384 average message size - client.cc:141
20240423 08:05:59.431149Z 630713 WARN 406.4546875 MiB/s throughput - client.cc:143
Bufsize: 16384 Threads: 1 Sessions: 10
20240423 08:06:04.435839Z 630754 INFO pid = 630754, tid = 630754 - server.cc:38
20240423 08:06:05.437393Z 630757 INFO pid = 630757, tid = 630757 - client.cc:197
20240423 08:06:05.437965Z 630757 WARN all connected - client.cc:122
20240423 08:06:15.437484Z 630757 WARN stop - client.cc:158
20240423 08:06:15.437957Z 630757 WARN all disconnected - client.cc:130
20240423 08:06:15.437972Z 630757 WARN 17275011072 total bytes read - client.cc:139
20240423 08:06:15.437974Z 630757 WARN 1054383 total messages read - client.cc:140
20240423 08:06:15.438011Z 630757 WARN 16384 average message size - client.cc:141
20240423 08:06:15.438021Z 630757 WARN 1647.4734375 MiB/s throughput - client.cc:143
Bufsize: 16384 Threads: 1 Sessions: 100
20240423 08:06:20.442012Z 630820 INFO pid = 630820, tid = 630820 - server.cc:38
20240423 08:06:21.442723Z 630823 INFO pid = 630823, tid = 630823 - client.cc:197
20240423 08:06:21.447357Z 630823 WARN all connected - client.cc:122
20240423 08:06:31.443814Z 630823 WARN stop - client.cc:158
20240423 08:06:31.445412Z 630823 WARN all disconnected - client.cc:130
20240423 08:06:31.445427Z 630823 WARN 19154436096 total bytes read - client.cc:139
20240423 08:06:31.445430Z 630823 WARN 1169094 total messages read - client.cc:140
20240423 08:06:31.445431Z 630823 WARN 16384 average message size - client.cc:141
20240423 08:06:31.445446Z 630823 WARN 1826.709375 MiB/s throughput - client.cc:143
Bufsize: 16384 Threads: 1 Sessions: 1000
20240423 08:06:36.449339Z 630884 INFO pid = 630884, tid = 630884 - server.cc:38
20240423 08:06:37.450658Z 630889 INFO pid = 630889, tid = 630889 - client.cc:197
20240423 08:06:37.495573Z 630889 WARN all connected - client.cc:122
20240423 08:06:47.451004Z 630889 WARN stop - client.cc:158
20240423 08:06:47.484683Z 630889 WARN all disconnected - client.cc:130
20240423 08:06:47.484710Z 630889 WARN 12080676864 total bytes read - client.cc:139
20240423 08:06:47.484859Z 630889 WARN 737346 total messages read - client.cc:140
20240423 08:06:47.484863Z 630889 WARN 16384 average message size - client.cc:141
20240423 08:06:47.484912Z 630889 WARN 1152.103125 MiB/s throughput - client.cc:143
Bufsize: 16384 Threads: 1 Sessions: 10000
20240423 08:06:52.490363Z 630949 INFO pid = 630949, tid = 630949 - server.cc:38
20240423 08:06:53.490886Z 630963 INFO pid = 630963, tid = 630963 - client.cc:197
20240423 08:06:53.909448Z 630963 WARN all connected - client.cc:122
20240423 08:07:03.537228Z 630963 WARN stop - client.cc:158
20240423 08:07:03.933908Z 630963 WARN all disconnected - client.cc:130
20240423 08:07:03.934199Z 630963 WARN 10123739136 total bytes read - client.cc:139
20240423 08:07:03.934213Z 630963 WARN 617904 total messages read - client.cc:140
20240423 08:07:03.934215Z 630963 WARN 16384 average message size - client.cc:141
20240423 08:07:03.934220Z 630963 WARN 965.475 MiB/s throughput - client.cc:143

我写的 echo_client 和 echo_server 程序贴在末尾。
运行结果为:
echo_server: no process found
Bufsize: 16384 Threads: 1 Sessions: 1
Tue Apr 23 16:20:17 2024
client start...
all connected
stop.
all disconnected
4071440384 total bytes read
248501 total messages read
16384 bytes per message
388.283 MiB/s throughout
Tue Apr 23 16:20:27 2024
client end...

Bufsize: 16384 Threads: 1 Sessions: 10
Tue Apr 23 16:20:33 2024
client start...
all connected
stop.
all disconnected
15644426240 total bytes read
954860 total messages read
16384 bytes per message
1491.97 MiB/s throughout
Tue Apr 23 16:20:43 2024
client end...

Bufsize: 16384 Threads: 1 Sessions: 100
Tue Apr 23 16:20:49 2024
client start...
all connected
stop.
all disconnected
17007493120 total bytes read
1038055 total messages read
16384 bytes per message
1621.96 MiB/s throughout
Tue Apr 23 16:20:59 2024
client end...

Bufsize: 16384 Threads: 1 Sessions: 1000
Tue Apr 23 16:21:05 2024
client start...
all connected
stop.
all disconnected
8327217152 total bytes read
508253 total messages read
16384 bytes per message
794.145 MiB/s throughout
Tue Apr 23 16:21:15 2024
client end...

Bufsize: 16384 Threads: 1 Sessions: 10000
Tue Apr 23 16:21:21 2024
client start...
stop.
all disconnected
259922837504 total bytes read
15864431 total messages read
16384 bytes per message
24788.2 MiB/s throughout
Tue Apr 23 16:26:12 2024
client end...

测试并发量的时长都是 10s

由上面贴出的muduo的运行结果和我写的测试程序的运行结果可知,当并发量为1000时,使用 co_context 写的测试程序在性能上有下降。当并发量为10000时,Client::handleTimeout函数执行时间太长(上面结果的标粗部分)。

我猜测是 Session::stop() 中 使用 mutex 保护了 co_await,但不确定,希望能够解答。若是 mutex 的原因,我该如何修改程序呢?

另一方面,对于初学协程的我,对协程调度的有疑惑。在 co_context 中:

  1. 我 co_spawn 了一个新的协程,这个 co_spawn 什么时候被调度呢?是不是在底层,有类似队列的东西?按先进先出的顺序调度。
  2. 当我 co_await 了一个操作,会暂停当前协程,让出控制权。那当这个操作完成后,会及时的恢复这个协程吗?

我的实现

测试脚本为:

#!/bin/sh

killall echo_server
timeout=${timeout:-10}
bufsize=${bufsize:-16384}
nothreads=1

for nosessions in 1 10 100 1000 10000; do
  sleep 5
  echo "Bufsize: $bufsize Threads: $nothreads Sessions: $nosessions"
  taskset -c 0 ./echo_server 33333 & srvpid=$!
  sleep 1
  taskset -c 1 ./echo_client 127.0.0.1 33333 $nothreads $bufsize $nosessions $timeout
  kill -9 $srvpid
done
// echo_server.cpp

#include <co_context/net.hpp>
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
using namespace co_context;

task<> session(int sockfd)
{
    co_context::socket sock{sockfd};
    constexpr int len = 16384;
    char buf[len];

    while (true)
    {
        auto nr = co_await lazy::recv(sockfd, buf);
        if (nr <= 0) {
            co_await lazy::close(sockfd);
            co_return;
        }

        co_await lazy::send(sockfd, {buf, static_cast<size_t>(nr)});
    }
}

task<> server(const uint16_t port)
{
    /*
        使用 co_contenxt 提供的 helper
        为简化工作,不考虑一些错误处理,可以直接使用 co_context 提供的 acceptor 类
    */ 
    inet_address listen_addr{port};
    const int listen_sockfd = ::socket(listen_addr.family(), SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
    if (listen_sockfd < 0) {
        std::cerr << "socket error: " << strerror(errno) << std::endl;
        co_return;
    }
    
    int optval = 1;
    if (::setsockopt(listen_sockfd, SOL_SOCKET, SO_REUSEADDR, 
        &optval, static_cast<socklen_t>(sizeof(optval))) < 0) {
        std::cerr << "setsockopt SO_REUSEADDR error: " << strerror(errno) << std::endl;
        co_return;
    }

    optval = 1;
    if (::setsockopt(
            listen_sockfd, IPPROTO_TCP, TCP_NODELAY, &optval,
            static_cast<socklen_t>(sizeof optval)
        ) < 0) {
        std::cout << "setsockopt TCP_NODELAY error: " << strerror(errno) << std::endl;
        co_return;
    }

    if (::bind(listen_sockfd, listen_addr.get_sockaddr(), listen_addr.length()) != 0) {
        std::cerr << "bind error: " << strerror(errno) << std::endl;
        co_return;
    }

    if (::listen(listen_sockfd, SOMAXCONN) != 0) {
        std::cerr << "listen error: " << strerror(errno) << std::endl;
        co_return;
    }

    for (; ;) {
        int sockfd = co_await lazy::accept(listen_sockfd, nullptr, nullptr, 0);
        if (sockfd < 0) {
            std::cerr << "accept error: " << strerror(errno) << std::endl;
            co_return;
        }
        co_spawn(session(sockfd));
    }
}


/*
    ./echo_server 33333
*/
int main(int argc, char *argv[])
{
    if (argc != 2) {
        std::cerr << "Usage: " << argv[0] << " <port>\n";
        return 1;
    }

    uint16_t port = static_cast<uint16_t>(std::atoi(argv[1]));
    io_context ctx;
    ctx.co_spawn(server(port));
    ctx.start();
    ctx.join();
    return 0;
}
// echo_client.cpp

#include <co_context/net.hpp>
#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <string>
#include <atomic>
#include <vector>
#include <chrono>
#include <mutex>

using Second = int;

class Client;

class Session
{
public:
    Session(co_context::io_context *io_context,
            Client *client,
            const co_context::inet_address &addr) : io_context_(io_context),
                                                    client_(client),
                                                    serverAddr_(addr),
                                                    socket_(::socket(serverAddr_.family(), SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP)),
                                                    bytesRead_(0),
                                                    byteWritten_(0),
                                                    messagesRead_(0)
    {
    }

    co_context::task<> start();

    co_context::task<> onMessage();

    co_context::task<> stop();

    int64_t bytesRead() const { return bytesRead_; }

    int64_t messagesRead() const { return messagesRead_; }

private:
    co_context::io_context *io_context_;
    Client *client_;
    co_context::inet_address serverAddr_;
    co_context::socket socket_;
    std::vector<char> buffers_;
    int64_t bytesRead_;
    int64_t byteWritten_;
    int64_t messagesRead_;
    std::mutex mutex_;
};

class Client
{
public:
    Client(co_context::io_context *io_context,
           const co_context::inet_address &serverAddr,
           int blockSize,
           int sessionCount,
           Second timeout, int threadCount) : io_context_(io_context),
                                             addr_(serverAddr),
                                             blockSize_(blockSize),
                                             sessionCount_(sessionCount),
                                             timeout_(timeout),
                                             numConntected_(0)
    {
        if (threadCount > 1)
        {
            // TODO: 多线程
        }

        /* 生成数据包 */
        for (int i = 0; i < blockSize; ++i)
        {
            message_.push_back(static_cast<char>(i % 128));
        }
    }

    co_context::task<> start()
    {

        for (int i = 0; i < sessionCount_; ++i)
        {
            Session *session = new Session(io_context_, this, addr_);
            io_context_->co_spawn(session->start());
            // co_await session->start();
            sessions_.emplace_back(session);
        }  

        co_await handleTimeout();
    }

    const std::string &message() const { return message_; }

    int blockSize() const { return blockSize_; }

    void onConnect()
    {
        // std::cout << numConntected_ << " connected" << std::endl;
        if (++numConntected_ == sessionCount_)
        {
            std::cout << "all connected" << std::endl;
        }
    }

    void onDisconnect()
    {
        // std::cout << numConntected_ << " disconnected" << std::endl;
        if (--numConntected_ == 0)
        {
            std::cout << "all disconnected" << std::endl;

            int64_t totalBytesRead = 0;
            int64_t totalMessagesRead = 0;

            for (const auto &session : sessions_)
            {
                totalBytesRead += session->bytesRead();
                totalMessagesRead += session->messagesRead();
            }

            std::cout << totalBytesRead << " total bytes read" << std::endl;
            std::cout << totalMessagesRead << " total messages read" << std::endl;
            std::cout << static_cast<double>(totalBytesRead) / static_cast<double>(totalMessagesRead)
                      << " bytes per message" << std::endl;
            std::cout << static_cast<double>(totalBytesRead) / (timeout_ * 1024 * 1024)
                      << " MiB/s throughout" << std::endl;

            auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
            std::cout << std::asctime(std::localtime(&now)) << " client end..." << std::endl;
        }
    }

private:
    co_context::task<> handleTimeout()
    {
        auto deadline = std::chrono::steady_clock::now();
        deadline += std::chrono::seconds(timeout_);
        co_await co_context::timeout_at(deadline);

        std::cout << "stop." << std::endl;
        for (auto &session : sessions_)
        {
            co_await session->stop();
        }
    }

private:
    co_context::io_context *io_context_;
    const co_context::inet_address addr_;
    int blockSize_;
    int sessionCount_;
    Second timeout_;
    std::atomic<int> numConntected_;
    std::string message_;
    std::vector<std::unique_ptr<Session>> sessions_;
};


co_context::task<> Session::start()
{
    // FIXME: connect error ?
    int ret = co_await socket_.connect(serverAddr_);
    if (ret < 0)
    {
        std::cerr << "connect error" << std::endl;
    }

    socket_.set_tcp_no_delay(true);
    client_->onConnect();

    io_context_->co_spawn(onMessage());
    
    // FIXME: send error ?
    int sn = co_await socket_.send(client_->message());
}

co_context::task<> Session::onMessage()
{
    char buf[16384];

    while (true)
    {
        int nr = co_await socket_.recv(buf);
        if (nr <= 0)
        {
            co_return;
        }
        ++messagesRead_;
        bytesRead_ += nr;

        int ns = co_await socket_.send(buf, static_cast<size_t>(nr));
        if (ns <= 0)
        {
            co_return;
        }
        byteWritten_ += ns;
    }
}

co_context::task<> Session::stop()
{
    {
        std::unique_lock<std::mutex> locker(mutex_);
        co_await socket_.shutdown_write();
    }
    
    client_->onDisconnect();
}


/*
    ./echo_client 127.0.0.1 33333 1 16384 100 10
*/
int main(int argc, char *argv[])
{
    auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    std::cout << std::asctime(std::localtime(&now)) << " client start..." << std::endl;

    if (argc != 7)
    {
        fprintf(stderr, "Usage: client <host_ip> <port> <threads> <blocksize> <sessions> <time>\n");
    }
    else
    {
        const char *ip = argv[1];
        uint16_t port = static_cast<uint16_t>(atoi(argv[2]));
        int threadCount = atoi(argv[3]);
        int blockSize = atoi(argv[4]);
        int sessionCount = atoi(argv[5]);
        int timeout = atoi(argv[6]);

        co_context::io_context io_ctx;
        co_context::inet_address serverAddr(ip, port);
        
        Client client(&io_ctx, serverAddr, blockSize, sessionCount, timeout, threadCount);
        io_ctx.co_spawn(client.start());
        io_ctx.start();
        io_ctx.join();
    }
}

License

现在 co_context 使用的许可证是 AGPL。所以对于一般的闭源服务来说,如果链接使用 co_context,我们只能对内提供服务。有没有考虑使用更宽松的许可证,比如 MIT license,或者 apache license 2.0 呢?

[bug] Segmentation fault happened when it loops

#include <iostream>
#include "co_context/io_context.hpp"
co_context::task<int> func() { co_return 6; }
int main() {
    co_context::io_context ctx;
    ctx.co_spawn([]() -> co_context::task<> {
        while (true) {
            auto x = co_await func();
            std::cout << x << std::endl;
        }
    }());
    ctx.start();
    ctx.join();
}

在这种情况下,会发生segmentation fault.

The report of valgrind

==146874==
==146874== Process terminating with default action of signal 11 (SIGSEGV)
==146874== Bad permissions for mapped region at address 0x5474FF8
==146874== at 0x4EE8D40: fwrite (iofwrite.c:31)
==146874==
==146874== HEAP SUMMARY:
==146874== in use at exit: 75,160 bytes in 5 blocks
==146874== total heap usage: 65,501 allocs, 65,496 frees, 3,742,936 bytes allocated
==146874==
==146874== 304 bytes in 1 blocks are possibly lost in loss record 3 of 5
==146874== at 0x4848A13: calloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==146874== by 0x4012CE9: calloc (rtld-malloc.h:44)
==146874== by 0x4012CE9: allocate_dtv (dl-tls.c:375)
==146874== by 0x4012CE9: _dl_allocate_tls (dl-tls.c:634)
==146874== by 0x4EFF259: allocate_stack (allocatestack.c:423)
==146874== by 0x4EFF259: pthread_create@@GLIBC_2.34 (pthread_create.c:652)
==146874== by 0x4CE9438: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_deletestd::thread::_State >, void (*)()) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.32)
==146874== by 0x10DC82: co_context::io_context::start() (in /home/tpa/Heimerdinger-Lab/Karma/build/benchmark/benchmark)
==146874== by 0x10B605: main (in /home/tpa/Heimerdinger-Lab/Karma/build/benchmark/benchmark)
==146874==
==146874== LEAK SUMMARY:
==146874== definitely lost: 0 bytes in 0 blocks
==146874== indirectly lost: 0 bytes in 0 blocks
==146874== possibly lost: 304 bytes in 1 blocks
==146874== still reachable: 74,856 bytes in 4 blocks
==146874== suppressed: 0 bytes in 0 blocks
==146874== Reachable blocks (those to which a pointer was found) are not shown.
==146874== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==146874==
==146874== For lists of detected and suppressed errors, rerun with: -s
==146874== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
[1] 146874 segmentation fault (core dumped) valgrind --leak-check=yes ./benchmark

请求增加使用示例

What happened + What you expected to happen

想使用co_context改造之前基于muduo写的小项目,对co_context如何发请求还不太熟悉。

使用muduo发送请求时,函数声明如下

SendRequest(const std::string &ip, int port, const void *buf, size_t buf_len, Callback res_cb, Callback timeout_cb, double time);

包括收到响应后如何处理,超时如何处理两方面。

在看co_context的example后,不太明白使用co_context应该如何处理。

所以可以添加一个发送请求并有超时处理的实例代码嘛

编译报错error: '__NR_io_uring_register' was not declared in this scope

编译环境

# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
# uname -r
5.10.0-2.el7.ucloud.x86_64
# ldd --version
ldd (GNU libc) 2.17
Copyright (C) 2012 Free Software Foundation, Inc.

编译报错信息

In file included from /root/co_context/include/uring/syscall.hpp:32,
                 from /root/co_context/include/uring/uring.hpp:36,
                 from /root/co_context/include/co_context/detail/uring_type.hpp:6,
                 from /root/co_context/include/co_context/detail/thread_meta.hpp:4,
                 from /root/co_context/include/co_context/detail/lazy_io_awaiter.hpp:3,
                 from /root/co_context/include/co_context/lazy_io.hpp:4,
                 from /root/co_context/lib/co_context/co/spin_mutex.cpp:2:
/root/co_context/include/uring/arch/x86/../syscall-defs.h: In function 'int __sys_io_uring_register(unsigned int, unsigned int, const void*, unsigned int)':
/root/co_context/include/uring/arch/x86/../syscall-defs.h:67:36: error: '__NR_io_uring_register' was not declared in this scope; did you mean '__sys_io_uring_register'?
   67 |         return (int) __do_syscall4(__NR_io_uring_register, fd, opcode, arg,
      |                                    ^~~~~~~~~~~~~~~~~~~~~~
/root/co_context/include/uring/arch/x86/syscall.h:89:24: note: in definition of macro '__do_syscall4'
   89 |                 : "a"((NUM)),   /* %rax */                              \
      |                        ^~~
/root/co_context/include/uring/arch/x86/../syscall-defs.h: In function 'int __sys_io_uring_setup(unsigned int, io_uring_params*)':
/root/co_context/include/uring/arch/x86/../syscall-defs.h:74:36: error: '__NR_io_uring_setup' was not declared in this scope; did you mean '__sys_io_uring_setup'?
   74 |         return (int) __do_syscall2(__NR_io_uring_setup, entries, p);
      |                                    ^~~~~~~~~~~~~~~~~~~
/root/co_context/include/uring/arch/x86/syscall.h:59:24: note: in definition of macro '__do_syscall2'
   59 |                 : "a"((NUM)),   /* %rax */      \
      |                        ^~~
/root/co_context/include/uring/arch/x86/../syscall-defs.h: In function 'int __sys_io_uring_enter2(unsigned int, unsigned int, unsigned int, unsigned int, sigset_t*, size_t)':
/root/co_context/include/uring/arch/x86/../syscall-defs.h:82:36: error: '__NR_io_uring_enter' was not declared in this scope; did you mean '__sys_io_uring_enter2'?
   82 |         return (int) __do_syscall6(__NR_io_uring_enter, fd, to_submit,
      |                                    ^~~~~~~~~~~~~~~~~~~
/root/co_context/lib/co_context/io_context.cpp: In member function 'void co_context::io_context::init()':
/root/co_context/lib/co_context/io_context.cpp:27:19: error: '::gettid' has not been declared; did you mean 'getuid'?
   27 |     this->tid = ::gettid();
      |                   ^~~~~~
      |                   getuid
In file included from /root/co_context/lib/co_context/co/spin_mutex.cpp:2:
/root/co_context/include/co_context/lazy_io.hpp: In function 'co_context::detail::lazy_timeout co_context::lazy::timeout_at(std::chrono::time_point<std::chrono::_V2::system_clock, _Dur2>)':
/root/co_context/include/co_context/lazy_io.hpp:204:17: error: static assertion failed: io_uring timeouts only use the CLOCK_MONOTONIC clock source until 5.15. That means the clock must be steady.
  203 |             liburingcxx::is_kernel_reach(5, 15)
      |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  204 |                 || std::chrono::system_clock::is_steady,
      |                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

rhel9 编译错误

image

[root@localhost co_context]# make
[ 1%] Building CXX object lib/co_context/CMakeFiles/co_context.dir/co/condition_variable.cpp.o
[ 2%] Building CXX object lib/co_context/CMakeFiles/co_context.dir/co/mutex.cpp.o
[ 3%] Building CXX object lib/co_context/CMakeFiles/co_context.dir/co/semaphore.cpp.o
[ 5%] Building CXX object lib/co_context/CMakeFiles/co_context.dir/io_context.cpp.o
[ 6%] Building CXX object lib/co_context/CMakeFiles/co_context.dir/net/inet_address.cpp.o
[ 7%] Building CXX object lib/co_context/CMakeFiles/co_context.dir/net/socket.cpp.o
In file included from /mnt/hgfs/github/co_context/include/co_context/net/socket.hpp:3,
from /mnt/hgfs/github/co_context/lib/co_context/net/socket.cpp:1:
/mnt/hgfs/github/co_context/include/co_context/lazy_io.hpp:469:65: error: ‘lazy_msg_ring’ in namespace ‘co_context::detail’ does not name a type
469 | [[nodiscard("Did you forget to co_await?")]] inline detail::lazy_msg_ring
| ^~~~~~~~~~~~~
make[2]: *** [lib/co_context/CMakeFiles/co_context.dir/build.make:146: lib/co_context/CMakeFiles/co_context.dir/net/socket.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:215: lib/co_context/CMakeFiles/co_context.dir/all] Error 2
make: *** [Makefile:91: all] Error 2
[root@localhost co_context]#
[root@localhost co_context]# uname -a
Linux localhost.localdomain 5.14.0-70.17.1.el9_0.x86_64 #1 SMP PREEMPT Wed Jul 13 18:23:04 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
[root@localhost co_context]# gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/11/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-redhat-linux
Configured with: ../configure --enable-bootstrap --enable-host-pie --enable-host-bind-now --enable-languages=c,c++,fortran,lto --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.rockylinux.org/ --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-gcc-major-version-only --with-linker-hash-style=gnu --enable-plugin --enable-initfini-array --without-isl --enable-offload-targets=nvptx-none --without-cuda-driver --enable-gnu-indirect-function --enable-cet --with-tune=generic --with-arch_64=x86-64-v2 --with-arch_32=x86-64 --build=x86_64-redhat-linux --with-build-config=bootstrap-lto --enable-link-serialization=1
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.2.1 20220127 (Red Hat 11.2.1-9) (GCC)

Add `IORING_SETUP_DEFER_TASKRUN` flag to `uring_type.hpp` if Kernel >= 6.1

IORING_SETUP_DEFER_TASKRUN

By default, io_uring will process all outstanding work at the end of any system call or thread interrupt. This can delay the application from making other progress. Setting this flag will hint to io_uring that it should defer work until an io_uring_enter(2) call with the IORING_ENTER_GETEVENTS flag set. This allows the application to request work to run just before it wants to process completions. This flag requires the IORING_SETUP_SINGLE_ISSUER flag to be set, and also enforces that the call to io_uring_enter(2) is called from the same thread that submitted requests. Note that if this flag is set then it is the application's responsibility to periodically trigger work (for example via any of the CQE waiting functions) or else completions may not be delivered. Available since 6.1.

#if LIBURINGCXX_IS_KERNEL_REACH(6, 1)
    | IORING_SETUP_DEFER_TASKRUN
#endif

axboe/liburing#536 (comment)

image

如何拓展lazy_io的范围

我今天在学习io_uring时,发现其设计与我在用户态实现的异步IO几乎完全一致。进程间的异步IO是为了实现异步远程函数调用:

  1. 在两个进程之间通过共享内存的方式传输数据
  2. 共享内存被分为两个区域,A进程可读可写但B进程只读(类似io_uring的SQ);A进程可读但B进程可读可写(类似io_uring的CQ)
  3. 这两个队列是单生产者、单消费者模型,提供无锁接口,内部使用内存屏障做同步
  4. 当A进程发起RPC时,需要将参数以及要调用的函数ID写入到共享内存中,B进程在通过某种方法监测到有调用发生时,就会从共享内存中提取数据然后调用函数,之后将函数返回值写入到CQ
  5. A进程与B进程可以通过用户态中断(可以理解为signal机制,但是不需要Linux 内核参与,而是硬件实现的跨核中断)相互进行通知。(这样实现出来的就类似于io_uring的中断驱动机制)
  6. A进程和B进程也可以通过轮询的方法监测共享内存,类似于io_uring的轮询模式
  7. A进程异步,B进程轮询监测内存,类似于io_uring的内核轮询模式

请问我该如何适配co_context,时其支持我的设计方案呢?如果您方便的话,我想参考一下您的论文,如果不方便公开的话,可以发送到我的邮箱:[email protected]

c++: error: '-march=native': ISA string must begin with rv32 or rv64

我认为最少要像

https://github.com/StanfordLegion/legion/blob/f7d3bbe27c994879528c4a7b0e19cd09bea29daf/CMakeLists.txt#L153
这样写才合理吧
不然riscv下编译不认

target_compile_options(co_context PUBLIC -march=native)

的-march=native

环境 真机ubuntu

make@make:~/ubuntu$ uname -m
x86_64

make@make:~/ubuntu$ uname -a

Linux make 5.15.0-91-generic #101-Ubuntu SMP Tue Nov 14 13:30:08 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

make@make:~/ubuntu$ 

docker riscv64

docker run -it -v /usr/bin/qemu-riscv64-static:/usr/bin/qemu-riscv64-static riscv64/ubuntu:23.04 /bin/bash


编译报错

root@53454d58d5b4:/# cd co_context
root@53454d58d5b4:/co_context# ls
CMakeLists.txt  CMakePresets.json  LICENSE  README.md  b  cmake  doc  example  extern  format_count.sh  include  lib  test
root@53454d58d5b4:/co_context# rm -rf b
root@53454d58d5b4:/co_context# ls
CMakeLists.txt  CMakePresets.json  LICENSE  README.md  cmake  doc  example  extern  format_count.sh  include  lib  test
root@53454d58d5b4:/co_context# mkdir b
root@53454d58d5b4:/co_context# cd b
root@53454d58d5b4:/co_context/b# cmake ..
-- The CXX compiler identification is GNU 12.3.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- target_kernel_version = 5.15.0-91-generic
-- LIBURINGCXX_KERNEL_VERSION_MAJOR = 5
-- LIBURINGCXX_KERNEL_VERSION_MINOR = 15
-- Performing Test LIBURINGCXX_HAS_KERNEL_RWF_T
-- Performing Test LIBURINGCXX_HAS_KERNEL_RWF_T - Success
-- Performing Test LIBURINGCXX_HAS_TIME_TYPES
-- Performing Test LIBURINGCXX_HAS_TIME_TYPES - Success
-- Performing Test LIBURINGCXX_HAS_OPENAT2
-- Performing Test LIBURINGCXX_HAS_OPENAT2 - Success
Setting default CMAKE_BUILD_TYPE to Release
CMake Warning at cmake/CompileOption.cmake:43 (message):
  mimalloc disabled
Call Stack (most recent call first):
  CMakeLists.txt:21 (include)


-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE  
-- Configuring done
-- Generating done
-- Build files have been written to: /co_context/b

root@53454d58d5b4:/co_context/b# uname -m
riscv64
root@53454d58d5b4:/co_context/b# uname -a
Linux 53454d58d5b4 5.15.0-91-generic #101-Ubuntu SMP Tue Nov 14 13:30:08 UTC 2023 riscv64 riscv64 riscv64 GNU/Linux


root@53454d58d5b4:/co_context/b# make -j4
[  1%] Building CXX object CMakeFiles/co_context.dir/lib/co_context/co/condition_variable.cpp.o
[  3%] Building CXX object CMakeFiles/co_context.dir/lib/co_context/co/semaphore.cpp.o
[  5%] Building CXX object CMakeFiles/co_context.dir/lib/co_context/co/mutex.cpp.o
[  7%] Building CXX object CMakeFiles/co_context.dir/lib/co_context/co/spin_mutex.cpp.o
c++: error: '-march=': ISA string must begin with rv32 or rv64
c++: error: '-march=native': ISA string must begin with rv32 or rv64
make[2]: *** [CMakeFiles/co_context.dir/build.make:76: CMakeFiles/co_context.dir/lib/co_context/co/condition_variable.cpp.o] Error 1
make[2]: *** Waiting for unfinished jobs....
make[2]: *** [CMakeFiles/co_context.dir/build.make:104: CMakeFiles/co_context.dir/lib/co_context/co/semaphore.cpp.o] Error 1
c++: error: '-march=native': ISA string must begin with rv32 or rv64
c++: error: '-march=native': ISA string must begin with rv32 or rv64
make[2]: *** [CMakeFiles/co_context.dir/build.make:90: CMakeFiles/co_context.dir/lib/co_context/co/mutex.cpp.o] Error 1
make[2]: *** [CMakeFiles/co_context.dir/build.make:118: CMakeFiles/co_context.dir/lib/co_context/co/spin_mutex.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:191: CMakeFiles/co_context.dir/all] Error 2
make: *** [Makefile:136: all] Error 2
root@53454d58d5b4:/co_context/b# 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.