libcoring is a C++ network library in Proactor pattern (@see: POSA2), and it is based on the new io_uring syscall of linux and coroutine in C++ 20. It's under development now, the progress could be viewed in Github project page.
There are somedocumentsavailable:
- For an overview of development document or to know how libcoring is arranged, please refer todesign document file.
- To build and run this library with demos, checkgetting startfile. Installation and library import/link configuration isn't available yet for it's still developing.
Just take a look at the directorydemo.
//codes are trimmed, check the source `coring/demo/echo_server.cpp` for details
task<>echo_loop(tcp::connection conn){
while(true){
auto& selected =co_awaitbuffer_pool.try_read_block(conn, GID, MAX_MESSAGE_LEN);
selected_buffer_resourcereturn_it_when_exit(selected);
co_awaitsocket_writer(conn, selected).write_all_to_file();
}
}
task<>event_loop() {
co_awaitbuffer_pool.provide_group_contiguous(buf, MAX_MESSAGE_LEN, BUFFERS_COUNT, GID);
while(true) {
autoconn =co_awaitacceptor.accept();
coro::spawn(echo_loop(conn));
}
}
voidrun(){
io_context context;
context.schedule(event_loop());
context.run();
}
//codes are trimmed, check the source `coring/demo/connect_with_timeout.cpp` for details
task<>connect() {
usingnamespacestd::chrono_literals;
autoendpoint =net::endpoint::from_resolve("google",80);
std::cout << endpoint.address_str() << std::endl;
autoconn =co_awaittcp::connect_to(endpoint, 3s);//timer in kernel, it would throw an exception if timeout
//... do sth with conn
//....................
co_awaittimeout(20s);//timer in user space, see coring/test/io_context_test.cpp
//with coroutine, this is natual
//... do sth else
//....................
}
voidrun() {
io_context context;
context.schedule(connect(&context));
context.run();
}
Notice: sinceIORING_ASYNC_CANCEL_ANY
in io_uring will soon be released, the development on cancellation part of
libcoring stalls now. The cancellation branch would be marked decrapted and be deleted soon. By the time the new kernel
is available, the development would go on with another branch. Codes below may be not supported or supported in newer
versions.
task<>connect(io_context *ioc) {
usingnamespacestd::chrono_literals;
io_cancel_source src;
autoep =net::endpoint::from_resolve("google",80);
//non-blocking, fire-and-forget, async_task
autopromise =tcp::connect_to(ep, src.get_token());
//I can just do many other things here, say do some O(n^2) computing...
//......
//use this for simplicity, user space timer is low cost-effective here...
co_awaitioc->timeout(3s);
if(!promise.is_ready()) {
autores =co_awaitsrc.cancel_and_wait_for_result(*ioc);
//res is 0 if successfully cancelled
}
[[maybe_unused]]autoc =co_awaitpromise;
//here may throw if cancelled, I didn't decide if we should count ECLEAN as an exception
}
For more details, checkgetting startfile.
Now the comparison is focus on the overhead of the utilities and coroutine abstraction in libcoring, when it comes to io_uring versus epoll/... is another topics. Currently, only httpd and echo-server is benched. Checkbenchmarkpage for details.
Without SQPoll, compared to raw C liburing interface (100%):
utility | QPS | Throughput |
---|---|---|
webbench (120 bytes) | 102% | 99% |
apache bench (120 bytes) | 101% | 102% |
rust-echo-bench (512 bytes) | 96% | N/A |
rust-echo-bench (2018 bytes) | 102% | N/A |
- [*now] Benchmarking for all module with alternatives designs.
- Threading, no good threading model design now.
- more simple buffer interface, and improve the performance on sth like
insert_iterator
with fmt. - [suspended]Cancellation for io_uring.
This project learn from following library or repositories:
- proactor patternboost.asio
- use buffer selection of io_uring in practiceio_uring-echo-server
- use io_uring in practiceLord of the io_uring guide
- executor model (might be accepted in C++23)libunifex
This project learn and modified some codes from following library or repositories:
- use C++20 coroutine in practice:cppcoro
- use io_uring in OOP wayliburing4cpp
The type of coroutinetask<>
is lazy in libcoring, just like theawaitable<>
in boost::asio, arguments on
lazytask<>
or non-lazytask<>
both make sense, the difference is only in theinitial_suspend
function, I would
try to find which is better design, maybe lazy task cooperate with executor would be enough:
- P1056R0: task
- P0443R12: executor
- Fire and forget
- Cancellation Problem
- my non-lazy task impl
- another C++2a coroutine library
The cancellation for io_uring and combined with stackless coroutine would be a little difficult to image. There are mature solution in C# await and async facilities, but IOCP differs to io_uring on both cancellation and multithreading restrictions. Although a simple cancellation token could be used for coroutine, but the supports for io_uring would be another subject. Another things is that the cancellation part in io_uring is still keep going, recently the CANCEL_ANY is patched, cancellation is now supporting cancel more than one request a time (use a fd). But it requires a further upgrade of latest kernel, I would try once it's available.
- cppcoro::Cancellation
- Cancellation in C#
- Use Cancellation in C#
- one io_uring patch
- another io_uring patch