C++ Workflow中有五种基础任务:通讯、计算、文件IO、定时器、计数器,在coke项目中也分别对应着一组基础组件。其中计数器通常用来实现功能更复杂的复合组件,coke中暂时不会有一个通用的计数器基础组件,因此本文要介绍的文件IO应当是基础组件中的最后一个了。

C++ Workflow一样,coke提供了一组类似于preadpwrite的文件IO接口,具体如下

FileAwaiter pread(int fd, void *buf, size_t count, off_t offset);
FileAwaiter pwrite(int fd, const void *buf, size_t count, off_t offset);

相比于其他异步任务,作者在实际工作中很少遇到异步文件IO相关的场景,因此在设计更易用的接口时遇到了瓶颈,若读者有相关的场景或好的设计想法,欢迎到项目ISSUE中讨论。此处先给出上述接口的具体使用方法

#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <fcntl.h>

#include "coke/coke.h"

coke::Task<> write_file(int fd) {
    const char *str = "Hello world!\n";
    std::size_t len = std::strlen(str);
    off_t offset = 0;
    coke::FileResult res;

    for (int i = 0; i < 3; i++) {

        // 将字符序列[str, str + len)写入到文件的[offset, offset + len)位置
        res = co_await coke::pwrite(fd, str, len, offset);

        if (res.state != coke::STATE_SUCCESS) {
            // 文件操作发生错误,其中res.error与Linux errno一致
            std::cout << coke::get_error_string(res.state, res.error) << std::endl;
            co_return;
        }
        else if (res.nbytes != static_cast<long>(len)) {
            // 当写入字符数量与传入的len不一致时,需要再次通过coke::pwrite
            // 将[str + nbytes, str + len)中的内容写入,此处略去了
            std::cout << "Partially Success " << res.nbytes << "/" << len << "\n";
        }
        else {
            // 全部写入成功
            std::cout << "Write success\n";
        }

        // 将偏移量移动到下次写入的位置
        offset += res.nbytes;
    }
}

coke::Task<> read_file(int fd) {
    constexpr std::size_t MAX_READ = 4096;
    char buf[MAX_READ+1];
    coke::FileResult res;

    // 从偏移量0读取至多MAX_READ个字符到buf中
    res = co_await coke::pread(fd, buf, MAX_READ, 0);

    if (res.state == coke::STATE_SUCCESS) {
        // 若读取成功,则res.nbytes表示成功读取的字符数
        buf[res.nbytes] = '\0';
        std::cout << "Read " << res.nbytes << " bytes. File content:\n";
        std::cout << buf;
    }
    else {
        std::cout << coke::get_error_string(res.state, res.error) << std::endl;
    }
}

int main() {
    const char *tmpfile = "test_fileio.tmp";
    int fd = open(tmpfile, O_RDWR|O_CREAT, 0644);

    if (fd < 0) {
        std::cout << "OpenFailed file:" << tmpfile << " errno:" << errno << std::endl;
        return -1;
    }

    coke::sync_wait(write_file(fd));
    coke::sync_wait(read_file(fd));

    close(fd);
    return 0;
}

有了这些基础组件,原则上就可以实现与之相关的所有功能了,但这还远远不够。本项目将会从实际场景出发,思考并设计一些通用的复合组件,让代码写起来更加顺畅。这些会需要更多的灵感和业余时间,所以不会很快就搞出来。

本系列文章同步发布于个人网站知乎专栏,转载请注明来源,以便读者及时获取最新内容及勘误。

2 个评论

    1. 好,门当然不能闭,但车是一定要造的。

      我最感兴趣的部分是在没有编译器开洞的情况下,无栈协程是如何以最小的开销处理局部变量的。

      kedixa

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注