Skip to content

Commit 9919b21

Browse files
committed
* added busy map so we don't select()
events that are currently ongoing * add disconnect log * change busy map logic * removed shadowing of a variable in remove lambda * re-wrote the recv handling on server again, should be good now * added -wall in build for all platforms
1 parent 4277f4c commit 9919b21

File tree

3 files changed

+86
-67
lines changed

3 files changed

+86
-67
lines changed

CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@ cmake_minimum_required(VERSION 3.15)
22
project(netlib)
33
set(CMAKE_CXX_STANDARD 20)
44

5+
# we want the maximum amount of compiler bickering
6+
if(MSVC)
7+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
8+
else()
9+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
10+
endif()
11+
512
find_package(Threads)
613

714
set(NETLIB_SRC

src/server.hpp

Lines changed: 78 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <cassert>
88
#include <functional>
99
#include <iostream>
10+
#include <map>
1011
#include <mutex>
1112
#include <optional>
1213
#include <thread>
@@ -43,7 +44,8 @@ namespace netlib {
4344
callback_error_t _cb_on_error{};
4445
std::thread _accept_thread;
4546
std::thread _processor_thread;
46-
netlib::thread_pool _thread_pool;
47+
netlib::thread_pool _thread_pool = netlib::thread_pool::create<1,1>();
48+
std::map<socket_t, std::atomic<bool>> _busy_map;
4749

4850
inline void processing_func() {
4951
while (_server_active) {
@@ -54,9 +56,14 @@ namespace netlib {
5456
{
5557
std::lock_guard<std::mutex> lock(_mutex);
5658
local_clients = _clients;
57-
for (auto& client : local_clients) {
59+
for (auto &client : local_clients) {
5860
socket_t fd = client.socket.get_raw().value();
59-
if (highest_fd < fd){
61+
assert(_busy_map.contains(fd));
62+
if (_busy_map[fd]) {
63+
// this fd is currently being handled by a task in threadpool
64+
continue;
65+
}
66+
if (highest_fd < fd) {
6067
highest_fd = fd;
6168
}
6269
FD_SET(fd, &fdset);
@@ -69,20 +76,31 @@ namespace netlib {
6976
int32_t select_res = ::select(highest_fd + 1, &fdset, nullptr, nullptr, &tv);
7077
if (select_res > 0) {
7178
std::vector<client_endpoint> client_refs(select_res);
72-
uint32_t index = 0;
79+
int32_t index = 0;
7380
{
81+
std::lock_guard<std::mutex> lock(_mutex);
7482
for (auto& client : local_clients) {
7583
socket_t fd = client.socket.get_raw().value();
7684
if (FD_ISSET(fd, &fdset)){
7785
client_refs[index++] = client;
86+
assert(_busy_map.contains(fd));
87+
_busy_map[fd] = true;
7888
}
7989
}
8090
}
8191
assert(index == select_res);
8292
//add callback tasks to threadpool for processing
8393
for (auto& client_to_recv : client_refs) {
8494
_thread_pool.add_task([&](client_endpoint ce){
85-
this->handle_client(ce);
95+
socket_t id = ce.socket.get_raw().value();
96+
std::error_condition error = this->handle_client(ce);
97+
if ((error) && (_cb_on_error)) {
98+
_cb_on_error(ce, error);
99+
}
100+
std::lock_guard<std::mutex> lock(_mutex);
101+
if (_busy_map.contains(id)) {
102+
_busy_map[id] = false;
103+
}
86104
}, client_to_recv);
87105
}
88106

@@ -100,7 +118,7 @@ namespace netlib {
100118
while (_server_active) {
101119
std::this_thread::sleep_for(std::chrono::milliseconds(10));
102120
new_endpoint.addr_len = sizeof(addrinfo);
103-
int32_t status = ::accept(_listener_sock->get_raw().value(), &new_endpoint.addr, &new_endpoint.addr_len);
121+
socket_t status = ::accept(_listener_sock->get_raw().value(), &new_endpoint.addr, &new_endpoint.addr_len);
104122
if (status > 0) {
105123
new_endpoint.socket.set_raw(status);
106124
new_endpoint.socket.set_nonblocking(true);
@@ -120,86 +138,82 @@ namespace netlib {
120138
if (_cb_on_error) {
121139
_cb_on_error(new_endpoint, std::errc::connection_aborted);
122140
}
123-
new_endpoint.socket.close();
141+
remove_client(new_endpoint);
124142
std::cout << "server kicked client accept" << std::endl;
125143
continue;
126144
}
127145
}
128146
if (new_endpoint.socket.is_valid()) {
129147
std::lock_guard<std::mutex> lock(_mutex);
148+
std::cout << "server added new client, id " << status << std::endl;
130149
_clients.push_back(new_endpoint);
150+
_busy_map[status] = false;
131151
}
132152
}
133153
}
134154
}
135155

136156
inline std::error_condition handle_client(client_endpoint endpoint) {
137157
std::vector<uint8_t> total_buffer;
138-
std::array<uint8_t, 2048> buffer{};
158+
std::array<uint8_t, 1024> buffer{};
139159
ssize_t recv_res = 0;
140-
ssize_t recv_res_cycle = 0;
141-
while ((recv_res_cycle = ::recv(endpoint.socket.get_raw().value(), buffer.data(), buffer.size(), 0)) > 0) {
142-
total_buffer.insert(total_buffer.end(), buffer.begin(), buffer.begin() + recv_res_cycle);
143-
recv_res += recv_res_cycle;
144-
}
145-
if (recv_res == 0){
146-
std::cout << "server recv_res == 0" << std::endl;
147-
if (_cb_on_error) {
148-
_cb_on_error(endpoint, std::errc::connection_aborted);
149-
}
150-
remove_client(endpoint.socket.get_raw().value());
151-
endpoint.socket.close();
152-
std::cout << "server kicked client recv 0" << std::endl;
153-
return std::errc::connection_aborted;
154-
} else if (recv_res < 0) {
155-
//error
156-
std::cout << "server recv_res == -1" << std::endl;
157-
std::error_condition recv_error = socket_get_last_error();
158-
//we do not want to spam callback with wouldblock messages
159-
//for portability we shall check both EAGAIN and EWOULDBLOCK
160-
if ((recv_error != std::errc::resource_unavailable_try_again) &&
161-
(recv_error != std::errc::operation_would_block)) {
162-
if (_cb_on_error) {
163-
_cb_on_error(endpoint, recv_error);
164-
}
165-
}
166-
return recv_error;
167-
} else {
168-
std::cout << "server recv_res > 0: " << recv_res << " " << total_buffer.size() << std::endl;
169-
//we got data
170-
if (_cb_on_recv) {
171-
netlib::server_response response = _cb_on_recv(endpoint, total_buffer);
172-
if (!response.answer.empty()) {
173-
ssize_t send_result = ::send(endpoint.socket.get_raw().value(),
174-
response.answer.data(),
175-
response.answer.size(),
176-
0);
177-
if ((send_result != response.answer.size()) && (_cb_on_error)) {
178-
_cb_on_error(endpoint, socket_get_last_error());
179-
}
180-
}
181-
if (response.terminate) {
182-
if (_cb_on_error) {
183-
_cb_on_error(endpoint, std::errc::connection_aborted);
160+
while (true) {
161+
recv_res = ::recv(endpoint.socket.get_raw().value(),
162+
buffer.data(), buffer.size(), 0);
163+
if (recv_res > 0) {
164+
total_buffer.insert(total_buffer.end(), buffer.begin(),
165+
buffer.begin() + recv_res);
166+
} else if (recv_res == 0) {
167+
std::cout << "server recv_res == 0" << std::endl;
168+
remove_client(endpoint);
169+
std::cout << "server kicked client recv 0" << std::endl;
170+
return std::errc::connection_aborted;
171+
} else if (recv_res < 0) {
172+
// error
173+
std::error_condition recv_error = socket_get_last_error();
174+
if ((recv_error == std::errc::resource_unavailable_try_again) ||
175+
(recv_error == std::errc::operation_would_block)) {
176+
// no more data, return what we got
177+
if (_cb_on_recv) {
178+
netlib::server_response response =
179+
_cb_on_recv(endpoint, total_buffer);
180+
if (!response.answer.empty()) {
181+
ssize_t send_result = ::send(
182+
endpoint.socket.get_raw().value(),
183+
response.answer.data(), response.answer.size(), 0);
184+
if (send_result != response.answer.size()) {
185+
return socket_get_last_error();
186+
}
187+
}
188+
if (response.terminate) {
189+
remove_client(endpoint);
190+
std::cout << "server kicked client because wanted"
191+
<< std::endl;
192+
return std::errc::connection_aborted;
193+
}
184194
}
185-
remove_client(endpoint.socket.get_raw().value());
186-
endpoint.socket.close();
187-
std::cout << "server kicked client because wanted" << std::endl;
195+
return {};
196+
} else {
197+
std::cout << "server recv_res == -1" << std::endl;
198+
return recv_error;
188199
}
189-
190200
}
191-
return {};
192201
}
193-
194202
}
195203

196-
bool remove_client(socket_t socket_id) {
204+
bool remove_client(client_endpoint& ce) {
197205
//the remove_if-> erase idiom is perhaps my most hated part about std containers
198206
std::lock_guard<std::mutex> lock(_mutex);
199-
return _clients.erase(
200-
std::remove_if(_clients.begin(), _clients.end(), [&](const client_endpoint& ce) {
201-
return ce.socket.get_raw() == socket_id;
202-
}),_clients.end()) != _clients.end();
207+
std::size_t client_count = _clients.size();
208+
_busy_map.erase(ce.socket.get_raw().value());
209+
std::cout << "remove_client, client count at start " << _clients.size() << std::endl;
210+
std::erase_if(_clients, [&](const client_endpoint& single_endpoint){
211+
return (ce.socket.get_raw().value() == single_endpoint.socket.get_raw().value());
212+
});
213+
std::cout << "removed client, id " << ce.socket.get_raw().value() << std::endl;
214+
ce.socket.close();
215+
assert((client_count - _clients.size()) == 1);
216+
return true;
203217
}
204218

205219
public:
@@ -233,9 +247,7 @@ namespace netlib {
233247
close_and_free();
234248
continue;
235249
}
236-
_listener_sock->set_nonblocking(true);
237-
//_listener_sock->set_reuseaddr(true);
238-
250+
_listener_sock->set_nonblocking(true); //we want to be able to join
239251
int32_t res = ::bind(_listener_sock->get_raw().value(), res_addrinfo->ai_addr, res_addrinfo->ai_addrlen);
240252
if (res < 0) {
241253
close_and_free();

src/thread_pool.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ namespace netlib {
4242
}
4343
void create_threads(std::size_t thread_count) {
4444
_thread_pool.reserve(thread_count + _thread_pool.size());
45-
for (int i = 0; i < thread_count; ++i) {
45+
for (uint32_t i = 0; i < thread_count; ++i) {
4646
add_thread();
4747
}
4848
}

0 commit comments

Comments
 (0)