5
5
#include < nano/node/transport/socket.hpp>
6
6
#include < nano/node/transport/transport.hpp>
7
7
8
+ #include < boost/asio/use_future.hpp>
9
+ #include < boost/exception/detail/exception_ptr.hpp>
8
10
#include < boost/format.hpp>
9
11
10
12
#include < cstdint>
@@ -36,8 +38,11 @@ nano::transport::socket::socket (nano::node & node_a, boost::asio::ip::tcp::sock
36
38
last_receive_time_or_init{ nano::seconds_since_epoch () },
37
39
default_timeout{ node_a.config .tcp_io_timeout },
38
40
silent_connection_tolerance_time{ node_a.network_params .network .silent_connection_tolerance_time },
41
+ read_buffer{ 16384 },
42
+ buffer_condition{ strand },
39
43
max_queue_size{ max_queue_size_a }
40
44
{
45
+ os_buffer.insert (os_buffer.begin (), 16384 , 0 );
41
46
}
42
47
43
48
nano::transport::socket::~socket ()
@@ -88,6 +93,7 @@ void nano::transport::socket::async_connect (nano::tcp_endpoint const & endpoint
88
93
node_l->observers .socket_connected .notify (*this_l);
89
94
}
90
95
callback (ec);
96
+ this_l->begin_read_loop ();
91
97
}));
92
98
});
93
99
}
@@ -101,31 +107,9 @@ void nano::transport::socket::async_read (std::shared_ptr<std::vector<uint8_t>>
101
107
if (!closed)
102
108
{
103
109
set_default_timeout ();
104
- boost::asio::post (strand, [this_l = shared_from_this (), buffer_a, callback = std::move (callback_a), size_a] () mutable {
105
- boost::asio::async_read (this_l->tcp_socket , boost::asio::buffer (buffer_a->data (), size_a),
106
- boost::asio::bind_executor (this_l->strand ,
107
- [this_l, buffer_a, cbk = std::move (callback)] (boost::system::error_code const & ec, std::size_t size_a) {
108
- debug_assert (this_l->strand .running_in_this_thread ());
109
-
110
- auto node_l = this_l->node_w .lock ();
111
- if (!node_l)
112
- {
113
- return ;
114
- }
115
-
116
- if (ec)
117
- {
118
- node_l->stats .inc (nano::stat::type::tcp, nano::stat::detail::tcp_read_error, nano::stat::dir::in);
119
- this_l->close ();
120
- }
121
- else
122
- {
123
- node_l->stats .add (nano::stat::type::traffic_tcp, nano::stat::detail::all, nano::stat::dir::in, size_a);
124
- this_l->set_last_completion ();
125
- this_l->set_last_receive_time ();
126
- }
127
- cbk (ec, size_a);
128
- }));
110
+ boost::asio::post (strand, [this_l = shared_from_this (), buffer_a, callback = std::move (callback_a), size_a] () {
111
+ this_l->requests .emplace_back (buffer_a, size_a, callback);
112
+ this_l->service_requests_maybe ();
129
113
});
130
114
}
131
115
}
@@ -389,6 +373,15 @@ void nano::transport::socket::close_internal ()
389
373
tcp_socket.shutdown (boost::asio::ip::tcp::socket::shutdown_both, ec);
390
374
tcp_socket.close (ec);
391
375
376
+ // FIXME Encapsulate or simplify this
377
+ for (auto const & request : requests)
378
+ {
379
+ node_l->stats .inc (nano::stat::type::tcp, nano::stat::detail::tcp_read_error, nano::stat::dir::in);
380
+ auto const & [buffer, size, callback] = request;
381
+ callback (boost::asio::error::operation_aborted, 0 );
382
+ }
383
+ requests.clear ();
384
+
392
385
if (ec)
393
386
{
394
387
node_l->stats .inc (nano::stat::type::socket, nano::stat::detail::error_socket_close);
@@ -408,6 +401,87 @@ nano::tcp_endpoint nano::transport::socket::local_endpoint () const
408
401
return local;
409
402
}
410
403
404
+ void nano::transport::socket::begin_read_loop ()
405
+ {
406
+ boost::asio::co_spawn (
407
+ strand, [this_l = shared_from_this ()] () -> asio::awaitable<void > {
408
+ co_await this_l->run ();
409
+ },
410
+ // FIXME This should probably clean up in a structured way by getting a future for this loop and wait for it similar to a thread::join()
411
+ asio::detached);
412
+ }
413
+
414
+ boost::asio::awaitable<void > nano::transport::socket::run ()
415
+ {
416
+ debug_assert (strand.running_in_this_thread ());
417
+
418
+ try
419
+ {
420
+ while (!closed)
421
+ {
422
+ // Wait until there is data available to read in the socket
423
+ co_await tcp_socket.async_wait (boost::asio::ip::tcp::socket::wait_read, boost::asio::use_awaitable);
424
+ if (read_buffer.capacity () == read_buffer.size ())
425
+ {
426
+ // Wait until there is writable space
427
+ co_await buffer_condition.async_wait (boost::asio::use_awaitable);
428
+ }
429
+ auto buffer = boost::asio::buffer (os_buffer.data (), read_buffer.capacity () - read_buffer.size ());
430
+ size_t amount_read = co_await tcp_socket.async_read_some (buffer, boost::asio::use_awaitable);
431
+
432
+ // FIXME This is the undesired copy
433
+ std::transform (os_buffer.begin (), os_buffer.begin () + amount_read, std::back_inserter (read_buffer), [] (uint8_t val) { return val; });
434
+
435
+ service_requests_maybe ();
436
+ }
437
+ }
438
+ catch (boost::system::system_error const & e)
439
+ {
440
+ close ();
441
+ }
442
+ }
443
+
444
+ void nano::transport::socket::service_requests_maybe ()
445
+ {
446
+ debug_assert (strand.running_in_this_thread ());
447
+
448
+ while (!requests.empty ())
449
+ {
450
+ auto front = requests.front ();
451
+ auto const & [buffer, size, callback] = front;
452
+ auto available = read_buffer.size ();
453
+ if (available < size)
454
+ {
455
+ // Once read requests can't be serviced with enough readable data, we're done
456
+ return ;
457
+ }
458
+ std::copy (read_buffer.begin (), read_buffer.begin () + size, buffer->begin ());
459
+ if (read_buffer.capacity () == read_buffer.size ())
460
+ {
461
+ buffer_condition.cancel ();
462
+ }
463
+
464
+ // FIXME having valid iterators will be needed when merging read_buffer and buffer'
465
+ read_buffer.erase (read_buffer.begin (), read_buffer.begin () + size);
466
+
467
+ // FIXME Execute callback outside this socket's strand if possible
468
+ boost::asio::post (strand, [this_l = shared_from_this (), front] () {
469
+ auto const & [buffer, size, callback] = front;
470
+ auto node_l = this_l->node_w .lock ();
471
+ if (!node_l)
472
+ {
473
+ return ;
474
+ }
475
+
476
+ node_l->stats .add (nano::stat::type::traffic_tcp, nano::stat::detail::all, nano::stat::dir::in, size);
477
+ this_l->set_last_completion ();
478
+ this_l->set_last_receive_time ();
479
+ callback (boost::system::error_code{}, size);
480
+ });
481
+ requests.pop_front ();
482
+ }
483
+ }
484
+
411
485
void nano::transport::socket::operator () (nano::object_stream & obs) const
412
486
{
413
487
obs.write (" remote_endpoint" , remote_endpoint ());
0 commit comments