yukunj / turtle Goto Github PK
View Code? Open in Web Editor NEWA C++17-based lightweight high-performance network library
Home Page: https://github.com/YukunJ/Turtle
License: MIT License
A C++17-based lightweight high-performance network library
Home Page: https://github.com/YukunJ/Turtle
License: MIT License
New Features Timer
should be added to track those inactive client Connection
and terminate them to save system socket resources.
The basic idea is to maintain a list/set of individual timers in a container use the system calls
#include <sys/timerfd.h>
int timerfd_create(int clockid, int flags);
int timerfd_settime(int fd, int flags,
const struct itimerspec *new_value,
struct itimerspec *old_value);
int timerfd_gettime(int fd, struct itimerspec *curr_value);
to set the alarm to be the earliest timeout time from the whole container individual timer. Upon timeout triggered, prune the container so as to delete expired Connection
. And when tackle new messages from a connection, the timer for that Connection
should be refreshed, and potentially reset the timer_fd
's alarm time if it happens to be the earliest timeout.
One caveat is that, we need to be careful about the life time management of Connection
. Hopefully, if we terminate the connection by closing the fd, it will not appear in next round's Epoll
call.
To be done in April/May when I have some free time.
Thanks to Cppers on reddit here.
Currently the Looper
use std::map<int, std::unique_ptr<Connection>>
to keep track of which connection's lifecycle is under its monitoring.
/**
* This Looper acts as the executor on a single thread
* adopt the philosophy of 'one looper per thread'
* */
class Looper {
public:
...
private:
...
std::map<int, std::unique_ptr<Connection>> connections_;
...
};
However, since we don't required the ordered property provided by std::map
, a better performance could be achieved by using std::unordered_map
. Typically given the fact that currently any access to this connections_
variable requires mutex
for synchronization.
I plan to implement this feature shortly.
Yukun
Feb 07
In the Acceptor
's event setter, used EPOLL_CTL_ADD
instead of EPOLLIN
as the read event signal. This is incorrect. It works before because they share the same value. But this is not portable.
Should distinguish the "operator" EPOLL_CTL_ADD
and event signal EPOLLIN
.
Same applies to MacOS Kqueue part.
Thanks to "大橘为重" on Zhihu platform for raising this issue to my attention.
rt
As suggested by my net friend, it's best to have clang-tidy
built into the project, which can help reveal many potential bugs in the source code.
Should be an easy fix to do.
Yukun
Feb 07
server在poll到连接新的事件时,貌似没有更新连接的超时时间。
One commenter 忘机 on Zhihu points out that:
auto resource_cached = cache->TryLoad(resource_full_path, cache_buf);
response.SetShouldTransferContent(
request.GetMethod() != Method::HEAD && !resource_cached);
no_more_parse = request.ShouldClose();
response.Serialize(response_buf);
if (resource_cached) {
// content directly from cache, not disk file I/P
response_buf.insert(response_buf.end(), cache_buf.begin(),
cache_buf.end());
} else {
// content not in cache, try store it
LoadFile(resource_full_path, cache_buf);
cache->TryInsert(resource_full_path, cache_buf);
}
Here when the requested file is not in the cache,response.Serialize(response_buf);
will load the content into response_buf
. But then the same file is loaded from Disk again to be saved in the cache.
In short, currently on a cold miss, the file is fetched from Disk twice. It could have directly saved from the response_buf
into cache the first time of loading, to save 1 disk I/O.
To be fixed soon, preferably in March.
Yukun
Code snippet at src/core/acceptor.cpp:65.
void Acceptor::SetCustomAcceptCallback(
std::function<void(Connection *)> custom_accept_callback) {
custom_accept_callback_ = std::move(custom_accept_callback);
acceptor_conn->SetCallback([this](auto &&PH1) {
BaseAcceptCallback(std::forward<decltype(PH1)>(PH1));
GetCustomAcceptCallback()(std::forward<decltype(PH1)>(PH1));
});
}
GetCustomAcceptCallback()
returns custom_accept_callback_
. Using custom_accept_callback_
may be more readable?
The following is the cope snippet for the Connection::Send()
which outwrites all the data stored in the Buffer
, it will stay in this function loop until all data has been sent out. In this sense, this code snippet is correct.
void Connection::Send() {
// robust write
ssize_t curr_write = 0;
ssize_t write;
const ssize_t to_write = GetWriteBufferSize();
const unsigned char *buf = write_buffer_->Data();
while (curr_write < to_write) {
if ((write = send(GetFd(), buf + curr_write, to_write - curr_write, 0)) <=
0) {
if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) {
perror("Error in Connection::Send()");
ClearWriteBuffer();
return;
}
write = 0;
}
curr_write += write;
}
ClearWriteBuffer();
}
However, one thing to be optimized is that: when the data to sent is large in size, the underlying socket buffer allocated by the operating system might be full and throws back EAGAIN/EWOULDBLOCK
to the send()
calls. In current version, the code just sit and wait until the buffer is empty enough to keep sending.
However, a better approach would be to register to the Poller
that "We are interested in the being able to write more event of this TCP connection", and this worker thread could go to work on other client's callbacks. When the write buffer is empty enough, OS will notifies the Poller
in the next round and it could continue sending the leftover bits.
Most likely, the above approach could achieve a better overall performance of the server, at the little cost of that "overloaded outwrite" connection might experience a short delay of full response since it might need to wait for next round of polling.
I plan to implement this feature in the near future.
Yukun
Feb 06, 2023
benchmark.sh: line 17: ./webbench: No such file or directory
What if use sockaddr_storage
instead of sockaddr
for both IPv4 and IPv6?
Seems sockaddr
and sockaddr_in
are both 16bytes, however sockaddr_in6
is 28 bytes, would it access illegal address after converting sockaddr*
into sockaddr_in6*
for IPv6?
Currently the Looper
contains a map of std::map<int, std::unique_ptr<Connection>>
. However, another alternative approach is to directly store as std::map<int, Connection>
.
Why I don't implement it this way during development? Because it's easier to manage the lifecycle of the Connection
by using std::unique_ptr
, because upon destruction Connection
will close its Socket
nested.
However, the alternative approach might have better performance as it reduces one layer of indirect memory access. (int
to std::unique_ptr
to real Connection
).
It is worth experimenting, but not a high priority.
Yukun
Feb 07
Appreciate kind net friends from reddit post to give me more revision advice.
- ✅ use TARGET_INCLUDE_DIRECTORIES instead of INCLUDE_DIRECTORIES
- ❌ FILE(GLOB) is not recommended
Ref: https://cmake.org/cmake/help/latest/command/file.html
-✅ avoid macros wherever its applicable use const or constexpr
-✅ Follow the coding guidelines pick google style or any other popular c++ coding guidelines
- ❌ Follow the naming conventions as per the coding guidelines
Ex: namespace TURTLE_SERVER should be in small case
- ✅ explicit not required for multi args constructor
Ex: explicit Acceptor,explicit Connection
- [Not Applicable, use `std::vector<unsigned char>` already] Use std::array instead of C-Style arrays
Most of these suggestions seem plausible. Will carefully look over them one by one and revise once I get a moment.
✅ means already adopted and revised.
❌ means it's not to be revised in near future.
Yukun
Feb 07
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.