Giter Club home page Giter Club logo

asyncio's Introduction

 _   _      _                                                                                      _
| \ | | ___| |_ ___ __ _ _ __     ___  _ __    _ __  _ __ ___   __ _ _ __ __ _ _ __ ___  _ __ ___ (_)_ __   __ _
|  \| |/ _ \ __/ __/ _` | '_ \   / _ \| '_ \  | '_ \| '__/ _ \ / _` | '__/ _` | '_ ` _ \| '_ ` _ \| | '_ \ / _` |
| |\  |  __/ || (_| (_| | | | | | (_) | | | | | |_) | | | (_) | (_| | | | (_| | | | | | | | | | | | | | | | (_| |
|_| \_|\___|\__\___\__,_|_| |_|  \___/|_| |_| | .__/|_|  \___/ \__, |_|  \__,_|_| |_| |_|_| |_| |_|_|_| |_|\__, |
                                              |_|              |___/                                       |___/
* 1996, Summer, Senior Software Engineer
* Skills: C/C++/Rust, Haskell/Scheme, Bash/Python/Javascript/PHP
* Interests: CS, OO/FP, Design/Coding/Writing

Latest talks

Latest blog posts

>>> More blog posts

Statistics

Stats Lang

asyncio's People

Contributors

aliihsancengiz avatar be3y4uu-k0t avatar netcan avatar russkel avatar xiaozhuai avatar ywy2090 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

asyncio's Issues

Installation error

Get this error while installing on a mac:

In file included from /Users/atulbansal/base/projects/cpp/asyncio/include/asyncio/event_loop.h:9:
/Users/atulbansal/base/projects/cpp/asyncio/include/asyncio/handle.h:9:10: fatal error: 'source_location' file not found
#include <source_location>
^~~~~~~~~~~~~~~~~
1 error generated.

Release test pass in GCC 11.2 :-)

Hi

My test don't show any release bug in GCC 11.2. I use in arch Linux.

Then:
" Debian Linux gcc-11/12, gcc-11 crash at Release mode"

Seems wrong Now !

关于task.h中的await_transform和co_await如何执行

co_await expr会被转化为

auto&& awaitable = **get_awaitable**(promise, static_cast<decltype(value)>(value));
auto&& awaiter = get_awaiter(static_cast<decltype(awaitable)>(awaitable));

而get_awaitable由下面伪代码构成

decltype(auto) **get_awaitable**(P& promise, T&& expr)
{
  if constexpr (has_any_await_transform_member_v<P>)
    return promise.await_transform(static_cast<T&&>(expr));
  else
    return static_cast<T&&>(expr);
}

参考task_test.cpp例子,struct Task不应该是一个awaitable对象,想问一下task.h中的await_transform是如何工作的。
调试代码,先运行await_transform再运行co_await,没弄明白参数是怎么传递的,能否做个简单提示
不胜感激

Compatability with cppcoro

Hello,

Great library, thanks for your work. I have a question regarding using the other coroutine types found in https://github.com/andreasbuhr/cppcoro with asyncio. Is there a lot of work being able to support these? I am a bit naive as to how this works as I have only very briefly looked over the code in both libs.

Cheers,

Russ

Can't compile it with gcc 11

At the beginning, I compile it with gcc 9 and get the following error:

asyncio/include/asyncio/handle.h:9:10: fatal error: source_location: No such file or directory
    9 | #include <source_location>
       |          ^~~~~~~~~~~~~~~~~

Then, I update the gcc to version 11.1 and still get this error.

Could author paste the successful building environment in the README?

Thanks.

单元测试报错

环境:
Ubuntu 18.04.1 LTS
gcc版本:
gcc version 11.1.0 (Ubuntu 11.1.0-1ubuntu1~18.04.1)

单元测试报错信息如下,请帮忙分析下是什么原因,谢谢!
`ubuntu@VM-0-16-ubuntu:~/c++/asyncio/build/test/ut$ ./asyncio_ut
this is a void value
Task C: Compute factorial(4), currently i=2...
Task B: Compute factorial(3), currently i=2...
Task A: Compute factorial(2), currently i=2...
Task C: Compute factorial(4), currently i=3...
Task B: Compute factorial(3), currently i=3...
Task A: factorial(2) = 2
Task C: Compute factorial(4), currently i=4...
Task B: factorial(3) = 6
Task C: factorial(4) = 24
Task B: Compute factorial(3), currently i=2...
Task A: Compute factorial(2), currently i=2...
this is a void value
Task C: Compute factorial(4), currently i=2...
Task B: Compute factorial(3), currently i=3...
Task A: factorial(2) = 2
Task C: Compute factorial(4), currently i=3...
Task B: factorial(3) = 6
Task C: Compute factorial(4), currently i=4...
Task C: factorial(4) = 24
Task B: Compute factorial(3), currently i=2...
Task A: Compute factorial(2), currently i=2...
Task B: Compute factorial(3), currently i=3...
Task A: factorial(2) = 2
Task B: factorial(3) = 6
Task B: Compute factorial(3), currently i=2...
wait_duration finished

asyncio_ut is a Catch v3.0.0-preview.3 host application.
Run with -? for options

Randomness seeded to: 1868188266

-------------------------------------------------------------------------------
Scenario: test timeout
  wait_for with gather
-------------------------------------------------------------------------------
/home/ubuntu/c++/asyncio/test/ut/task_test.cpp:275
...............................................................................

/home/ubuntu/c++/asyncio/test/ut/task_test.cpp:278: FAILED:
  REQUIRE_NOTHROW( co_await wait_for(gather(sleep(10ms), sleep(20ms), sleep(30ms)), 50ms) )
due to unexpected exception with message:
  TimeoutError

===============================================================================
test cases: 11 | 10 passed | 1 failed
assertions: 96 | 95 passed | 1 failed`

不支持 windows?

这样太不兼容 python asynio了?
C++ 里有没不阻塞线程的 sleep函数

heap-use-after-free

环境

Ubuntu 18.04.4 LTS
gcc 11.1.0

测试内容

在echo_server中,在write之前加300ms的sleep时间。
在echo_client中,创建多个连接(测试了17个)同时发送给echo_server,并且使用wait_for进行超时控制,超时是300ms。
客户端和服务端的时间300ms,是特意设置的。

测试结果

偶尔会有成功,偶尔会有超时,偶尔会产生heap-use-after-free的coredump。重复测试多次,是会复现问题的。

请帮忙看看是哪里导致了内存释放了,还在被使用。

测试代码

server

#include <asyncio/runner.h>
#include <asyncio/start_server.h>
#include <asyncio/sleep.h>
#include <asyncio/wait_for.h>
#include <arpa/inet.h>

using namespace std::chrono;
using asyncio::Task;
using asyncio::Stream;
using asyncio::get_in_addr;
using asyncio::get_in_port;

Task<> handle_echo(Stream stream) {
    auto& sockinfo = stream.get_sock_info();
    auto sa = reinterpret_cast<const sockaddr*>(&sockinfo);
    char addr[INET6_ADDRSTRLEN] {};

    auto data = co_await stream.read(100);
    fmt::print("Received: '{}' from '{}:{}'\n", data.data(),
               inet_ntop(sockinfo.ss_family, get_in_addr(sa), addr, sizeof addr),
               get_in_port(sa));

    co_await asyncio::sleep(300ms);
    fmt::print("Send: '{}'\n", data.data());
    co_await stream.write(data);

    fmt::print("Close the connection\n");
    stream.close();
}

Task<void> amain() {
    auto server = co_await asyncio::start_server(
            handle_echo, "127.0.0.1", 8888);

    fmt::print("Serving on 127.0.0.1:8888\n");

    co_await server.serve_forever();
}

int main() {
    asyncio::run(amain());
    return 0;
}

client

#include <iostream>
#include <asyncio/open_connection.h>
#include <asyncio/runner.h>
#include <asyncio/sleep.h>
#include <asyncio/schedule_task.h>
#include <asyncio/wait_for.h>

using namespace asyncio;
using namespace std::chrono;

Task<> tcp_echo_client(std::string_view message) {
        auto stream = co_await asyncio::open_connection("127.0.0.1", 8888);

        fmt::print("Send: '{}'\n", message);
        co_await stream.write(Stream::Buffer(message.begin(), message.end() + 1));

        fmt::print("Send: ok\n");

        auto data = co_await asyncio::wait_for(stream.read(100), 300ms);

        fmt::print("Received: '{}'\n", data.data());

        fmt::print("Close the connection\n");
        stream.close();
}

int main(int argc, char** argv) {

    std::vector<ScheduledTask<Task<>>> tasks;
    for (size_t i = 0; i < 16; ++i)
    {
        tasks.emplace_back(tcp_echo_client("hello world! 1"));
    }
    
    try
    {
        asyncio::run(tcp_echo_client("hello world!"));
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what() << '\n';
    }
    printf("end ....\n");

    return 0;
}

coredump的输出

./echo_client 
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world! 1'
Send: 'hello world!'
Send: ok
Send: ok
Send: ok
Send: ok
Send: ok
Send: ok
Send: ok
Send: ok
Send: ok
Send: ok
Send: ok
Send: ok
Send: ok
Send: ok
Send: ok
Send: ok
Send: ok
=================================================================
==26685==ERROR: AddressSanitizer: heap-use-after-free on address 0x612000002a60 at pc 0x5580ce2d05a6 bp 0x7ffc3359c220 sp 0x7ffc3359c210
WRITE of size 1 at 0x612000002a60 thread T0
    #0 0x5580ce2d05a5 in asyncio::Handle::set_state(asyncio::Handle::State) /home/ubuntu/c++/asyncio/include/asyncio/handle.h:25
    #1 0x5580ce2f1e3d in asyncio::EventLoop::run_once() /home/ubuntu/c++/asyncio/src/event_loop.cpp:62
    #2 0x5580ce2f11f7 in asyncio::EventLoop::run_until_complete() /home/ubuntu/c++/asyncio/src/event_loop.cpp:17
    #3 0x5580ce2d4fbe in decltype(auto) asyncio::run<asyncio::Task<void> >(asyncio::Task<void>&&) (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xf3fbe)
    #4 0x5580ce2cda06 in main /home/ubuntu/c++/asyncio/test/st/echo_client.cpp:40
    #5 0x7fe6a576abf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6)
    #6 0x5580ce1f1d49 in _start (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0x10d49)

0x612000002a60 is located 32 bytes inside of 312-byte region [0x612000002a40,0x612000002b78)
freed by thread T0 here:
    #0 0x5580ce282f47 in operator delete(void*) (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xa1f47)
    #1 0x5580ce2c68a1 in asyncio::Stream::read(long) [clone .actor] /home/ubuntu/c++/asyncio/include/asyncio/stream.h:46
    #2 0x5580ce2c6b29 in asyncio::Stream::read(long) [clone .destroy] /home/ubuntu/c++/asyncio/include/asyncio/stream.h:34
    #3 0x5580ce2e0ee3 in std::__n4861::coroutine_handle<asyncio::Task<std::vector<char, std::allocator<char> > >::promise_type>::destroy() const (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xffee3)
    #4 0x5580ce2dbd41 in asyncio::Task<std::vector<char, std::allocator<char> > >::destroy() (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xfad41)
    #5 0x5580ce2d5f81 in asyncio::Task<std::vector<char, std::allocator<char> > >::~Task() (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xf4f81)
    #6 0x5580ce2dee4f in asyncio::detail::WaitForAwaiterRegistry<asyncio::Task<std::vector<char, std::allocator<char> > >, std::chrono::duration<long, std::ratio<1l, 1000l> > >::~WaitForAwaiterRegistry() (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xfde4f)
    #7 0x5580ce2ce5c8 in asyncio::Task<decltype ((((declval<asyncio::detail::GetAwaiter<asyncio::Task<std::vector<char, std::allocator<char> > > >::type>)()).await_resume)())> asyncio::detail::wait_for<asyncio::Task<std::vector<char, std::allocator<char> > >, long, std::ratio<1l, 1000l> >(asyncio::NoWaitAtInitialSuspend, asyncio::Task<std::vector<char, std::allocator<char> > >&&, std::chrono::duration<long, std::ratio<1l, 1000l> >) [clone .actor] /home/ubuntu/c++/asyncio/include/asyncio/wait_for.h:101
    #8 0x5580ce2f0d93 in std::__n4861::coroutine_handle<asyncio::Task<std::vector<char, std::allocator<char> > >::promise_type>::resume() const (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0x10fd93)
    #9 0x5580ce2ef90b in asyncio::Task<std::vector<char, std::allocator<char> > >::promise_type::run() (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0x10e90b)
    #10 0x5580ce2f1eb5 in asyncio::EventLoop::run_once() /home/ubuntu/c++/asyncio/src/event_loop.cpp:63
    #11 0x5580ce2f11f7 in asyncio::EventLoop::run_until_complete() /home/ubuntu/c++/asyncio/src/event_loop.cpp:17
    #12 0x5580ce2d4fbe in decltype(auto) asyncio::run<asyncio::Task<void> >(asyncio::Task<void>&&) (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xf3fbe)
    #13 0x5580ce2cda06 in main /home/ubuntu/c++/asyncio/test/st/echo_client.cpp:40
    #14 0x7fe6a576abf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6)

previously allocated by thread T0 here:
    #0 0x5580ce282427 in operator new(unsigned long) (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xa1427)
    #1 0x5580ce2d3590 in asyncio::Stream::read(long) (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xf2590)
    #2 0x5580ce2ccb2e in tcp_echo_client(std::basic_string_view<char, std::char_traits<char> >) [clone .actor] /home/ubuntu/c++/asyncio/test/st/echo_client.cpp:28
    #3 0x5580ce2f0df3 in std::__n4861::coroutine_handle<asyncio::Task<void>::promise_type>::resume() const (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0x10fdf3)
    #4 0x5580ce2f0063 in asyncio::Task<void>::promise_type::run() (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0x10f063)
    #5 0x5580ce2f1eb5 in asyncio::EventLoop::run_once() /home/ubuntu/c++/asyncio/src/event_loop.cpp:63
    #6 0x5580ce2f11f7 in asyncio::EventLoop::run_until_complete() /home/ubuntu/c++/asyncio/src/event_loop.cpp:17
    #7 0x5580ce2d4fbe in decltype(auto) asyncio::run<asyncio::Task<void> >(asyncio::Task<void>&&) (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xf3fbe)
    #8 0x5580ce2cda06 in main /home/ubuntu/c++/asyncio/test/st/echo_client.cpp:40
    #9 0x7fe6a576abf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6)

SUMMARY: AddressSanitizer: heap-use-after-free /home/ubuntu/c++/asyncio/include/asyncio/handle.h:25 in asyncio::Handle::set_state(asyncio::Handle::State)
Shadow bytes around the buggy address:
  0x0c247fff84f0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c247fff8500: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa
  0x0c247fff8510: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c247fff8520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c247fff8530: 00 00 00 00 00 00 00 00 00 00 00 00 00 fa fa fa
=>0x0c247fff8540: fa fa fa fa fa fa fa fa fd fd fd fd[fd]fd fd fd
  0x0c247fff8550: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c247fff8560: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa
  0x0c247fff8570: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c247fff8580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c247fff8590: 00 00 00 00 00 00 00 00 00 00 00 00 00 fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==26685==ABORTING

wait_for控制超时导致coredump

在测试echo_client时,使用wait_for为read增加超时机制。在很快收到echo_server响应后,打印data.data()数据时,程序coredump了。请帮忙看看是什么问题,谢谢!

运行环境:
Ubuntu 18.04.4 LTS
gcc version 11.1.0

测试代码如下:

#include <iostream>
#include <asyncio/open_connection.h>
#include <asyncio/runner.h>
#include <asyncio/sleep.h>
#include <asyncio/schedule_task.h>
#include <asyncio/wait_for.h>
using asyncio::Stream;
using asyncio::Task;

using namespace std::chrono;

Task<> tcp_echo_client(std::string_view message) {
        auto stream = co_await asyncio::open_connection("127.0.0.1", 8888);

        fmt::print("Send: '{}'\n", message);
        co_await stream.write(Stream::Buffer(message.begin(), message.end()));

        fmt::print("Send: ok\n");

        auto data = co_await asyncio::wait_for(stream.read(100), 300ms);

        fmt::print("wait_for: ok data len {}\n", data.size());
        fmt::print("Received: '{}'\n", data.data());

        fmt::print("Close the connection\n");
        stream.close();
}

int main(int argc, char** argv) {
    asyncio::run(tcp_echo_client("hello world!"));
    return 0;
}

输出和报错信息如下:

Send: 'hello world!'
Send: ok
wait_for: ok data len 12
=================================================================
==16717==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000019c at pc 0x55555557d747 bp 0x7fffffffcfa0 sp 0x7fffffffc748
READ of size 13 at 0x60200000019c thread T0
    #0 0x55555557d746 in __interceptor_strlen.part.0 (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0x29746)
    #1 0x5555556906bd in fmt::v8::basic_string_view<char>::basic_string_view(char const*) /home/ubuntu/c++/asyncio/third_party/fmt/include/fmt/core.h:440
    #2 0x5555556906bd in fmt::v8::appender fmt::v8::detail::write<char, fmt::v8::appender>(fmt::v8::appender, char const*) /home/ubuntu/c++/asyncio/third_party/fmt/include/fmt/format.h:2122
    #3 0x55555568a2f5 in fmt::v8::appender fmt::v8::detail::default_arg_formatter<char>::operator()<char const*>(char const*) /home/ubuntu/c++/asyncio/third_party/fmt/include/fmt/format.h:2162
    #4 0x55555569254b in decltype ({parm#1}(0)) fmt::v8::visit_format_arg<fmt::v8::detail::default_arg_formatter<char>, fmt::v8::basic_format_context<fmt::v8::appender, char> >(fmt::v8::detail::default_arg_formatter<char>&&, fmt::v8::basic_format_arg<fmt::v8::basic_format_context<fmt::v8::appender, char> > const&) /home/ubuntu/c++/asyncio/third_party/fmt/include/fmt/core.h:1560
    #5 0x55555569254b in fmt::v8::detail::vformat_to<char>(fmt::v8::detail::buffer<char>&, fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<std::conditional<std::is_same<fmt::v8::type_identity<char>::type, char>::value, fmt::v8::appender, std::back_insert_iterator<fmt::v8::detail::buffer<fmt::v8::type_identity<char>::type> > >::type, fmt::v8::type_identity<char>::type> >, fmt::v8::detail::locale_ref)::format_handler::on_replacement_field(int, char const*) /home/ubuntu/c++/asyncio/third_party/fmt/include/fmt/format.h:2919
    #6 0x55555569254b in char const* fmt::v8::detail::parse_replacement_field<char, fmt::v8::detail::vformat_to<char>(fmt::v8::detail::buffer<char>&, fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<std::conditional<std::is_same<fmt::v8::type_identity<char>::type, char>::value, fmt::v8::appender, std::back_insert_iterator<fmt::v8::detail::buffer<fmt::v8::type_identity<char>::type> > >::type, fmt::v8::type_identity<char>::type> >, fmt::v8::detail::locale_ref)::format_handler&>(char const*, char const*, fmt::v8::detail::vformat_to<char>(fmt::v8::detail::buffer<char>&, fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<std::conditional<std::is_same<fmt::v8::type_identity<char>::type, char>::value, fmt::v8::appender, std::back_insert_iterator<fmt::v8::detail::buffer<fmt::v8::type_identity<char>::type> > >::type, fmt::v8::type_identity<char>::type> >, fmt::v8::detail::locale_ref)::format_handler&) /home/ubuntu/c++/asyncio/third_party/fmt/include/fmt/core.h:2536
    #7 0x55555567b74c in void fmt::v8::detail::parse_format_string<false, char, fmt::v8::detail::vformat_to<char>(fmt::v8::detail::buffer<char>&, fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<std::conditional<std::is_same<fmt::v8::type_identity<char>::type, char>::value, fmt::v8::appender, std::back_insert_iterator<fmt::v8::detail::buffer<fmt::v8::type_identity<char>::type> > >::type, fmt::v8::type_identity<char>::type> >, fmt::v8::detail::locale_ref)::format_handler>(fmt::v8::basic_string_view<char>, fmt::v8::detail::vformat_to<char>(fmt::v8::detail::buffer<char>&, fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<std::conditional<std::is_same<fmt::v8::type_identity<char>::type, char>::value, fmt::v8::appender, std::back_insert_iterator<fmt::v8::detail::buffer<fmt::v8::type_identity<char>::type> > >::type, fmt::v8::type_identity<char>::type> >, fmt::v8::detail::locale_ref)::format_handler&&) /home/ubuntu/c++/asyncio/third_party/fmt/include/fmt/core.h:2571
    #8 0x55555567b74c in void fmt::v8::detail::vformat_to<char>(fmt::v8::detail::buffer<char>&, fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<std::conditional<std::is_same<fmt::v8::type_identity<char>::type, char>::value, fmt::v8::appender, std::back_insert_iterator<fmt::v8::detail::buffer<fmt::v8::type_identity<char>::type> > >::type, fmt::v8::type_identity<char>::type> >, fmt::v8::detail::locale_ref) /home/ubuntu/c++/asyncio/third_party/fmt/include/fmt/format.h:2945
    #9 0x55555566dc10 in fmt::v8::vprint(_IO_FILE*, fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<fmt::v8::appender, char> >) /home/ubuntu/c++/asyncio/third_party/fmt/include/fmt/format-inl.h:2611
    #10 0x55555566de4a in fmt::v8::vprint(fmt::v8::basic_string_view<char>, fmt::v8::basic_format_args<fmt::v8::basic_format_context<fmt::v8::appender, char> >) /home/ubuntu/c++/asyncio/third_party/fmt/include/fmt/format-inl.h:2627
    #11 0x555555640102 in void fmt::v8::print<char*>(fmt::v8::basic_format_string<char, fmt::v8::type_identity<char*>::type>, char*&&) /home/ubuntu/c++/asyncio/third_party/fmt/include/fmt/core.h:3150
    #12 0x555555640102 in tcp_echo_client(std::basic_string_view<char, std::char_traits<char> >) [clone .actor] /home/ubuntu/c++/asyncio/test/st/echo_client.cpp:26
    #13 0x555555662401 in std::__n4861::coroutine_handle<asyncio::Task<void>::promise_type>::resume() const (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0x10e401)
    #14 0x555555661671 in asyncio::Task<void>::promise_type::run() (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0x10d671)
    #15 0x5555556634c3 in asyncio::EventLoop::run_once() /home/ubuntu/c++/asyncio/src/event_loop.cpp:63
    #16 0x555555662805 in asyncio::EventLoop::run_until_complete() /home/ubuntu/c++/asyncio/src/event_loop.cpp:17
    #17 0x555555647d14 in decltype(auto) asyncio::run<asyncio::Task<void> >(asyncio::Task<void>&&) (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xf3d14)
    #18 0x55555564099b in main /home/ubuntu/c++/asyncio/test/st/echo_client.cpp:33
    #19 0x7ffff6a15bf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6)
    #20 0x555555564bb9 in _start (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0x10bb9)

0x60200000019c is located 0 bytes to the right of 12-byte region [0x602000000190,0x60200000019c)
allocated by thread T0 here:
    #0 0x5555555f5297 in operator new(unsigned long) (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xa1297)
    #1 0x555555659af1 in __gnu_cxx::new_allocator<char>::allocate(unsigned long, void const*) /usr/include/c++/11/ext/new_allocator.h:121
    #2 0x5555556552dd in std::allocator<char>::allocate(unsigned long) /usr/include/c++/11/bits/allocator.h:173
    #3 0x5555556552dd in std::allocator_traits<std::allocator<char> >::allocate(std::allocator<char>&, unsigned long) /usr/include/c++/11/bits/alloc_traits.h:460
    #4 0x555555653ae9 in std::_Vector_base<char, std::allocator<char> >::_M_allocate(unsigned long) (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xffae9)
    #5 0x55555565374c in std::_Vector_base<char, std::allocator<char> >::_M_create_storage(unsigned long) (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xff74c)
    #6 0x55555564ee7c in std::_Vector_base<char, std::allocator<char> >::_Vector_base(unsigned long, std::allocator<char> const&) (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xfae7c)
    #7 0x555555657ccd in std::vector<char, std::allocator<char> >::vector(std::vector<char, std::allocator<char> > const&) (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0x103ccd)
    #8 0x555555654b6e in asyncio::Result<std::vector<char, std::allocator<char> > >::result() & (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0x100b6e)
    #9 0x555555651d35 in asyncio::detail::WaitForAwaiter<std::vector<char, std::allocator<char> >, std::chrono::duration<long, std::ratio<1l, 1000l> > >::await_resume() (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xfdd35)
    #10 0x555555641124 in asyncio::Task<decltype ((((declval<asyncio::detail::GetAwaiter<asyncio::Task<std::vector<char, std::allocator<char> > > >::type>)()).await_resume)())> asyncio::detail::wait_for<asyncio::Task<std::vector<char, std::allocator<char> > >, long, std::ratio<1l, 1000l> >(asyncio::NoWaitAtInitialSuspend, asyncio::Task<std::vector<char, std::allocator<char> > >&&, std::chrono::duration<long, std::ratio<1l, 1000l> >) [clone .actor] /home/ubuntu/c++/asyncio/include/asyncio/wait_for.h:100
    #11 0x5555556623a1 in std::__n4861::coroutine_handle<asyncio::Task<std::vector<char, std::allocator<char> > >::promise_type>::resume() const (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0x10e3a1)
    #12 0x555555660f19 in asyncio::Task<std::vector<char, std::allocator<char> > >::promise_type::run() (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0x10cf19)
    #13 0x5555556634c3 in asyncio::EventLoop::run_once() /home/ubuntu/c++/asyncio/src/event_loop.cpp:63
    #14 0x555555662805 in asyncio::EventLoop::run_until_complete() /home/ubuntu/c++/asyncio/src/event_loop.cpp:17
    #15 0x555555647d14 in decltype(auto) asyncio::run<asyncio::Task<void> >(asyncio::Task<void>&&) (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0xf3d14)
    #16 0x55555564099b in main /home/ubuntu/c++/asyncio/test/st/echo_client.cpp:33
    #17 0x7ffff6a15bf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/ubuntu/c++/asyncio/build/test/st/echo_client+0x29746) in __interceptor_strlen.part.0
Shadow bytes around the buggy address:
  0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff8000: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
  0x0c047fff8010: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
  0x0c047fff8020: fa fa fd fd fa fa fd fd fa fa fd fd fa fa fd fd
=>0x0c047fff8030: fa fa 00[04]fa fa fd fd fa fa fa fa fa fa fa fa
  0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==16717==ABORTING
[1] + Done                       "/usr/bin/gdb" --interpreter=mi --tty=${DbgTerm} 0<"/tmp/Microsoft-MIEngine-In-qj4itxmc.b1y" 1>"/tmp/Microsoft-MIEngine-Out-gs1cjxb4.rcj"

vcpkg

it would be better if this lib can be used through vcpkg😋

Python: run_soon_threadsafe

Nice Python-like-API-approach. I started something like this some time ago and my event loop class looked pretty
much the same.

In Python, you can have multiple event loops:
See https://docs.python.org/3/library/asyncio-eventloop.html call_soon_threadsafe, set_event_loop etc.

I think it's about making this a thread_local:

EventLoop& get_event_loop() {
    static EventLoop loop;
    return loop;
}

The continuation may be a bit different as well. I'd love a combination of this API with the
multithreading-capabilities of https://github.com/David-Haim/concurrencpp.

OSX compilation troubles

Hello,

nikita@MacBook-Air-Nikita ~ % sw_vers
ProductName:            macOS
ProductVersion:         13.1
BuildVersion:           22C65

while I was building asyncio I have faced with many issues:

In file included from /Users/nikita/Desktop/test/asyncio/src/event_loop.cpp:6:
/Users/nikita/Desktop/test/asyncio/include/asyncio/event_loop.h: In member function 'constexpr void asyncio::EventLoop::WaitEventAwaiter::await_suspend(std::__n4861::coroutine_handle<_Promise>)':
/Users/nikita/Desktop/test/asyncio/include/asyncio/event_loop.h:60:27: error: 'using Selector = struct asyncio::KQueueSelector' {aka 'struct asyncio::KQueueSelector'} has no member named 'register_event'
   60 |                 selector_.register_event(event_);
      |                           ^~~~~~~~~~~~~~
/Users/nikita/Desktop/test/asyncio/include/asyncio/event_loop.h: In member function 'void asyncio::EventLoop::WaitEventAwaiter::destroy()':
/Users/nikita/Desktop/test/asyncio/include/asyncio/event_loop.h:70:27: error: 'using Selector = struct asyncio::KQueueSelector' {aka 'struct asyncio::KQueueSelector'} has no member named 'remove_event'
   70 |                 selector_.remove_event(event_);
      |                           ^~~~~~~~~~~~
/Users/nikita/Desktop/test/asyncio/include/asyncio/event_loop.h: In member function 'bool asyncio::EventLoop::is_stop()':
/Users/nikita/Desktop/test/asyncio/include/asyncio/event_loop.h:93:65: error: 'using Selector = struct asyncio::KQueueSelector' {aka 'struct asyncio::KQueueSelector'} has no member named 'is_stop'
   93 |         return schedule_.empty() && ready_.empty() && selector_.is_stop();
      |                

In asyncio/include/asyncio/selector/kqueue_selector.h I finished KQueueSelector:

#ifndef ASYNCIO_KQUEUE_SELECTOR_H
#define ASYNCIO_KQUEUE_SELECTOR_H
#include <asyncio/asyncio_ns.h>
#include <asyncio/selector/event.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>
#include <cerrno>
#include <cstdio>
#include <vector>
#include <chrono>
#include <ranges>
#include <memory>

using namespace std::chrono;

ASYNCIO_NS_BEGIN
struct KQueueSelector {
    KQueueSelector(): kq_(kqueue()) {
        if (kq_ < 0) {
            perror("kqueue create");
            throw;
        }
    }

    std::vector<Event> select(int timeout /* ms */) {
        errno = 0;
        auto nsec = duration_cast<nanoseconds>(milliseconds{ timeout });
        const auto sec = duration_cast<seconds>(nsec);
        const timespec ts {
            .tv_sec = sec.count(),
            .tv_nsec = (nsec - sec).count(),
        };
        std::vector<struct kevent> events(register_event_count_);
        int ndfs = kevent(kq_, nullptr, 0, events.data(), register_event_count_, timeout < 0 ? nullptr : &ts);
        if (ndfs < 0)
            throw std::system_error { errno, std::system_category(), "kevent::select" };

        if (!ndfs)
            return {}; // return empty std::vector<Event>

        std::vector<Event> result;
        if (ndfs >= 100) [[unlikely]] { // FIXME: Is there a needed?
            result.reserve(ndfs);
        }
        for (auto event : events | std::views::take(ndfs)) {
            auto handle_info = reinterpret_cast<HandleInfo*>(event.udata);
            if (handle_info->handle != nullptr && handle_info->handle != (Handle*)&handle_info->handle) {
                result.emplace_back(Event {
                    .handle_info = *handle_info
                });
            } else {
                // mark event ready, but has no response callback
                handle_info->handle = (Handle*)&handle_info->handle;
            }
        }
        return result;
    }

    ~KQueueSelector() {
        if (kq_ > 0) {
            close(kq_);
        }
    }

    bool is_stop() const {
        return register_event_count_ == 1;
    }

    void register_event(const Event& event) {
        struct kevent ev {
            .ident = static_cast<uintptr_t>(event.fd),
            .filter = static_cast<int16_t>(event.flags),
            .flags = EV_ADD | EV_ENABLE,
            .udata = const_cast<HandleInfo*>(&event.handle_info)
        };
        if (!kevent(kq_, &ev, 1, nullptr, 0, nullptr)) {
            ++register_event_count_;
        }
    }

    void remove_event(const Event& event) {
        struct kevent ev {
            .ident = static_cast<uintptr_t>(event.fd),
            .filter = static_cast<int16_t>(event.flags),
            .flags = EV_DELETE | EV_DISABLE,
            .udata = nullptr
        };
        if (!kevent(kq_, &ev, 1, nullptr, 0, nullptr)) {
            --register_event_count_;
        }
    }

private:
    int kq_;
    int register_event_count_ { 1 }; // FIXME: Is there a needed? With zero (0) faster than one (1).
    /* "When Fa nevents is zero, kevent ();
will return immediately even if there is a Fa timeout specified unlike [select](https://www.opennet.ru/cgi-bin/opennet/man.cgi?topic=select&category=2)(2)."
    https://www.opennet.ru/man.shtml?topic=kevent
    */
};

ASYNCIO_NS_END
#endif //ASYNCIO_KQUEUE_SELECTOR_H

I replaced this std::vector<struct kevent> events_; on int register_event_count_ { 1 }; because I don't know why this need

Then I have faced with many issues again:

In file included from /Users/nikita/Desktop/test/asyncio/include/asyncio/open_connection.h:8,
                 from /Users/nikita/Desktop/test/asyncio/test/st/echo_client.cpp:4:
/Users/nikita/Desktop/test/asyncio/include/asyncio/stream.h:98:48: error: 'EPOLLIN' was not declared in this scope; did you mean 'POLL_IN'?
   98 |     Event read_ev_ { .fd = read_fd_, .events = EPOLLIN };
      |                                                ^~~~~~~
      |                                                POLL_IN
/Users/nikita/Desktop/test/asyncio/include/asyncio/stream.h:99:50: error: 'EPOLLOUT' was not declared in this scope; did you mean 'POLL_OUT'?
   99 |     Event write_ev_ { .fd = write_fd_, .events = EPOLLOUT };
      |                                                  ^~~~~~~~
      |                                                  POLL_OUT
/Users/nikita/Desktop/test/asyncio/include/asyncio/open_connection.h: In function 'asyncio::Task<bool> asyncio::detail::connect(int, const sockaddr*, socklen_t)':
/Users/nikita/Desktop/test/asyncio/include/asyncio/open_connection.h:26:36: error: 'EPOLLOUT' was not declared in this scope; did you mean 'POLL_OUT'?
   26 |     Event ev { .fd = fd, .events = EPOLLOUT };
      |                                    ^~~~~~~~
      |                                    POLL_OUT
In file included from /Users/nikita/Desktop/test/asyncio/include/asyncio/start_server.h:9,
                 from /Users/nikita/Desktop/test/asyncio/test/ut/task_test.cpp:14:
/Users/nikita/Desktop/test/asyncio/include/asyncio/stream.h:98:48: error: 'EPOLLIN' was not declared in this scope; did you mean 'POLL_IN'?
   98 |     Event read_ev_ { .fd = read_fd_, .events = EPOLLIN };
      |                                                ^~~~~~~
      |                                                POLL_IN
/Users/nikita/Desktop/test/asyncio/include/asyncio/stream.h:99:50: error: 'EPOLLOUT' was not declared in this scope; did you mean 'POLL_OUT'?
   99 |     Event write_ev_ { .fd = write_fd_, .events = EPOLLOUT };
      |                                                  ^~~~~~~~
      |                                                  POLL_OUT
In file included from /Users/nikita/Desktop/test/asyncio/test/st/echo_server.cpp:1:
/Users/nikita/Desktop/test/asyncio/include/asyncio/stream.h:98:48: error: 'EPOLLIN' was not declared in this scope; did you mean 'POLL_IN'?
   98 |     Event read_ev_ { .fd = read_fd_, .events = EPOLLIN };
      |                                                ^~~~~~~
      |                                                POLL_IN
/Users/nikita/Desktop/test/asyncio/include/asyncio/stream.h:99:50: error: 'EPOLLOUT' was not declared in this scope; did you mean 'POLL_OUT'?
   99 |     Event write_ev_ { .fd = write_fd_, .events = EPOLLOUT };
      |                                                  ^~~~~~~~
      |                                                  POLL_OUT
/Users/nikita/Desktop/test/asyncio/include/asyncio/start_server.h: In member function 'asyncio::Task<> asyncio::Server<CONNECT_CB>::serve_forever()':
/Users/nikita/Desktop/test/asyncio/include/asyncio/start_server.h:33:41: error: 'EPOLLIN' was not declared in this scope; did you mean 'POLL_IN'?
   33 |         Event ev { .fd = fd_, .events = EPOLLIN };
      |                                         ^~~~~~~
      |                                         POLL_IN
In file included from /Users/nikita/Desktop/test/asyncio/test/st/echo_server.cpp:4:
/Users/nikita/Desktop/test/asyncio/include/asyncio/start_server.h: In member function 'asyncio::Task<> asyncio::Server<CONNECT_CB>::serve_forever()':
/Users/nikita/Desktop/test/asyncio/include/asyncio/start_server.h:33:41: error: 'EPOLLIN' was not declared in this scope; did you mean 'POLL_IN'?
   33 |         Event ev { .fd = fd_, .events = EPOLLIN };
      |                                         ^~~~~~~
      |                                         POLL_IN
In file included from /Users/nikita/Desktop/test/asyncio/test/ut/task_test.cpp:15:
/Users/nikita/Desktop/test/asyncio/include/asyncio/open_connection.h: In function 'asyncio::Task<bool> asyncio::detail::connect(int, const sockaddr*, socklen_t)':
/Users/nikita/Desktop/test/asyncio/include/asyncio/open_connection.h:26:36: error: 'EPOLLOUT' was not declared in this scope; did you mean 'POLL_OUT'?
   26 |     Event ev { .fd = fd, .events = EPOLLOUT };
      |                                    ^~~~~~~~
      |                                    POLL_OUT

For MacOS we need to use EVFILT_READ/EVFILT_WRITE instead of EPOLLIN/EPOLLOUT.
In asyncio/include/asyncio/selector/event.h I added enum class Flags: int16_t and renamed events to flags:

#ifndef ASYNCIO_EVENT_H
#define ASYNCIO_EVENT_H

#include <asyncio/asyncio_ns.h>
#include <asyncio/handle.h>
#include <cstdint>

#if defined(__APPLE__)
    #include <sys/event.h>
#elif defined(__linux__)
    #include <sys/epoll.h>
#else
    #error "Support only Linux & MacOS!"
#endif

ASYNCIO_NS_BEGIN
struct Event {
    enum class Flags: int16_t {
    #if defined(__APPLE__)
        EVENT_READ = EVFILT_READ,
        EVENT_WRITE = EVFILT_WRITE
    #elif defined(__linux__)
        EVENT_READ = EPOLLIN,
        EVENT_WRITE = EPOLLOUT
    #else
        #error "Support only Linux & MacOS!"
    #endif
    };

    int fd;
    Flags flags;
    HandleInfo handle_info;
};
ASYNCIO_NS_END

#endif //ASYNCIO_EVENT_H

Then replace events to flags in:

  • epoll_selector.h::45 -- epoll_event ev{ .events = event.flags, .data {.ptr = const_cast<HandleInfo*>(&event.handle_info) } };
  • epoll_selector.h::52 -- epoll_event ev{ .events = event.flags };
  • open_connection.h::26 -- Event ev { .fd = fd, .flags = Event::Flags::EVENT_WRITE };
  • start_server.h::33 -- Event ev { .fd = fd_, .flags = Event::Flags::EVENT_READ };
  • stream.h::98-99 -- Event read_ev_ { .fd = read_fd_, .flags = Event::Flags::EVENT_READ }; Event write_ev_ { .fd = write_fd_, .flags = Event::Flags::EVENT_WRITE };

And the last issues:

In file included from /Users/nikita/Desktop/test/asyncio/test/st/echo_client.cpp:4:
/Users/nikita/Desktop/test/asyncio/include/asyncio/open_connection.h: In function 'asyncio::Task<asyncio::Stream> asyncio::open_connection(std::string_view, uint16_t)':
/Users/nikita/Desktop/test/asyncio/include/asyncio/open_connection.h:54:62: error: 'SOCK_NONBLOCK' was not declared in this scope; did you mean 'O_NONBLOCK'?
   54 |         if ( (sockfd = socket(p->ai_family, p->ai_socktype | SOCK_NONBLOCK, p->ai_protocol)) == -1) {
      |                                                              ^~~~~~~~~~~~~
      |                                                              O_NONBLOCK
In file included from /Users/nikita/Desktop/test/asyncio/test/st/echo_server.cpp:4:
/Users/nikita/Desktop/test/asyncio/include/asyncio/start_server.h: In function 'asyncio::Task<asyncio::Server<CONNECT_CB> > asyncio::start_server(CONNECT_CB, std::string_view, uint16_t)':
/Users/nikita/Desktop/test/asyncio/include/asyncio/start_server.h:87:64: error: 'SOCK_NONBLOCK' was not declared in this scope; did you mean 'SIG_UNBLOCK'?
   87 |         if ( (serverfd = socket(p->ai_family, p->ai_socktype | SOCK_NONBLOCK, p->ai_protocol)) == -1) {
      |                                                                ^~~~~~~~~~~~~
      |                                                                SIG_UNBLOCK
In file included from /Users/nikita/Desktop/test/asyncio/test/ut/task_test.cpp:14:
/Users/nikita/Desktop/test/asyncio/include/asyncio/start_server.h: In function 'asyncio::Task<asyncio::Server<CONNECT_CB> > asyncio::start_server(CONNECT_CB, std::string_view, uint16_t)':
/Users/nikita/Desktop/test/asyncio/include/asyncio/start_server.h:87:64: error: 'SOCK_NONBLOCK' was not declared in this scope; did you mean 'O_NONBLOCK'?
   87 |         if ( (serverfd = socket(p->ai_family, p->ai_socktype | SOCK_NONBLOCK, p->ai_protocol)) == -1) {
      |                                                                ^~~~~~~~~~~~~
      |                                                                O_NONBLOCK
In file included from /Users/nikita/Desktop/test/asyncio/test/ut/task_test.cpp:15:
/Users/nikita/Desktop/test/asyncio/include/asyncio/open_connection.h: In function 'asyncio::Task<asyncio::Stream> asyncio::open_connection(std::string_view, uint16_t)':
/Users/nikita/Desktop/test/asyncio/include/asyncio/open_connection.h:54:62: error: 'SOCK_NONBLOCK' was not declared in this scope; did you mean 'O_NONBLOCK'?
   54 |         if ( (sockfd = socket(p->ai_family, p->ai_socktype | SOCK_NONBLOCK, p->ai_protocol)) == -1) {
      |                                                              ^~~~~~~~~~~~~
      |                                                              O_NONBLOCK

MacOS not supported SOCK_NONBLOCK, then I added into asyncio/include/asyncio/stream.h:

...

#if defined(__APPLE__) || defined(__CYGWIN__) || defined(__NetBSD__)
    #ifndef SOCK_NONBLOCK /* __APPLE__: Protocol not supported */
        #define SOCK_NONBLOCK 0
    #endif
    #include <sys/ioctl.h>
    #include <fcntl.h>
#endif

ASYNCIO_NS_BEGIN
namespace socket {
    // Redesign python method `socket.setblocking(bool)`:
    // https://github.com/python/cpython/blob/928752ce4c23f47d3175dd47ecacf08d86a99c9d/Modules/socketmodule.c#L683
    // https://stackoverflow.com/a/1549344/14070318
    bool set_blocking(int fd, bool blocking) {
        if (fd < 0)
            return false;
    #if SOCK_NONBLOCK != 0
        return true;
    #elif defined(_WIN32)
        unsigned long block = !blocking;
        return !ioctlsocket(fd, FIONBIO, &block);
    #elif __has_include(<sys/ioctl.h>) && defined(FIONBIO)
        unsigned int block = !blocking;
        return !ioctl(fd, FIONBIO, &block);
    #else
        int delay_flag, new_delay_flag;
        delay_flag = fcntl(fd, F_GETFL, 0);
        if (delay_flag == -1)
            return false;
        new_delay_flag = blocking ? (delay_flag & ~O_NONBLOCK) : (delay_flag | O_NONBLOCK);
        if (new_delay_flag != delay_flag)
            return !fcntl(fd, F_SETFL, new_delay_flag);
        else
            return false;
    #endif
    }
}

...
if ((serverfd = ::socket(p->ai_family, p->ai_socktype | SOCK_NONBLOCK, p->ai_protocol)) == -1) {
    continue;
}
socket::set_blocking(serverfd, false);
if ((sockfd = ::socket(p->ai_family, p->ai_socktype | SOCK_NONBLOCK, p->ai_protocol)) == -1) {
    continue;
}
socket::set_blocking(sockfd, false);
nikita@MacBook-Air-Nikita build % CXX=g++-12 CXXFLAGS=-I/opt/homebrew/Cellar/gcc/12.2.0/include/\ -I/opt/homebrew/Cellar/fmt/9.1.0/include\ -L/opt/homebrew/Cellar/fmt/9.1.0/lib\ -lfmt cmake -DCMAKE_SYSROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk/ ..
-- The CXX compiler identification is GNU 12.2.0
-- Checking whether CXX compiler has -isysroot
-- Checking whether CXX compiler has -isysroot - yes
-- Checking whether CXX compiler supports OSX deployment target flag
-- Checking whether CXX compiler supports OSX deployment target flag - yes
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /opt/homebrew/bin/g++-12 - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
CMAKE_BUILD_TYPE=
-- Module support is disabled.
-- Version: 8.0.1
-- Build type: 
-- CXX_STANDARD: 20
-- Performing Test has_std_20_flag
-- Performing Test has_std_20_flag - Success
-- Performing Test has_std_2a_flag
-- Performing Test has_std_2a_flag - Success
-- Performing Test SUPPORTS_USER_DEFINED_LITERALS
-- Performing Test SUPPORTS_USER_DEFINED_LITERALS - Success
-- Performing Test FMT_HAS_VARIANT
-- Performing Test FMT_HAS_VARIANT - Success
-- Required features: cxx_variadic_templates
-- Looking for strtod_l
-- Looking for strtod_l - found
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/nikita/Desktop/test/asyncio/build
nikita@MacBook-Air-Nikita build % make -j
[  2%] Built target nanobench
[  5%] Built target fmt
[  6%] Building CXX object CMakeFiles/asyncio.dir/src/event_loop.cpp.o
[ 83%] Built target Catch2
[ 85%] Built target Catch2WithMain
[ 86%] Linking CXX shared library libasyncio.dylib
[ 86%] Built target asyncio
[ 87%] Building CXX object test/pt/CMakeFiles/sched_test.dir/sched_test.cpp.o
[ 88%] Building CXX object test/st/CMakeFiles/hello_world.dir/hello_world.cpp.o
[ 89%] Building CXX object test/st/CMakeFiles/echo_client.dir/echo_client.cpp.o
[ 91%] Building CXX object test/st/CMakeFiles/echo_server.dir/echo_server.cpp.o
[ 92%] Building CXX object test/ut/CMakeFiles/asyncio_ut.dir/task_test.cpp.o
[ 93%] Building CXX object test/ut/CMakeFiles/asyncio_ut.dir/selector_test.cpp.o
[ 94%] Building CXX object test/ut/CMakeFiles/asyncio_ut.dir/result_test.cpp.o
[ 95%] Linking CXX executable hello_world
[ 96%] Linking CXX executable sched_test
[ 96%] Built target hello_world
[ 97%] Linking CXX executable echo_server
[ 97%] Built target sched_test
[ 97%] Built target echo_server
[ 98%] Linking CXX executable echo_client
[ 98%] Built target echo_client
[100%] Linking CXX executable asyncio_ut
[100%] Built target asyncio_ut
nikita@MacBook-Air-Nikita build % sudo make install
Password:
[  3%] Built target fmt
[  5%] Built target asyncio
[  7%] Built target hello_world
[ 10%] Built target echo_client
[ 12%] Built target echo_server
[ 14%] Built target nanobench
[ 91%] Built target Catch2
[ 93%] Built target Catch2WithMain
[ 95%] Built target sched_test
[100%] Built target asyncio_ut
Install the project...
-- Install configuration: ""
CMake Error at cmake_install.cmake:59 (file):
  file cannot create directory: /usr/include/asyncio.  Maybe need
  administrative privileges.


make: *** [install] Error 1

I tried to compile from the command line.

nikita@MacBook-Air-Nikita build % brew install fmt
nikita@MacBook-Air-Nikita test % g++-12 -fdiagnostics-color=always -c asyncio/src/event_loop.cpp -o asyncio/src/event_loop.o --sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk/ -I asyncio/include -I/opt/homebrew/Cellar/gcc/12.2.0/include/ -I/opt/homebrew/Cellar/fmt/9.1.0/include -L/opt/homebrew/Cellar/fmt/9.1.0/lib -lfmt --std=c++20 -O3
nikita@MacBook-Air-Nikita test % ar rvs asyncio/src/event_loop.a asyncio/src/event_loop.o
ar: creating archive asyncio/src/event_loop.a
a - asyncio/src/event_loop.o
nikita@MacBook-Air-Nikita test % g++-12 -fdiagnostics-color=always -g test1.cpp -o test1 asyncio/src/event_loop.a --sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk/ -I asyncio/include -I/opt/homebrew/Cellar/gcc/12.2.0/include/ -I/opt/homebrew/Cellar/fmt/9.1.0/include -L/opt/homebrew/Cellar/fmt/9.1.0/lib -lfmt --std=c++20 -O3
Undefined symbols for architecture arm64:
  "__ZN3fmt2v97vformatB5cxx11ENS0_17basic_string_viewIcEENS0_17basic_format_argsINS0_20basic_format_contextINS0_8appenderEcEEEE", referenced from:
      __ZNK7asyncio4TaskIiE12promise_type14dump_backtraceEm in ccM2pz6n.o
      __ZNK7asyncio4TaskIbE12promise_type14dump_backtraceEm in ccM2pz6n.o
ld: symbol(s) not found for architecture arm64
collect2: error: ld returned 1 exit status

I just added into asyncio/include/asyncio/event_loop.h (link):

#define FMT_HEADER_ONLY
#include <fmt/core.h>
#include <fmt/format.h>
nikita@MacBook-Air-Nikita test % g++-12 -fdiagnostics-color=always -c asyncio/src/event_loop.cpp -o asyncio/src/event_loop.o --sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk/ -I asyncio/include -I/opt/homebrew/Cellar/gcc/12.2.0/include/ -I/opt/homebrew/Cellar/fmt/9.1.0/include -L/opt/homebrew/Cellar/fmt/9.1.0/lib -lfmt --std=c++20 -O3
nikita@MacBook-Air-Nikita test % ar rvs asyncio/src/event_loop.a asyncio/src/event_loop.o 
r - asyncio/src/event_loop.o
nikita@MacBook-Air-Nikita test % g++-12 -fdiagnostics-color=always -g test1.cpp -o test1 asyncio/src/event_loop.a --sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk/ -I asyncio/include -I/opt/homebrew/Cellar/gcc/12.2.0/include/ -I/opt/homebrew/Cellar/fmt/9.1.0/include -L/opt/homebrew/Cellar/fmt/9.1.0/lib -lfmt --std=c++20 -O3
nikita@MacBook-Air-Nikita test % ./test1
true

All works!

Test 1

// test1.cpp
#include <iostream>
#include <asyncio/event_loop.h>
#include <asyncio/runner.h>
#include <asyncio/task.h>

using asyncio::Task;

auto main() -> int {
    auto completes_synchronously = []() -> Task<int> {
        co_return 1;
    };

    auto main = [&]() -> Task<bool> {
        int sum = 0;
        for (int i = 0; i < 1'000'000; ++i) {
            sum += co_await completes_synchronously();
        }
        co_return sum == 1'000'000;
    };

    std::cout << std::boolalpha << asyncio::run(main()) << std::endl;
    return 0;
}
Test 2
// test2.cpp
#include <iostream>
#include <asyncio/event_loop.h>
#include <asyncio/runner.h>
#include <asyncio/gather.h>
#include <asyncio/task.h>  
using asyncio::Task;  
auto main() -> int {
    auto completes_synchronously = []() -> Task<int> {
        co_return 1;
    };  
    auto main = [&]() -> Task<bool> {
        int sum = 0;
        for (int i = 0; i < 1'000'000; ++i) {
            sum += co_await completes_synchronously();
        }
        co_return sum == 1'000'000;
    };  
    std::cout << std::boolalpha << asyncio::run([&]() -> Task<bool> {
        auto&& [R1, R2, R3, R4, R5, R6, R7, R8] = co_await asyncio::gather(
            main(), main(), main(), main(), main(), main(), main(), main()
        );
        std::vector result { R1, R2, R3 };
        co_return std::all_of(result.begin(), result.end(), [](auto element){ return !!element; });
    }()) << std::endl;
    return 0;
}
Test 3
// test3.cpp
#include <iostream>
#include <asyncio/schedule_task.h>
#include <asyncio/callstack.h>
#include <asyncio/runner.h>
#include <asyncio/sleep.h>
#include <asyncio/task.h>
#include <fmt/format.h>
#include <string_view>
#include <string>

using namespace asyncio;

Task<std::string_view> hello() {
    co_return "hello";
}

Task<std::string_view> world() {
    co_return "world";
}

Task<std::string> hello_world() {
    co_return fmt::format("{} {}", co_await hello(), co_await world());
}

int main() {
    fmt::print("run result: {}\n", asyncio::run(hello_world()));
    return 0;
}
Test 4
// test4_echo_server.cpp
#include <iostream>
#include <asyncio/stream.h>
#include <arpa/inet.h>
#include <asyncio/runner.h>
#include <asyncio/start_server.h>
#include <asyncio/task.h>
#include <fmt/core.h>

using asyncio::Stream;
using asyncio::Task;
using asyncio::get_in_port;
using asyncio::get_in_addr;

int add_count = 0;
int rel_count = 0;

Task<> handle_echo(Stream stream) {
    auto sockinfo = stream.get_sock_info();
    char addr[INET6_ADDRSTRLEN] {};
    auto sa = reinterpret_cast<const sockaddr*>(&sockinfo);

    ++add_count;
    fmt::print("connections: {}/{}\n", rel_count, add_count);
    while (true) {
        try {
            auto data = co_await stream.read(200);
            if (data.empty()) { break; }
            fmt::print("Received: '{}' from '{}:{}'\n", data.data(),
                    inet_ntop(sockinfo.ss_family, get_in_addr(sa), addr, sizeof addr),
                    get_in_port(sa));
            co_await stream.write(data);
        } catch (...) {
            break;
        }
    }
    ++rel_count;
    fmt::print("connections: {}/{}\n", rel_count, add_count);
    stream.close();
}

Task<> echo_server() {
    auto server = co_await asyncio::start_server(
            handle_echo, "127.0.0.1", 8888);

    fmt::print("Serving on 127.0.0.1:8888\n");

    co_await server.serve_forever();
}

int main() {
    asyncio::run(echo_server());
    return 0;
}
// test4_echo_client.cpp
#include <iostream>
#include <asyncio/open_connection.h>
#include <asyncio/runner.h>
#include <asyncio/wait_for.h>

using asyncio::Task;
using asyncio::Stream;
using namespace std::chrono;

Task<> tcp_echo_client(std::string_view message) {
    auto stream = co_await asyncio::open_connection("127.0.0.1", 8888);

    fmt::print("Send: '{}'\n", message);
    co_await stream.write(Stream::Buffer(message.begin(), message.end() + 1 /* plus '\0' */));

    auto data = co_await asyncio::wait_for(stream.read(100), 300ms);
    fmt::print("Received: '{}'\n", data.data());

    fmt::print("Close the connection\n");
    stream.close();
}

int main(int argc, char** argv) {
    asyncio::run(tcp_echo_client("hello world!"));
    return 0;
}
Test 5
// test5.cpp
#include <iostream>
#include <asyncio/event_loop.h>
#include <asyncio/selector/selector.h>

using namespace ASYNCIO_NS;
using namespace std::chrono;

int main() {
    EventLoop loop;
    Selector selector;
    auto before_wait = loop.time();
    selector.select(300); // Without
    auto after_wait = loop.time();
    std::cout << std::boolalpha << (after_wait - before_wait >= 300ms) << std::endl;
    return 0;
}
Test 6
// test6.cpp
#include <iostream>
#include <asyncio/runner.h>
#include <asyncio/callstack.h>
#include <asyncio/task.h>
#include <asyncio/gather.h>
#include <asyncio/exception.h>
#include <asyncio/sleep.h>
#include <asyncio/schedule_task.h>
#include <asyncio/wait_for.h>
#include <asyncio/start_server.h>
#include <asyncio/open_connection.h>
#include <functional>
#include <limits>

using namespace ASYNCIO_NS;
using namespace std::chrono_literals;

template<typename...> struct dump;
template<size_t N>
Task<> coro_depth_n(std::vector<int>& result) {
    result.push_back(N);
    if constexpr (N > 0) {
        co_await coro_depth_n<N - 1>(result);
        result.push_back(N * 10);
    }
}

auto test_Task_await() {
    std::vector<int> result;
    {
        asyncio::run(coro_depth_n<0>(result));
        std::vector<int> expected { 0 };
        std::cout << std::boolalpha << "simple await: " << (result == expected) << std::endl;
        result.clear();
    }

    {
        asyncio::run(coro_depth_n<1>(result));
        std::vector<int> expected { 1, 0, 10 };
        std::cout << std::boolalpha << "nest await: " << (result == expected) << std::endl;
        result.clear();
    }

    {
        asyncio::run(coro_depth_n<2>(result));
        std::vector<int> expected { 2, 1, 0, 10, 20 };
        std::cout << std::boolalpha << "2 depth await: " << (result == expected) << std::endl;
        result.clear();
    }

    {
        asyncio::run(coro_depth_n<3>(result));
        std::vector<int> expected { 3, 2, 1, 0, 10, 20, 30 };
        std::cout << std::boolalpha << "3 depth await: " << (result == expected) << std::endl;
        result.clear();
    }

    {
        asyncio::run(coro_depth_n<4>(result));
        std::vector<int> expected { 4, 3, 2, 1, 0, 10, 20, 30, 40 };
        std::cout << std::boolalpha << "4 depth await: " << (result == expected) << std::endl;
        result.clear();
    }
}

Task<int64_t> square(int64_t x) {
    co_return x * x;
}

auto Task_test() {
    {
        bool called {false};
        asyncio::run([&]() -> Task<> {
            auto t = square(5);
            auto tt = std::move(t);
            std::cout << std::boolalpha << "co_await empty task<>: " << !t.valid() << std::endl;
            std::cout << std::boolalpha << "co_await empty task<>: " << tt.valid() << std::endl;
            try {
                co_await t;
                std::cout << "co_await empty task<>: " << "no exc (bad)" << std::endl;
            } catch(InvalidFuture const&) {
                std::cout << "co_await empty task<>: " << "good exc" << std::endl;
            } catch(...) {
                std::cout << "co_await empty task<>: " << "bad exc" << std:: endl;
            }
            called = true;
        }());

        std::cout << std::boolalpha << "co_await empty task<>: " << called << std::endl;
    }
}

auto test_Task_await_result_value() {
    {
        auto square_sum = [&](int x, int y) -> Task<int> {
            auto tx = square(x);
            auto x2 = co_await tx;
            auto y2 = co_await square(y);
            co_return x2 + y2;
        };
        std::cout << std::boolalpha << "square_sum 3, 4: " << (asyncio::run(square_sum(3, 4)) == 25) << std::endl;
    }

    {
        std::function<auto(size_t) -> Task<size_t>> fibo =
            [&](size_t n) -> Task<size_t> {
                if (n <= 1) { co_return n; }
                co_return co_await fibo(n - 1) +
                          co_await fibo(n - 2);
            };
        std::cout << std::boolalpha << "fibonacci: " << (asyncio::run(fibo(0)) == 0) << std::endl;
        std::cout << std::boolalpha << "fibonacci: " << (asyncio::run(fibo(1)) == 1) << std::endl;
        std::cout << std::boolalpha << "fibonacci: " << (asyncio::run(fibo(2)) == 1) << std::endl;
        std::cout << std::boolalpha << "fibonacci: " << (asyncio::run(fibo(12)) == 144) << std::endl;
    }
}

auto test_Task_for_loop() {
    auto sequense = [](int64_t n) -> Task<int64_t> {
        int64_t result = 1;
        int64_t sign = -1;
        for (size_t i = 2; i <= n; ++i) {
            result += (co_await square(i)) * sign;
            sign *= -1;
        }
        co_return result;
    };
    std::cout << std::boolalpha << "test_Task_for_loop: " << (asyncio::run(sequense(1)) == 1) << std::endl;
    std::cout << std::boolalpha << "test_Task_for_loop: " << (asyncio::run(sequense(10)) == -55) << std::endl;
    std::cout << std::boolalpha << "test_Task_for_loop: " << (asyncio::run(sequense(100)) == -5050) << std::endl;
    std::cout << std::boolalpha << "test_Task_for_loop: " << (asyncio::run(sequense(100000)) == -5000050000) << std::endl;
}

auto test_schedule_task() {
    bool called{false};
    auto f = [&]() -> Task<int> {
        called = true;
        co_return 0xababcaab;
    };

    {
        asyncio::run([&]() -> Task<> {
            auto handle = asyncio::schedule_task(f());
            co_return;
        }());
        std::cout << std::boolalpha << "run and detach created task: " << !called << std::endl;
    }

    {
        asyncio::run([&]() -> Task<> {
            auto handle = asyncio::schedule_task(f());
            std::cout << std::boolalpha << "run and await created task: " << (co_await handle == 0xababcaab) << std::endl;
            std::cout << std::boolalpha << "run and await created task: " << (co_await handle == 0xababcaab) << std::endl;
        }());
        std::cout << std::boolalpha << "run and await created task: " << called << std::endl;
    }

    {
        asyncio::run([&]() -> Task<> {
            auto handle = asyncio::schedule_task(f());
            handle.cancel();
            try {
                co_await handle;
                std::cout << "cancel and await created task: " << "no exc (bad)" << std::endl;
            } catch(InvalidFuture const&) {
                std::cout << "cancel and await created task: " << "good exc" << std::endl;
            } catch(...) {
                std::cout << "cancel and await created task: " << "bad exc" << std:: endl;
            }
        }());
    }

}

auto int_div(int a, int b) -> Task<double> {
    if (b == 0) { throw std::overflow_error("b is 0!"); }
    co_return a / b;
};

auto test_exception() {
    std::cout << std::boolalpha << "test_exception: " << (
        std::abs(asyncio::run(int_div(4, 2)) - 2.0) <= std::numeric_limits<double>::epsilon()
    ) << std::endl;
    try {
        asyncio::run(int_div(4, 0));
        std::cout << "test_exception: " << "no exc (bad)" << std::endl;
    } catch(std::overflow_error const&) {
        std::cout << "test_exception: " << "good exc" << std::endl;
    } catch(...) {
        std::cout << "test_exception: " << "bad exc" << std:: endl;
    }
}

auto test_gather() {
    bool is_called = false;
    auto factorial = [&](std::string_view name, int number) -> Task<int> {
        int r = 1;
        for (int i = 2; i <= number; ++i) {
            fmt::print("Task {}: Compute factorial({}), currently i={}...\n", name, number, i);
            co_await asyncio::sleep(0.1s);
            r *= i;
        }
        fmt::print("Task {}: factorial({}) = {}\n", name, number, r);
        co_return r;
    };
    auto test_void_func = []() -> Task<> {
        fmt::print("this is a void value\n");
        co_return;
    };

    {
        std::cout << std::boolalpha << "test lvalue & rvalue gather: " << !is_called << std::endl;
        asyncio::run([&]() -> Task<> {
            auto fac_lvalue = factorial("A", 2);
            auto fac_xvalue = factorial("B", 3);
            auto&& fac_rvalue = factorial("C", 4);
            {
                auto&& [a, b, c, _void] = co_await asyncio::gather(
                        fac_lvalue,
                        static_cast<Task<int>&&>(fac_xvalue),
                        std::move(fac_rvalue),
                        test_void_func()
                );
                std::cout << std::boolalpha << "test lvalue & rvalue gather: " << (a == 2) << std::endl;
                std::cout << std::boolalpha << "test lvalue & rvalue gather: " << (b == 6) << std::endl;
                std::cout << std::boolalpha << "test lvalue & rvalue gather: " << (c == 24) << std::endl;
            }
            std::cout << std::boolalpha << "test lvalue & rvalue gather: " << ((co_await fac_lvalue) == 2) << std::endl;
            std::cout << std::boolalpha << "test lvalue & rvalue gather: " << !fac_xvalue.valid() << std::endl; // be moved
            std::cout << std::boolalpha << "test lvalue & rvalue gather: " << !fac_rvalue.valid() << std::endl; // be moved
            is_called = true;
        }());
        std::cout << std::boolalpha << "test lvalue & rvalue gather: " << is_called << std::endl;
        is_called = false;
    }

    {
        std::cout << std::boolalpha << "test gather of gather: " << !is_called << std::endl;
        asyncio::run([&]() -> Task<> {
            auto&& [ab, c, _void] = co_await asyncio::gather(
                    gather(factorial("A", 2),
                            factorial("B", 3)),
                    factorial("C", 4),
                    test_void_func()
            );
            auto&& [a, b] = ab;
            std::cout << std::boolalpha << "test gather of gather: " << (a == 2) << std::endl;
            std::cout << std::boolalpha << "test gather of gather: " << (b == 6) << std::endl;
            std::cout << std::boolalpha << "test gather of gather: " << (c == 24) << std::endl;
            is_called = true;
        }());
        std::cout << std::boolalpha << "test gather of gather: " << is_called << std::endl;
        is_called = false;
    }

    {
        std::cout << std::boolalpha << "test detach gather: " << !is_called << std::endl;
        auto res = asyncio::gather(
            factorial("A", 2),
            factorial("B", 3)
        );
        asyncio::run([&]() -> Task<> {
            auto&& [a, b] = co_await std::move(res);
            std::cout << std::boolalpha << "test detach gather: " << (a == 2) << std::endl;
            std::cout << std::boolalpha << "test detach gather: " << (b == 6) << std::endl;
            is_called = true;
        }());
        std::cout << std::boolalpha << "test detach gather: " << is_called << std::endl;
        is_called = false;
    }

    {
        std::cout << std::boolalpha << "test exception gather: " << !is_called << std::endl;
        try {
            asyncio::run([&]() -> Task<std::tuple<double, int>> {
                is_called = true;
                co_return co_await asyncio::gather(
                    int_div(4, 0),
                    factorial("B", 3)
                );
            }());
            std::cout << "test exception gather: " << "no exc (bad)" << std::endl;
        } catch(std::overflow_error const&) {
            std::cout << "test exception gather: " << "good exc" << std::endl;
        } catch(...) {
            std::cout << "test exception gather: " << "bad exc" << std:: endl;
        }
        std::cout << std::boolalpha << "test exception gather: " << is_called << std::endl;
        is_called = false;
    }
}

auto test_sleep() {
    size_t call_time = 0;
    auto say_after = [&](auto delay, std::string_view what) -> Task<> {
        co_await asyncio::sleep(delay);
        fmt::print("{}\n", what);
        ++call_time;
    };

    {
        call_time = 0;
        auto async_main = [&]() -> Task<> {
            auto task1 = schedule_task(say_after(100ms, "hello"));
            auto task2 = schedule_task(say_after(200ms, "world"));

            co_await task1;
            co_await task2;
        };
        auto before_wait = get_event_loop().time();
        asyncio::run(async_main());
        auto after_wait = get_event_loop().time();
        auto diff = after_wait - before_wait;
        std::cout << std::boolalpha << "schedule sleep and await: " << (diff >= 200ms) << std::endl;
        std::cout << std::boolalpha << "schedule sleep and await: " << (diff < 300ms) << std::endl;
        std::cout << std::boolalpha << "schedule sleep and await: " << (call_time == 2) << std::endl;
    }

    {
        call_time = 0;
        auto async_main = [&]() -> Task<> {
            auto task1 = schedule_task(say_after(100ms, "hello"));
            auto task2 = schedule_task(say_after(200ms, "world"));

            co_await task1;
            task2.cancel();
        };
        auto before_wait = get_event_loop().time();
        asyncio::run(async_main());
        auto after_wait = get_event_loop().time();
        auto diff = after_wait - before_wait;
        std::cout << std::boolalpha << "schedule sleep and cancel: " << (diff >= 100ms) << std::endl;
        std::cout << std::boolalpha << "schedule sleep and cancel: " << (diff < 200ms) << std::endl;
        std::cout << std::boolalpha << "schedule sleep and cancel: " << (call_time == 1) << std::endl;
    }

    {
        call_time = 0;
        auto async_main = [&]() -> Task<> {
            auto task1 = schedule_task(say_after(100ms, "hello"));
            auto task2 = schedule_task(say_after(200ms, "world"));

            co_await task1;
            task2.cancel();
            // delay 300ms to exit
            co_await asyncio::sleep(200ms);
        };
        auto before_wait = get_event_loop().time();
        asyncio::run(async_main());
        auto after_wait = get_event_loop().time();
        auto diff = after_wait - before_wait;
        std::cout << std::boolalpha << "schedule sleep and cancel, delay exit: " << (diff >= 300ms) << std::endl;
        std::cout << std::boolalpha << "schedule sleep and cancel, delay exit: " << (diff < 400ms) << std::endl;
        std::cout << std::boolalpha << "schedule sleep and cancel, delay exit: " << (call_time == 1) << std::endl;
    }
}

auto cancel_a_infinite_loop_coroutine() {
    int count = 0;
    asyncio::run([&]() -> Task<>{
        auto inf_loop = [&]() -> Task<> {
            while (true) {
                ++count;
                co_await asyncio::sleep(1ms);
            }
        };
        auto task = schedule_task(inf_loop());
        co_await asyncio::sleep(10ms);
        task.cancel();
    }());
    std::cout << std::boolalpha << "cancel_a_infinite_loop_coroutine: " << (count > 0) << std::endl;
    std::cout << std::boolalpha << "cancel_a_infinite_loop_coroutine: " << (count < 10) << std::endl;
}

auto test_timeout() {
    bool is_called = false;
    auto wait_duration = [&](auto duration) -> Task<int> {
        co_await sleep(duration);
        fmt::print("wait_duration finished\n");
        is_called = true;
        co_return 0xbabababc;
    };

    auto wait_for_test = [&](auto duration, auto timeout) -> Task<int> {
        co_return co_await wait_for(wait_duration(duration), timeout);
    };

    {
        std::cout << std::boolalpha << "no timeout: " << !is_called << std::endl;
        std::cout << std::boolalpha << "no timeout: " << (asyncio::run(wait_for_test(12ms, 120ms)) == 0xbabababc) << std::endl;
        std::cout << std::boolalpha << "no timeout: " << is_called << std::endl;
        is_called = false;
    }

    {
        std::cout << std::boolalpha << "wait_for with sleep: " << !is_called << std::endl;
        auto wait_for_rvalue = wait_for(sleep(30ms), 50ms);
        asyncio::run([&]() -> Task<> {
            try {
                co_await std::move(wait_for_rvalue);
                std::cout << "wait_for with sleep: " << "no exc (good)" << std::endl;
            } catch (...) {
                std::cout << "wait_for with sleep: " << "bad exc" << std:: endl;
            }
            try {
                co_await wait_for(sleep(50ms), 30ms);
                std::cout << "wait_for with sleep: " << "no exc (bad)" << std::endl;
            } catch(TimeoutError const&) {
                std::cout << "wait_for with sleep: " << "good exc" << std::endl;
            } catch(...) {
                std::cout << "wait_for with sleep: " << "bad exc" << std:: endl;
            }
            is_called = true;
        }());
        std::cout << std::boolalpha << "wait_for with sleep: " << is_called << std::endl;
        is_called = false;
    }

    {
        std::cout << std::boolalpha << "wait_for with gather: " << !is_called << std::endl;
        asyncio::run([&]() -> Task<> {
            try {
                co_await wait_for(gather(sleep(10ms), sleep(20ms), sleep(30ms)), 50ms);
                std::cout << "wait_for with gather: " << "no exc (good)" << std::endl;
            } catch (...) {
                std::cout << "wait_for with gather: " << "bad exc" << std:: endl;
            }
            try {
                co_await wait_for(gather(sleep(10ms), sleep(80ms), sleep(30ms)), 50ms);
                std::cout << "wait_for with gather: " << "no exc (bad)" << std::endl;
            } catch(TimeoutError const&) {
                std::cout << "wait_for with gather: " << "good exc" << std::endl;
            } catch(...) {
                std::cout << "wait_for with gather: " << "bad exc" << std:: endl;
            }
            is_called = true;
        }());
        std::cout << std::boolalpha << "wait_for with gather: " << is_called << std::endl;
        is_called = false;
    }

    {
        try {
            asyncio::run([]() -> Task<> {
                auto v = co_await wait_for(int_div(5, 0), 100ms);
            }());
            std::cout << "notime out with exception: " << "no exc (bad)" << std::endl;
        } catch(std::overflow_error const&) {
            std::cout << "notime out with exception: " << "good exc" << std::endl;
        } catch(...) {
            std::cout << "notime out with exception: " << "bad exc" << std:: endl;
        }
    }

    {
        std::cout << std::boolalpha << "timeout error: " << !is_called << std::endl;
        try {
            asyncio::run(wait_for_test(200ms, 100ms));
            std::cout << "timeout error: " << "no exc (bad)" << std::endl;
        } catch(TimeoutError const&) {
            std::cout << "timeout error: " << "good exc" << std::endl;
        } catch(...) {
            std::cout << "timeout error: " << "bad exc" << std:: endl;
        }
        std::cout << std::boolalpha << "timeout error: " << !is_called << std::endl;
    }

    {
        asyncio::run([]() -> Task<> {
            co_await wait_for(std::suspend_always{}, 1s);
            co_await wait_for(std::suspend_never{}, 1s);
        }());
        std::cout << std::boolalpha << "wait for awaitable: " << true << std::endl;
    }
}

auto echo_server__client() {
    bool is_called = false;
    constexpr std::string_view message = "hello world!";

    asyncio::run([&]() -> Task<> {
        auto handle_echo = [&](Stream stream) -> Task<> {
            auto& sockinfo = stream.get_sock_info();
            auto data = co_await stream.read(100);
            std::cout << std::boolalpha << "echo_server__client(handle_echo): " << (std::string_view{data.data()} == message) << std::endl;
            co_await stream.write(data);
        };

        auto echo_server = [&]() -> Task<> {
            auto server = co_await asyncio::start_server(
                    handle_echo, "127.0.0.1", 8888);
            co_await server.serve_forever();
        };

        auto echo_client = [&]() -> Task<> {
            auto stream = co_await asyncio::open_connection("127.0.0.1", 8888);

            co_await stream.write(Stream::Buffer(message.begin(), message.end()));

            auto data = co_await stream.read(100);
            std::cout << std::boolalpha << "echo_server__client(echo_client): " << (std::string_view{data.data()} == message) << std::endl;
            is_called = true;
        };

        auto srv = schedule_task(echo_server());
        co_await echo_client();
        srv.cancel();
    }());
    std::cout << std::boolalpha << "echo_server__client: " << is_called << std::endl;
}

int main() {
    test_Task_await();
    Task_test();
    test_Task_await_result_value();
    test_Task_for_loop();
    test_schedule_task();
    test_exception();
    test_gather();
    test_sleep();
    cancel_a_infinite_loop_coroutine();
    test_timeout();
    echo_server__client();
    return 0;
}

I'm very unsure if I did everything right, but it compiles, and that was the most important thing for me!
Thank you for your time.

Links:

cmake error: Could not find a package configuration file provided by "fmt"

Below is the error msg:

-- 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
-- Module support is disabled.
-- Version: 8.0.1
-- Build type: 
-- CXX_STANDARD: 20
-- Performing Test has_std_20_flag
-- Performing Test has_std_20_flag - Success
-- Performing Test has_std_2a_flag
-- Performing Test has_std_2a_flag - Success
-- Performing Test SUPPORTS_USER_DEFINED_LITERALS
-- Performing Test SUPPORTS_USER_DEFINED_LITERALS - Success
-- Performing Test FMT_HAS_VARIANT
-- Performing Test FMT_HAS_VARIANT - Success
-- Required features: cxx_variadic_templates
-- Looking for strtod_l
-- Looking for strtod_l - found
CMake Error at CMakeLists.txt:59 (find_package):
  By not providing "Findfmt.cmake" in CMAKE_MODULE_PATH this project has
  asked CMake to find a package configuration file provided by "fmt", but
  CMake did not find one.

  Could not find a package configuration file provided by "fmt" with any of
  the following names:

    fmtConfig.cmake
    fmt-config.cmake

  Add the installation prefix of "fmt" to CMAKE_PREFIX_PATH or set "fmt_DIR"
  to a directory containing one of the above files.  If "fmt" provides a
  separate development package or SDK, be sure it has been installed.


-- Configuring incomplete, errors occurred!
See also "/home/yhc/asyncio/build/CMakeFiles/CMakeOutput.log".```

No install target?

Hi, I tried to install asyncio but it seems an install target isn't present.

Do you plan on adding one?

Thank you for your time.

[Feature Request] TLS Support?

Greetings and also AWESOME WORK @netcan I have been wondering why C++ doesn't have a library like this - been struggling with ASIO. Turns out, it DOES have a library like this I just hadn't heard of it.

One thing that Python Asyncio has going for it that seems to be missing, though, is TLS support. I know adding that isn't easy, but figure it should be on the TODO list of any project like this.

Or, is it more common these days to use tools like stunnel ? I've found TLS outside of Python to be very finnicky to setup.

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.