diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 00000000000..9ce1f1ad19c --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,44 @@ + + + + \ No newline at end of file diff --git a/README.md b/README.md index ff5e9f565d6..413c3480191 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,8 @@ Recommended hardware configuration for SeaStar * CPUs - As much as you need. SeaStar is highly friendly for multi-core and NUMA * NICs - As fast as possible, we recommend 10G or 40G cards. It's possible to use - 1G to but you may be limited by their capacity. - In addition, the more hardware queue per cpu the better for SeaStar. + 1G too but you may be limited by their capacity. + In addition, the more hardware queue per cpu the better for SeaStar. Otherwise we have to emulate that in software. * Disks - Fast SSDs with high number of IOPS. * Client machines - Usually a single client machine can't load our servers. diff --git a/apps/fair_queue_tester/fair_queue_tester.cc b/apps/fair_queue_tester/fair_queue_tester.cc index 9b75d2d9674..4ddb0705b0f 100644 --- a/apps/fair_queue_tester/fair_queue_tester.cc +++ b/apps/fair_queue_tester/fair_queue_tester.cc @@ -33,6 +33,7 @@ #include #include +using namespace seastar; using namespace std::chrono_literals; static auto random_seed = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); diff --git a/apps/httpd/main.cc b/apps/httpd/main.cc index f112a6ed5c3..15fabed887d 100644 --- a/apps/httpd/main.cc +++ b/apps/httpd/main.cc @@ -19,8 +19,11 @@ * Copyright 2015 Cloudius Systems */ +#include +#include #include "http/httpd.hh" #include "http/handlers.hh" +#include "http/websocket_handler.hh" #include "http/function_handlers.hh" #include "http/file_handler.hh" #include "apps/httpd/demo.json.hh" @@ -28,7 +31,9 @@ namespace bpo = boost::program_options; +using namespace seastar; using namespace httpd; +using namespace websocket; class handl : public httpd::handler_base { public: @@ -47,10 +52,59 @@ void set_routes(routes& r) { function_handler* h2 = new function_handler([](std::unique_ptr req) { return make_ready_future("json-future"); }); + + auto ws_echo_handler = new websocket::ws_handler(); + + ws_echo_handler->on_message_future([] (const std::unique_ptr& req, duplex_stream& stream, + message message) { + return stream.write(std::move(message)).then([&stream] { + return stream.flush(); + }); + }); + + auto ws_sha1_handler = new websocket::ws_handler(); + + ws_sha1_handler->on_connection_future([] (const std::unique_ptr& req, duplex_stream& stream) { + return stream.write(websocket::message(TEXT, "Hello from seastar ! Send any message to this endpoint" + " and get back it's payload SHA1 in base64 format !")).then([&stream] { + return stream.flush(); + }); + }); + + ws_sha1_handler->on_message_future([] (const std::unique_ptr& req, duplex_stream& stream, + message message) { + CryptoPP::SHA hash; + byte digest[CryptoPP::SHA::DIGESTSIZE]; + hash.Update((byte*) message.payload.begin(), message.payload.size()); + hash.Final(digest); + + sstring base64; + + CryptoPP::Base64Encoder encoder; + encoder.Put(digest, sizeof(digest)); + encoder.MessageEnd(); + CryptoPP::word64 size = encoder.MaxRetrievable(); + if (size) { + base64.resize(size); + encoder.Get((byte*) base64.data(), base64.size()); + + return stream.write(websocket::message(TEXT, base64.substr(0, base64.size() - 1))).then([&stream] { + return stream.flush(); + }); + } + return make_ready_future(); + }); + + ws_sha1_handler->on_disconnection([] (const std::unique_ptr& req) { + print("websocket client disconnected\n"); + }); + r.add(operation_type::GET, url("/"), h1); r.add(operation_type::GET, url("/jf"), h2); - r.add(operation_type::GET, url("/file").remainder("path"), - new directory_handler("/")); + r.add(operation_type::GET, url("/file").remainder("path"), new directory_handler("/")); + r.put("/", ws_echo_handler); + r.put("/sha1", ws_sha1_handler); + demo_json::hello_world.set(r, [] (const_req req) { demo_json::my_object obj; obj.var1 = req.param.at("var1"); @@ -65,18 +119,18 @@ void set_routes(routes& r) { int main(int ac, char** av) { app_template app; app.add_options()("port", bpo::value()->default_value(10000), - "HTTP Server port"); + "HTTP Server port"); return app.run_deprecated(ac, av, [&] { - auto&& config = app.configuration(); + auto &&config = app.configuration(); uint16_t port = config["port"].as(); auto server = new http_server_control(); auto rb = make_shared("apps/httpd/"); server->start().then([server] { return server->set_routes(set_routes); - }).then([server, rb]{ - return server->set_routes([rb](routes& r){rb->set_api_doc(r);}); - }).then([server, rb]{ - return server->set_routes([rb](routes& r) {rb->register_function(r, "demo", "hello world application");}); + }).then([server, rb] { + return server->set_routes([rb](routes &r) { rb->set_api_doc(r); }); + }).then([server, rb] { + return server->set_routes([rb](routes &r) { rb->register_function(r, "demo", "hello world application"); }); }).then([server, port] { return server->listen(port); }).then([server, port] { @@ -85,6 +139,5 @@ int main(int ac, char** av) { return server->stop(); }); }); - }); } diff --git a/apps/iotune/iotune.cc b/apps/iotune/iotune.cc index 2c42d1bf5ee..304328ebc2d 100644 --- a/apps/iotune/iotune.cc +++ b/apps/iotune/iotune.cc @@ -44,9 +44,13 @@ #include "core/aligned_buffer.hh" #include "util/defer.hh" +using namespace seastar; using namespace std::chrono_literals; +namespace seastar { bool filesystem_has_good_aio_support(sstring directory, bool verbose); +} + class iotune_manager; class iotune_timeout_exception : public std::exception { diff --git a/apps/memcached/ascii.rl b/apps/memcached/ascii.rl index f6f577c4126..fe5ad0a9b6c 100644 --- a/apps/memcached/ascii.rl +++ b/apps/memcached/ascii.rl @@ -25,6 +25,8 @@ #include #include +using namespace seastar; + %%{ machine memcache_ascii_protocol; diff --git a/apps/memcached/memcache.cc b/apps/memcached/memcache.cc index ef53ff0a38c..9a03677e3cc 100644 --- a/apps/memcached/memcache.cc +++ b/apps/memcached/memcache.cc @@ -48,12 +48,13 @@ #define VERSION "v1.0" #define VERSION_STRING PLATFORM " " VERSION +using namespace seastar; using namespace net; -namespace bi = boost::intrusive; - namespace memcache { +namespace bi = boost::intrusive; + static constexpr double default_slab_growth_factor = 1.25; static constexpr uint64_t default_slab_page_size = 1UL*MB; static constexpr uint64_t default_per_cpu_slab_size = 0UL; // zero means reclaimer is enabled. diff --git a/apps/memcached/memcached.hh b/apps/memcached/memcached.hh index 54fa217fccd..10a355328fd 100644 --- a/apps/memcached/memcached.hh +++ b/apps/memcached/memcached.hh @@ -22,6 +22,8 @@ namespace memcache { +using namespace seastar; + class item; class cache; diff --git a/apps/seawreck/seawreck.cc b/apps/seawreck/seawreck.cc index fc9fbd72e3b..af87018601b 100644 --- a/apps/seawreck/seawreck.cc +++ b/apps/seawreck/seawreck.cc @@ -20,6 +20,7 @@ */ #include "http/http_response_parser.hh" +#include "http/websocket.hh" #include "core/print.hh" #include "core/reactor.hh" #include "core/app-template.hh" @@ -28,6 +29,9 @@ #include "core/semaphore.hh" #include "core/future-util.hh" #include +#include + +using namespace seastar; template void http_debug(const char* fmt, Args&&... args) { @@ -37,6 +41,7 @@ void http_debug(const char* fmt, Args&&... args) { } class http_client { + private: unsigned _duration; unsigned _conn_per_core; @@ -48,13 +53,21 @@ class http_client { bool _timer_based; bool _timer_done{false}; uint64_t _total_reqs{0}; + bool _websocket{false}; + long _max_latency{0}; + long _min_latency{INT_MAX}; + double _sum_avr_latency{0}; + unsigned _payload_size; + public: - http_client(unsigned duration, unsigned total_conn, unsigned reqs_per_conn) + http_client(unsigned duration, unsigned total_conn, unsigned reqs_per_conn, bool websocket, unsigned payload_size) : _duration(duration) , _conn_per_core(total_conn / smp::count) , _reqs_per_conn(reqs_per_conn) , _run_timer([this] { _timer_done = true; }) - , _timer_based(reqs_per_conn == 0) { + , _timer_based(reqs_per_conn == 0) + , _websocket(websocket) + , _payload_size(payload_size) { } class connection { @@ -65,6 +78,10 @@ class http_client { http_response_parser _parser; http_client* _http_client; uint64_t _nr_done{0}; + long _max_latency{0}; + long _min_latency{INT_MAX}; + long _sum_latency{0}; + public: connection(connected_socket&& fd, http_client* client) : _fd(std::move(fd)) @@ -77,12 +94,25 @@ class http_client { return _nr_done; } + long max_latency() { + return _max_latency; + } + + long min_latency() { + return _min_latency; + } + + double avr_latency() { + return (double)_sum_latency / (double)_nr_done; + } + future<> do_req() { + auto start = std::chrono::steady_clock::now(); return _write_buf.write("GET / HTTP/1.1\r\nHost: 127.0.0.1:10000\r\n\r\n").then([this] { return _write_buf.flush(); - }).then([this] { + }).then([this, start] { _parser.init(); - return _read_buf.consume(_parser).then([this] { + return _read_buf.consume(_parser).then([this, start] { // Read HTTP response header first if (_parser.eof()) { return make_ready_future<>(); @@ -96,8 +126,14 @@ class http_client { auto content_len = std::stoi(it->second); http_debug("Content-Length = %d\n", content_len); // Read HTTP response body - return _read_buf.read_exactly(content_len).then([this] (temporary_buffer buf) { + return _read_buf.read_exactly(content_len).then([this, start] (temporary_buffer buf) { _nr_done++; + auto ping = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); + _sum_latency += ping; + if (ping > _max_latency) + _max_latency = ping; + if (ping < _min_latency) + _min_latency = ping; http_debug("%s\n", buf.get()); if (_http_client->done(_nr_done)) { return make_ready_future(); @@ -110,11 +146,94 @@ class http_client { } }; + class ws_connection { + private: + connected_socket _fd; + input_stream _read_buf; + output_stream _write_buf; + http_client* _http_client; + uint64_t _nr_done{0}; + long _max_latency{0}; + long _min_latency{INT_MAX}; + long _sum_latency{0}; + httpd::websocket::message _message; + temporary_buffer _header; + long _read_value; + public: + ws_connection(connected_socket&& fd, http_client* client) + : _fd(std::move(fd)) + , _read_buf(_fd.input()) + , _write_buf(_fd.output()) + , _http_client(client) { + using random_bytes_engine = std::independent_bits_engine< + std::default_random_engine, std::numeric_limits::digits, unsigned char>; + random_bytes_engine rbe; + + sstring payload(client->_payload_size, '\0'); + + std::generate(payload.begin(), payload.end(), std::ref(rbe)); + _message = httpd::websocket::message(httpd::websocket::opcode::BINARY, payload); + _header = _message.get_header(); + _read_value = _header.size() - 4 + _message.payload.size(); + } + + uint64_t nr_done() { + return _nr_done; + } + + long max_latency() { + return _max_latency; + } + + long min_latency() { + return _min_latency; + } + + double avr_latency() { + return (double)_sum_latency / (double)_nr_done; + } + + future<> do_req() { + + auto start = std::chrono::steady_clock::now(); + return _write_buf.write(temporary_buffer(_header.begin(), _header.size())).then([this] { + _write_buf.write(temporary_buffer(_message.payload.begin(), _message.payload.size())); + }).then([this] { return _write_buf.flush(); }).then([this, start] { + return _read_buf.skip(_read_value).then([this, start] { + auto ping = std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); + _sum_latency += ping; + if (ping > _max_latency) + _max_latency = ping; + if (ping < _min_latency) + _min_latency = ping; + _nr_done++; + if (_http_client->done(_nr_done)) { + return make_ready_future(); + } else { + return do_req(); + } + }); + }); + } + }; + future total_reqs() { print("Requests on cpu %2d: %ld\n", engine().cpu_id(), _total_reqs); return make_ready_future(_total_reqs); } + long max_latency() { + return _max_latency; + } + + long min_latency() { + return _min_latency; + } + + double avr_latency() { + return _sum_avr_latency / (double)_conn_per_core; + } + bool done(uint64_t nr_done) { if (_timer_based) { return _timer_done; @@ -125,12 +244,25 @@ class http_client { future<> connect(ipv4_addr server_addr) { // Establish all the TCP connections first - for (unsigned i = 0; i < _conn_per_core; i++) { - engine().net().connect(make_ipv4_address(server_addr)).then([this] (connected_socket fd) { - _sockets.push_back(std::move(fd)); - http_debug("Established connection %6d on cpu %3d\n", _conn_connected.current(), engine().cpu_id()); - _conn_connected.signal(); - }).or_terminate(); + if (_websocket) { + for (unsigned i = 0; i < _conn_per_core; i++) { + httpd::websocket::connect(make_ipv4_address(server_addr)).then( + [this](connected_socket fd) { + _sockets.push_back(std::move(fd)); + http_debug("Established connection %6d on cpu %3d\n", _conn_connected.current(), + engine().cpu_id()); + _conn_connected.signal(); + }).or_terminate(); + } + } + else { + for (unsigned i = 0; i < _conn_per_core; i++) { + engine().net().connect(make_ipv4_address(server_addr)).then([this] (connected_socket fd) { + _sockets.push_back(std::move(fd)); + http_debug("Established connection %6d on cpu %3d\n", _conn_connected.current(), engine().cpu_id()); + _conn_connected.signal(); + }).or_terminate(); + } } return _conn_connected.wait(_conn_per_core); } @@ -141,19 +273,48 @@ class http_client { if (_timer_based) { _run_timer.arm(std::chrono::seconds(_duration)); } - for (auto&& fd : _sockets) { - auto conn = new connection(std::move(fd), this); - conn->do_req().then_wrapped([this, conn] (auto&& f) { - http_debug("Finished connection %6d on cpu %3d\n", _conn_finished.current(), engine().cpu_id()); - _total_reqs += conn->nr_done(); - _conn_finished.signal(); - delete conn; - try { - f.get(); - } catch (std::exception& ex) { - print("http request error: %s\n", ex.what()); - } - }); + + if (_websocket) { + for (auto&& fd : _sockets) { + auto conn = new ws_connection(std::move(fd), this); + conn->do_req().then_wrapped([this, conn] (auto&& f) { + http_debug("Finished connection %6d on cpu %3d\n", _conn_finished.current(), engine().cpu_id()); + _total_reqs += conn->nr_done(); + _sum_avr_latency += conn->avr_latency(); + if (conn->max_latency() > _max_latency) + _max_latency = conn->max_latency(); + if (conn->min_latency() < _min_latency) + _min_latency = conn->min_latency(); + _conn_finished.signal(); + delete conn; + try { + f.get(); + } catch (std::exception& ex) { + print("websocket error: %s\n", ex.what()); + } + }); + } + } + else { + for (auto&& fd : _sockets) { + auto conn = new connection(std::move(fd), this); + conn->do_req().then_wrapped([this, conn] (auto&& f) { + http_debug("Finished connection %6d on cpu %3d\n", _conn_finished.current(), engine().cpu_id()); + _total_reqs += conn->nr_done(); + _sum_avr_latency += conn->avr_latency(); + if (conn->max_latency() > _max_latency) + _max_latency = conn->max_latency(); + if (conn->min_latency() < _min_latency) + _min_latency = conn->min_latency(); + _conn_finished.signal(); + delete conn; + try { + f.get(); + } catch (std::exception& ex) { + print("http request error: %s\n", ex.what()); + } + }); + } } // All finished @@ -164,6 +325,42 @@ class http_client { } }; +// Implements @Reducer concept. +template +class max { +private: + Result _result; +public: + future<> operator()(const Addend& value) { + if (value > _result) + _result += value; + return make_ready_future<>(); + } + Result get() && { + return std::move(_result); + } +}; + +// Implements @Reducer concept. +template +class min { +private: + Result _result; + bool init{false}; +public: + future<> operator()(const Addend& value) { + if (value < _result || !init) + { + _result += value; + init = true; + } + return make_ready_future<>(); + } + Result get() && { + return std::move(_result); + } +}; + namespace bpo = boost::program_options; int main(int ac, char** av) { @@ -172,7 +369,9 @@ int main(int ac, char** av) { ("server,s", bpo::value()->default_value("192.168.66.100:10000"), "Server address") ("conn,c", bpo::value()->default_value(100), "total connections") ("reqs,r", bpo::value()->default_value(0), "reqs per connection") - ("duration,d", bpo::value()->default_value(10), "duration of the test in seconds)"); + ("duration,d", bpo::value()->default_value(10), "duration of the test in seconds)") + ("websocket,w", bpo::bool_switch()->default_value(false), "benchmark websocket") + ("size,z", bpo::value()->default_value(20), "websocket request payload size"); return app.run(ac, av, [&app] () -> future { auto& config = app.configuration(); @@ -180,6 +379,8 @@ int main(int ac, char** av) { auto reqs_per_conn = config["reqs"].as(); auto total_conn= config["conn"].as(); auto duration = config["duration"].as(); + auto websocket = config["websocket"].as(); + auto payload_size = config["size"].as(); if (total_conn % smp::count != 0) { print("Error: conn needs to be n * cpu_nr\n"); @@ -191,33 +392,44 @@ int main(int ac, char** av) { // Start http requests on all the cores auto started = steady_clock_type::now(); print("========== http_client ============\n"); + print("Benchmark: %s\n", websocket ? "websocket" : "http"); print("Server: %s\n", server); print("Connections: %u\n", total_conn); print("Requests/connection: %s\n", reqs_per_conn == 0 ? "dynamic (timer based)" : std::to_string(reqs_per_conn)); - return http_clients->start(std::move(duration), std::move(total_conn), std::move(reqs_per_conn)).then([http_clients, started, server] { + return http_clients->start(std::move(duration), std::move(total_conn), std::move(reqs_per_conn), std::move + (websocket), std::move(payload_size)).then([http_clients, started, server] { return http_clients->invoke_on_all(&http_client::connect, ipv4_addr{server}); }).then([http_clients] { return http_clients->invoke_on_all(&http_client::run); }).then([http_clients] { return http_clients->map_reduce(adder(), &http_client::total_reqs); }).then([http_clients, started] (auto total_reqs) { - // All the http requests are finished - auto finished = steady_clock_type::now(); - auto elapsed = finished - started; - auto secs = static_cast(elapsed.count() / 1000000000.0); - print("Total cpus: %u\n", smp::count); - print("Total requests: %u\n", total_reqs); - print("Total time: %f\n", secs); - print("Requests/sec: %f\n", static_cast(total_reqs) / secs); - print("========== done ============\n"); - return http_clients->stop().then([http_clients] { - // FIXME: If we call engine().exit(0) here to exit when - // requests are done. The tcp connection will not be closed - // properly, becasue we exit too earily and the FIN packets are - // not exchanged. + // All the http requests are finished + auto finished = steady_clock_type::now(); + auto elapsed = finished - started; + auto secs = elapsed.count() / 1000000000.0; + print("Total cpus: %u\n", smp::count); + print("Total requests: %u\n", total_reqs); + print("Total time: %f\n", secs); + print("Requests/sec: %f\n", static_cast(total_reqs) / secs); + }).then([http_clients] { + return when_all(http_clients->map_reduce(max(), &http_client::max_latency), + http_clients->map_reduce(min(), &http_client::min_latency), + http_clients->map_reduce(adder(), &http_client::avr_latency)); + }).then([http_clients] (std::tuple, future, future> joined) { + print("Max latency : %u ms\n", std::get<0>(joined).get0()); + print("Min latency : %u ms\n", std::get<1>(joined).get0()); + print("Avr latency : %f ms\n", std::get<2>(joined).get0() / (double)smp::count); + }).then([http_clients] { + print("========== done ============\n"); + return http_clients->stop().then([http_clients] { + // FIXME: If we call engine().exit(0) here to exit when + // requests are done. The tcp connection will not be closed + // properly, becasue we exit too earily and the FIN packets are + // not exchanged. delete http_clients; return make_ready_future(0); - }); + }); }); }); } diff --git a/configure.py b/configure.py index a4808f6744e..de8f34e998f 100755 --- a/configure.py +++ b/configure.py @@ -213,7 +213,6 @@ def sanitize_vptr_flag(compiler): 'tests/connect_test', 'tests/chunked_fifo_test', 'tests/circular_buffer_test', - 'tests/scollectd_test', 'tests/perf/perf_fstream', 'tests/json_formatter_test', 'tests/dns_test', @@ -329,6 +328,8 @@ def sanitize_vptr_flag(compiler): 'http/reply.cc', 'http/request_parser.rl', 'http/api_docs.cc', + 'http/websocket.cc', + 'http/websocket_message.cc' ] boost_test_lib = [ @@ -426,7 +427,7 @@ def have_xen(): 'tests/tcp_sctp_client': ['tests/tcp_sctp_client.cc'] + core + libnet, 'tests/tls_test': ['tests/tls_test.cc'] + core + libnet, 'tests/fair_queue_test': ['tests/fair_queue_test.cc'] + core, - 'apps/seawreck/seawreck': ['apps/seawreck/seawreck.cc', 'http/http_response_parser.rl'] + core + libnet, + 'apps/seawreck/seawreck': ['apps/seawreck/seawreck.cc', 'http/http_response_parser.rl'] + core + libnet + http, 'apps/fair_queue_tester/fair_queue_tester': ['apps/fair_queue_tester/fair_queue_tester.cc'] + core, 'apps/iotune/iotune': ['apps/iotune/iotune.cc'] + ['core/resource.cc', 'core/fsqual.cc'], 'tests/blkdiscard_test': ['tests/blkdiscard_test.cc'] + core, @@ -449,7 +450,6 @@ def have_xen(): 'tests/connect_test': ['tests/connect_test.cc'] + core + libnet, 'tests/chunked_fifo_test': ['tests/chunked_fifo_test.cc'] + core, 'tests/circular_buffer_test': ['tests/circular_buffer_test.cc'] + core, - 'tests/scollectd_test': ['tests/scollectd_test.cc'] + core, 'tests/perf/perf_fstream': ['tests/perf/perf_fstream.cc'] + core, 'tests/json_formatter_test': ['tests/json_formatter_test.cc'] + core + http, 'tests/dns_test': ['tests/dns_test.cc'] + core + libnet, @@ -472,7 +472,6 @@ def have_xen(): 'tests/fstream_test', 'tests/rpc_test', 'tests/connect_test', - 'tests/scollectd_test', 'tests/json_formatter_test', 'tests/dns_test', 'tests/execution_stage_test', @@ -570,7 +569,7 @@ def update(lines, vars): if args.with_osv: libs += '-lintel_dpdk -lrt -lm -ldl' else: - libs += '-Wl,--whole-archive -lrte_pmd_vmxnet3_uio -lrte_pmd_i40e -lrte_pmd_ixgbe -lrte_pmd_e1000 -lrte_pmd_ring -lrte_hash -lrte_kvargs -lrte_mbuf -lrte_ethdev -lrte_eal -lrte_mempool -lrte_ring -lrte_cmdline -lrte_cfgfile -Wl,--no-whole-archive -lrt -lm -ldl' + libs += '-Wl,--whole-archive -lrte_pmd_vmxnet3_uio -lrte_pmd_i40e -lrte_pmd_ixgbe -lrte_pmd_e1000 -lrte_pmd_ring -lrte_pmd_bnxt -lrte_pmd_cxgbe -lrte_pmd_ena -lrte_pmd_enic -lrte_pmd_fm10k -lrte_pmd_nfp -lrte_pmd_qede -lrte_pmd_sfc_efx -lrte_hash -lrte_kvargs -lrte_mbuf -lrte_ethdev -lrte_eal -lrte_mempool -lrte_ring -lrte_cmdline -lrte_cfgfile -Wl,--no-whole-archive -lrt -lm -ldl' args.user_cflags += ' -Ifmt' @@ -840,7 +839,7 @@ def have_hwloc(): f.write('build {}: ragel {}\n'.format(hh, src)) for hh in swaggers: src = swaggers[hh] - f.write('build {}: swagger {}\n'.format(hh,src)) + f.write('build {}: swagger {} | json/json2code.py\n'.format(hh,src)) for pb in protobufs: src = protobufs[pb] c_pb = pb.replace('.h','.cc') diff --git a/core/align.hh b/core/align.hh index e53c5dd91e4..d24a231c10f 100644 --- a/core/align.hh +++ b/core/align.hh @@ -25,6 +25,8 @@ #include #include +namespace seastar { + template inline constexpr T align_up(T v, T align) { @@ -51,4 +53,12 @@ T* align_down(T* v, size_t align) { return reinterpret_cast(align_down(reinterpret_cast(v), align)); } + +template +inline bool is_aligned(const T* v) { + return (uintptr_t)(const void *)(v) % sizeof(T) == 0; +} + +} + #endif /* ALIGN_HH_ */ diff --git a/core/aligned_buffer.hh b/core/aligned_buffer.hh index 1d8245a7a9c..ad6e0db02b1 100644 --- a/core/aligned_buffer.hh +++ b/core/aligned_buffer.hh @@ -24,6 +24,9 @@ #include #include "print.hh" +namespace seastar { + + struct free_deleter { void operator()(void* p) { ::free(p); } }; @@ -45,3 +48,4 @@ std::unique_ptr allocate_aligned_buffer(size_t size, s } +} diff --git a/core/app-template.cc b/core/app-template.cc index 69430fe82da..29d73b8b774 100644 --- a/core/app-template.cc +++ b/core/app-template.cc @@ -30,6 +30,8 @@ #include #include +namespace seastar { + namespace bpo = boost::program_options; app_template::app_template(app_template::config cfg) @@ -141,3 +143,5 @@ app_template::run_deprecated(int ac, char ** av, std::function&& func) smp::cleanup(); return exit_code; } + +} diff --git a/core/app-template.hh b/core/app-template.hh index 9e85197d00a..be5b0718833 100644 --- a/core/app-template.hh +++ b/core/app-template.hh @@ -27,6 +27,8 @@ #include #include +namespace seastar { + class app_template { public: struct config { @@ -63,4 +65,6 @@ public: int run(int ac, char ** av, std::function ()>&& func); }; +} + #endif diff --git a/core/apply.hh b/core/apply.hh index c015274fde7..437166ad5bd 100644 --- a/core/apply.hh +++ b/core/apply.hh @@ -25,6 +25,8 @@ #include #include +namespace seastar { + template struct apply_helper; @@ -56,4 +58,6 @@ auto apply(Func&& func, const std::tuple& args) { return helper::apply(std::forward(func), args); } +} + #endif /* APPLY_HH_ */ diff --git a/core/array_map.hh b/core/array_map.hh index 9e0a1f60d18..e9d46319cd8 100644 --- a/core/array_map.hh +++ b/core/array_map.hh @@ -24,6 +24,8 @@ #include +namespace seastar { + // unordered_map implemented as a simple array template @@ -46,6 +48,6 @@ public: } }; - +} #endif /* ARRAY_MAP_HH_ */ diff --git a/core/bitops.hh b/core/bitops.hh index c33768f5602..22458dba6a5 100644 --- a/core/bitops.hh +++ b/core/bitops.hh @@ -24,6 +24,8 @@ #include +namespace seastar { + inline constexpr unsigned count_leading_zeros(unsigned x) { return __builtin_clz(x); @@ -66,4 +68,6 @@ inline constexpr unsigned log2floor(T n) { return std::numeric_limits::digits - count_leading_zeros(n) - 1; } +} + #endif /* BITOPS_HH_ */ diff --git a/core/bitset-iter.hh b/core/bitset-iter.hh index a79a097f512..cec275fbc72 100644 --- a/core/bitset-iter.hh +++ b/core/bitset-iter.hh @@ -17,6 +17,8 @@ #include #include +namespace seastar { + namespace bitsets { static constexpr int ulong_bits = std::numeric_limits::digits; @@ -172,6 +174,7 @@ static inline set_range for_each_set(std::bitset bitset, int offset = 0) return set_range(bitset, offset); } +} } diff --git a/core/byteorder.hh b/core/byteorder.hh index beabdcfe17d..a3ac3f26499 100644 --- a/core/byteorder.hh +++ b/core/byteorder.hh @@ -25,6 +25,8 @@ #include #include "unaligned.hh" +namespace seastar { + inline uint8_t cpu_to_le(uint8_t x) { return x; } inline uint8_t le_to_cpu(uint8_t x) { return x; } inline uint16_t cpu_to_le(uint16_t x) { return htole16(x); } @@ -121,3 +123,5 @@ produce_be(char*& p, T datum) { write_be(p, datum); p += sizeof(T); } + +} diff --git a/core/chunked_fifo.hh b/core/chunked_fifo.hh index a60b796c4b9..217c43ddd0a 100644 --- a/core/chunked_fifo.hh +++ b/core/chunked_fifo.hh @@ -21,6 +21,11 @@ #pragma once +#include +#include + +namespace seastar { + // An unbounded FIFO queue of objects of type T. // // It provides operations to push items in one end of the queue, and pop them @@ -75,9 +80,6 @@ // uses move/copy constructors instead of move/copy assignments, which are // less efficient. -#include -#include - template class chunked_fifo { static_assert((items_per_chunk & (items_per_chunk - 1)) == 0, @@ -462,3 +464,5 @@ void chunked_fifo::reserve(size_t n) { ++_nfree_chunks; } } + +} diff --git a/core/circular_buffer.hh b/core/circular_buffer.hh index 838dfe379a8..316792ad110 100644 --- a/core/circular_buffer.hh +++ b/core/circular_buffer.hh @@ -37,6 +37,8 @@ #include #include +namespace seastar { + template > class circular_buffer { struct impl : Alloc { @@ -440,4 +442,6 @@ circular_buffer::erase(iterator first, iterator last) { } } +} + #endif /* CIRCULAR_BUFFER_HH_ */ diff --git a/core/condition-variable.hh b/core/condition-variable.hh index 33eef0e8431..9f948d913dc 100644 --- a/core/condition-variable.hh +++ b/core/condition-variable.hh @@ -24,6 +24,8 @@ #include "core/future-util.hh" #include "core/semaphore.hh" +namespace seastar { + /// \addtogroup fiber-module /// @{ @@ -174,3 +176,5 @@ public: }; /// @} + +} diff --git a/core/deleter.hh b/core/deleter.hh index 7749f624b34..ea3b0ef7e73 100644 --- a/core/deleter.hh +++ b/core/deleter.hh @@ -27,6 +27,8 @@ #include #include +namespace seastar { + /// \addtogroup memory-module /// @{ @@ -272,4 +274,6 @@ make_object_deleter(deleter d, T&& obj) { /// @} +} + #endif /* DELETER_HH_ */ diff --git a/core/distributed.hh b/core/distributed.hh index d95a6eea39f..0c4b62f7928 100644 --- a/core/distributed.hh +++ b/core/distributed.hh @@ -23,5 +23,10 @@ #include "sharded.hh" +namespace seastar { + + template -using distributed = seastar::sharded; +using distributed = sharded; + +} diff --git a/core/do_with.hh b/core/do_with.hh index 630bf8d2a3a..f991be69611 100644 --- a/core/do_with.hh +++ b/core/do_with.hh @@ -26,6 +26,8 @@ #include #include +namespace seastar { + /// \addtogroup future-util /// @{ @@ -112,3 +114,5 @@ do_with(T1&& rv1, T2&& rv2, T3_or_F&& rv3, More&&... more) { } /// @} + +} diff --git a/core/dpdk_rte.cc b/core/dpdk_rte.cc index 46cc08897cd..5e358fcddf1 100644 --- a/core/dpdk_rte.cc +++ b/core/dpdk_rte.cc @@ -24,6 +24,8 @@ #include #include +namespace seastar { + namespace dpdk { bool eal::initialized = false; @@ -111,4 +113,6 @@ size_t eal::mem_size(int num_cpus, bool hugetlbfs_membackend) } // namespace dpdk +} + #endif // HAVE_DPDK diff --git a/core/dpdk_rte.hh b/core/dpdk_rte.hh index 215c1481add..52cec58a19a 100644 --- a/core/dpdk_rte.hh +++ b/core/dpdk_rte.hh @@ -38,6 +38,8 @@ #endif /******************************************************************************/ +namespace seastar { + namespace dpdk { // DPDK Environment Abstraction Layer @@ -57,5 +59,8 @@ public: }; } // namespace dpdk + +} + #endif // HAVE_DPDK #endif // DPDK_RTE_HH_ diff --git a/core/enum.hh b/core/enum.hh index be6c4ff9843..1ea3423224b 100644 --- a/core/enum.hh +++ b/core/enum.hh @@ -31,6 +31,8 @@ #include #include +namespace seastar { + template class enum_hash { static_assert(std::is_enum::value, "must be an enum"); @@ -40,3 +42,5 @@ public: return std::hash()(static_cast(e)); } }; + +} diff --git a/core/execution_stage.hh b/core/execution_stage.hh index d5dcf5cc460..39b6d31f180 100644 --- a/core/execution_stage.hh +++ b/core/execution_stage.hh @@ -28,6 +28,7 @@ #include "metrics.hh" #include "util/reference_wrapper.hh" #include "util/gcc6-concepts.hh" +#include "../util/defer.hh" namespace seastar { @@ -308,7 +309,7 @@ public: /// Usage example: /// ``` /// void do_something(int&, int, std::vector&&); - /// thread_local auto stage = seastar::make_execution_stage(do_something); + /// thread_local auto stage = seastar::make_execution_stage("execution-stage", do_something); /// /// int global_value; /// @@ -346,14 +347,14 @@ public: /// Usage example: /// ``` /// double do_something(int); -/// thread_local auto stage1 = seastar::make_execution_stage(do_something); +/// thread_local auto stage1 = seastar::make_execution_stage("execution-stage1", do_something); /// /// future func1(int val) { /// return stage1(val); /// } /// /// future do_some_io(int); -/// thread_local auto stage2 = seastar::make_execution_stage(do_some_io); +/// thread_local auto stage2 = seastar::make_execution_stage("execution-stage2", do_some_io); /// /// future func2(int val) { /// return stage2(val); @@ -382,14 +383,14 @@ auto make_execution_stage(const sstring& name, Function&& fn) { /// void do_something(int); /// }; /// -/// thread_local auto stage = seastar::make_execution_stage(&foo::do_something); +/// thread_local auto stage = seastar::make_execution_stage("execution-stage", &foo::do_something); /// /// future<> func(foo& obj, int val) { /// return stage(&obj, val); /// } /// ``` /// -/// \see make_execution_stage(Function&&) +/// \see make_execution_stage(const sstring&, Function&&) /// \param name unique name of the execution stage /// \param fn member function to be executed by the stage /// \return concrete_execution_stage @@ -407,34 +408,37 @@ auto make_execution_stage(const sstring& name, Ret (Object::*fn)(Args...) const) inline execution_stage::execution_stage(const sstring& name) : _name(name) - , _metric_group("execution_stages", { - metrics::make_derive("tasks_scheduled", - metrics::description("Counts tasks scheduled by execution stages"), - { metrics::label_instance("execution_stage", name), }, - [name, &esm = internal::execution_stage_manager::get()] { - return esm.get_stage(name)->get_stats().tasks_scheduled; - }), - metrics::make_derive("tasks_preempted", - metrics::description("Counts tasks which were preempted before execution all queued operations"), - { metrics::label_instance("execution_stage", name), }, - [name, &esm = internal::execution_stage_manager::get()] { - return esm.get_stage(name)->get_stats().tasks_preempted; - }), - metrics::make_derive("function_calls_enqueued", - metrics::description("Counts function calls added to execution stages queues"), - { metrics::label_instance("execution_stage", name), }, - [name, &esm = internal::execution_stage_manager::get()] { - return esm.get_stage(name)->get_stats().function_calls_enqueued; - }), - metrics::make_derive("function_calls_executed", - metrics::description("Counts function calls executed by execution stages"), - { metrics::label_instance("execution_stage", name), }, - [name, &esm = internal::execution_stage_manager::get()] { - return esm.get_stage(name)->get_stats().function_calls_executed; - }), - }) + { internal::execution_stage_manager::get().register_execution_stage(*this); + auto undo = defer([&] { internal::execution_stage_manager::get().unregister_execution_stage(*this); }); + _metric_group = metrics::metric_group("execution_stages", { + metrics::make_derive("tasks_scheduled", + metrics::description("Counts tasks scheduled by execution stages"), + { metrics::label_instance("execution_stage", name), }, + [name, &esm = internal::execution_stage_manager::get()] { + return esm.get_stage(name)->get_stats().tasks_scheduled; + }), + metrics::make_derive("tasks_preempted", + metrics::description("Counts tasks which were preempted before execution all queued operations"), + { metrics::label_instance("execution_stage", name), }, + [name, &esm = internal::execution_stage_manager::get()] { + return esm.get_stage(name)->get_stats().tasks_preempted; + }), + metrics::make_derive("function_calls_enqueued", + metrics::description("Counts function calls added to execution stages queues"), + { metrics::label_instance("execution_stage", name), }, + [name, &esm = internal::execution_stage_manager::get()] { + return esm.get_stage(name)->get_stats().function_calls_enqueued; + }), + metrics::make_derive("function_calls_executed", + metrics::description("Counts function calls executed by execution stages"), + { metrics::label_instance("execution_stage", name), }, + [name, &esm = internal::execution_stage_manager::get()] { + return esm.get_stage(name)->get_stats().function_calls_executed; + }), + }); + undo.cancel(); } inline execution_stage::~execution_stage() diff --git a/core/expiring_fifo.hh b/core/expiring_fifo.hh index d5b8ceee696..63fe96259be 100644 --- a/core/expiring_fifo.hh +++ b/core/expiring_fifo.hh @@ -29,6 +29,8 @@ #include "future-util.hh" #include "lowres_clock.hh" +namespace seastar { + template struct dummy_expiry { void operator()(T&) noexcept {}; @@ -165,3 +167,5 @@ public: drop_expired_front(); } }; + +} diff --git a/core/fair_queue.hh b/core/fair_queue.hh index 0862868ec6d..be8aa37244a 100644 --- a/core/fair_queue.hh +++ b/core/fair_queue.hh @@ -33,6 +33,8 @@ #include #include +namespace seastar { + /// \addtogroup io-module /// @{ @@ -227,3 +229,5 @@ public: } }; /// @} + +} diff --git a/core/file-impl.hh b/core/file-impl.hh index f9cf465f63c..c35a9f9f887 100644 --- a/core/file-impl.hh +++ b/core/file-impl.hh @@ -25,6 +25,8 @@ #include #include +namespace seastar { + class posix_file_handle_impl : public seastar::file_handle_impl { int _fd; std::atomic* _refcount; @@ -157,3 +159,4 @@ public: virtual future<> allocate(uint64_t position, uint64_t length) override; }; +} diff --git a/core/file.hh b/core/file.hh index 30028fa4c71..d8e6576c0a2 100644 --- a/core/file.hh +++ b/core/file.hh @@ -36,6 +36,8 @@ #include #include +namespace seastar { + /// \addtogroup fileio-module /// @{ @@ -99,8 +101,6 @@ const io_priority_class& default_priority_class(); class file; class file_impl; -namespace seastar { - class file_handle; // A handle that can be transported across shards and used to @@ -112,8 +112,6 @@ public: virtual shared_ptr to_file() && = 0; }; -} - class file_impl { protected: static file_impl* get_file_impl(file& f); @@ -135,7 +133,7 @@ public: virtual future<> allocate(uint64_t position, uint64_t length) = 0; virtual future size(void) = 0; virtual future<> close() = 0; - virtual std::unique_ptr dup(); + virtual std::unique_ptr dup(); virtual subscription list_directory(std::function (directory_entry de)> next) = 0; virtual future> dma_read_bulk(uint64_t offset, size_t range_size, const io_priority_class& pc) = 0; @@ -174,7 +172,7 @@ public: : _file_impl(std::move(impl)) {} /// Constructs a file object from a \ref file_handle obtained from another shard - explicit file(seastar::file_handle&& handle); + explicit file(file_handle&& handle); /// Checks whether the file object was initialized. /// @@ -430,7 +428,7 @@ public: /// /// \note Use on read-only files. /// - seastar::file_handle dup(); + file_handle dup(); template struct read_state; @@ -439,8 +437,6 @@ private: friend class file_impl; }; -namespace seastar { - /// \brief A shard-transportable handle to a file /// /// If you need to access a file (for reads only) across multiple shards, @@ -449,9 +445,9 @@ namespace seastar { /// object on that shard. This is more efficient than calling open_file_dma() /// again. class file_handle { - std::unique_ptr _impl; + std::unique_ptr _impl; private: - explicit file_handle(std::unique_ptr impl) : _impl(std::move(impl)) {} + explicit file_handle(std::unique_ptr impl) : _impl(std::move(impl)) {} public: /// Copies a file handle object file_handle(const file_handle&); @@ -466,11 +462,9 @@ public: /// Converts the file handle object to a \ref file. file to_file() &&; - friend class ::file; + friend class file; }; -} - /// \cond internal template @@ -543,4 +537,6 @@ private: /// @} +} + #endif /* FILE_HH_ */ diff --git a/core/fsqual.cc b/core/fsqual.cc index a7c50bb8b12..96c7ea6d9ff 100644 --- a/core/fsqual.cc +++ b/core/fsqual.cc @@ -31,6 +31,8 @@ #include #include "fsqual.hh" +namespace seastar { + // Runs func(), and also adds the number of context switches // that happened during func() to counter. template @@ -92,3 +94,5 @@ bool filesystem_has_good_aio_support(sstring directory, bool verbose) { } return ok; } + +} diff --git a/core/fsqual.hh b/core/fsqual.hh index c00a93ce2fd..5c6ee028346 100644 --- a/core/fsqual.hh +++ b/core/fsqual.hh @@ -25,6 +25,10 @@ #include "sstring.hh" +namespace seastar { + bool filesystem_has_good_aio_support(sstring directory, bool verbose = false); +} + #endif /* CORE_FSQUAL_HH_ */ diff --git a/core/fstream.cc b/core/fstream.cc index 6c3e1317b4a..354cea75948 100644 --- a/core/fstream.cc +++ b/core/fstream.cc @@ -27,6 +27,8 @@ #include #include +namespace seastar { + class file_data_source_impl : public data_source_impl { struct issued_read { uint64_t _pos; @@ -245,7 +247,7 @@ class file_data_source_impl : public data_source_impl { _read_buffers.emplace_back(_pos, actual_size, futurize>>::apply([&] { return _file.dma_read_bulk(start, len, _options.io_priority_class); }).then_wrapped( - [this, start, end, pos = _pos, remain = _remain] (future> ret) { + [this, start, pos = _pos, remain = _remain] (future> ret) { --_reads_in_progress; if (_done && !_reads_in_progress) { _done->set_value(); @@ -426,3 +428,5 @@ output_stream make_file_output_stream(file f, file_output_stream_options o return output_stream(file_data_sink(std::move(f), options), options.buffer_size, true); } +} + diff --git a/core/fstream.hh b/core/fstream.hh index 79f7d00e3cb..e92622e9244 100644 --- a/core/fstream.hh +++ b/core/fstream.hh @@ -34,6 +34,8 @@ #include "iostream.hh" #include "shared_ptr.hh" +namespace seastar { + class file_input_stream_history { static constexpr uint64_t window_size = 4 * 1024 * 1024; struct window { @@ -51,7 +53,7 @@ class file_input_stream_history { struct file_input_stream_options { size_t buffer_size = 8192; ///< I/O buffer size unsigned read_ahead = 0; ///< Maximum number of extra read-ahead operations - ::io_priority_class io_priority_class = default_priority_class(); + ::seastar::io_priority_class io_priority_class = default_priority_class(); lw_shared_ptr dynamic_adjustments = { }; ///< Input stream history, if null dynamic adjustments are disabled }; @@ -83,7 +85,7 @@ struct file_output_stream_options { unsigned buffer_size = 8192; unsigned preallocation_size = 1024*1024; // 1MB unsigned write_behind = 1; ///< Number of buffers to write in parallel - ::io_priority_class io_priority_class = default_priority_class(); + ::seastar::io_priority_class io_priority_class = default_priority_class(); }; // Create an output_stream for writing starting at the position zero of a @@ -100,3 +102,4 @@ output_stream make_file_output_stream( file file, file_output_stream_options options); +} diff --git a/core/function_traits.hh b/core/function_traits.hh index 1d278a6207f..a3b9b9d31a8 100644 --- a/core/function_traits.hh +++ b/core/function_traits.hh @@ -23,6 +23,8 @@ #include +namespace seastar { + template struct function_traits; @@ -63,3 +65,4 @@ template struct function_traits : public function_traits> {}; +} diff --git a/core/future-util.hh b/core/future-util.hh index 840f25f9f33..cfe1dbe31f6 100644 --- a/core/future-util.hh +++ b/core/future-util.hh @@ -37,6 +37,8 @@ #include #include "util/tuple_utils.hh" +namespace seastar { + /// \cond internal extern __thread size_t task_quota; /// \endcond @@ -372,7 +374,7 @@ future<> do_for_each(Iterator begin, Iterator end, AsyncAction&& action) { if (begin == end) { return f; } - if (!f.available()) { + if (!f.available() || need_preempt()) { return std::move(f).then([action = std::forward(action), begin = std::move(begin), end = std::move(end)] () mutable { return do_for_each(std::move(begin), std::move(end), std::forward(action)); @@ -403,7 +405,6 @@ future<> do_for_each(Container& c, AsyncAction&& action) { } /// \cond internal -namespace seastar { namespace internal { template @@ -446,12 +447,9 @@ public: } }; -} } /// \endcond -namespace seastar { - GCC6_CONCEPT( /// \cond internal @@ -480,8 +478,6 @@ concept bool AllAreFutures = impl::is_tuple_of_futures>::val ) -} - /// Wait for many futures to complete, capturing possible errors (variadic version). /// @@ -497,14 +493,13 @@ GCC6_CONCEPT( requires seastar::AllAreFutures ) inline future> when_all(Futs&&... futs) { - namespace si = seastar::internal; + namespace si = internal; using state = si::when_all_state, Futs...>; auto s = make_lw_shared(std::forward(futs)...); return s->wait_all(std::make_index_sequence()); } /// \cond internal -namespace seastar { namespace internal { template @@ -563,7 +558,6 @@ do_when_all(FutureIterator begin, FutureIterator end) { return complete_when_all(std::move(ret), ret.begin()); } -} } /// \endcond @@ -582,7 +576,7 @@ GCC6_CONCEPT( requires requires (FutureIterator i) { { *i++ }; requires is_futur inline future::value_type>> when_all(FutureIterator begin, FutureIterator end) { - namespace si = seastar::internal; + namespace si = internal; using itraits = std::iterator_traits; using result_transform = si::identity_futures_vector; return si::do_when_all(std::move(begin), std::move(end)); @@ -848,8 +842,6 @@ future with_timeout(std::chrono::time_point timeout, futu return result; } -namespace seastar { - namespace internal { template diff --git a/core/future.hh b/core/future.hh index de551fb6257..7e82684ea34 100644 --- a/core/future.hh +++ b/core/future.hh @@ -35,6 +35,8 @@ #include "function_traits.hh" #include "../util/gcc6-concepts.hh" +namespace seastar { + /// \defgroup future-module Futures and Promises /// /// \brief @@ -74,6 +76,9 @@ class promise; template class future; +template +class shared_future; + /// \brief Creates a \ref future in an available, value state. /// /// Creates a \ref future object that is already resolved. This @@ -648,8 +653,6 @@ using futurize_t = typename futurize::type; /// @} -namespace seastar { - GCC6_CONCEPT( template @@ -672,8 +675,6 @@ concept bool ApplyReturnsAnyFuture = requires (Func f, T... args) { ) -} - /// \addtogroup future-module /// @{ @@ -721,7 +722,7 @@ private: template void schedule(Func&& func) { if (state()->available()) { - ::schedule(std::make_unique>(std::move(func), std::move(*state()))); + ::seastar::schedule(std::make_unique>(std::move(func), std::move(*state()))); } else { assert(_promise); _promise->schedule(std::move(func)); @@ -811,8 +812,8 @@ public: std::tuple get() { if (!state()->available()) { wait(); - } else if (seastar::thread_impl::get() && seastar::thread_impl::should_yield()) { - seastar::thread_impl::yield(); + } else if (thread_impl::get() && thread_impl::should_yield()) { + thread_impl::yield(); } return get_available_state().get(); } @@ -836,13 +837,13 @@ public: /// \cond internal void wait() { - auto thread = seastar::thread_impl::get(); + auto thread = thread_impl::get(); assert(thread); schedule([this, thread] (future_state&& new_state) { *state() = std::move(new_state); - seastar::thread_impl::switch_in(thread); + thread_impl::switch_in(thread); }); - seastar::thread_impl::switch_out(thread); + thread_impl::switch_out(thread); } /// \endcond @@ -878,7 +879,7 @@ public: /// \return a \c future representing the return value of \c func, applied /// to the eventual value of this future. template >> - GCC6_CONCEPT( requires seastar::CanApply ) + GCC6_CONCEPT( requires ::seastar::CanApply ) Result then(Func&& func) noexcept { using futurator = futurize>; @@ -925,7 +926,7 @@ public: /// \return a \c future representing the return value of \c func, applied /// to the eventual value of this future. template >> - GCC6_CONCEPT( requires seastar::CanApply ) + GCC6_CONCEPT( requires ::seastar::CanApply ) Result then_wrapped(Func&& func) noexcept { using futurator = futurize>; @@ -985,7 +986,7 @@ public: * nested will be propagated. */ template - GCC6_CONCEPT( requires seastar::CanApply ) + GCC6_CONCEPT( requires ::seastar::CanApply ) future finally(Func&& func) noexcept { return then_wrapped(finally_body>::value>(std::forward(func))); } @@ -1072,9 +1073,9 @@ public: /// future<>, the handler function does not need to return anything. template /* Broken? - GCC6_CONCEPT( requires seastar::ApplyReturns, std::exception_ptr> - || (sizeof...(T) == 0 && seastar::ApplyReturns) - || (sizeof...(T) == 1 && seastar::ApplyReturns) + GCC6_CONCEPT( requires ::seastar::ApplyReturns, std::exception_ptr> + || (sizeof...(T) == 0 && ::seastar::ApplyReturns) + || (sizeof...(T) == 1 && ::seastar::ApplyReturns) ) */ future handle_exception(Func&& func) noexcept { using func_ret = std::result_of_t; @@ -1162,9 +1163,9 @@ void promise::make_ready() noexcept { if (_task) { _state = nullptr; if (Urgent == urgent::yes && !need_preempt()) { - ::schedule_urgent(std::move(_task)); + ::seastar::schedule_urgent(std::move(_task)); } else { - ::schedule(std::move(_task)); + ::seastar::schedule(std::move(_task)); } } } @@ -1222,7 +1223,7 @@ template template typename futurize::type futurize::apply(Func&& func, std::tuple&& args) noexcept { try { - return convert(::apply(std::forward(func), std::move(args))); + return convert(::seastar::apply(std::forward(func), std::move(args))); } catch (...) { return make_exception_future(std::current_exception()); } @@ -1266,7 +1267,7 @@ inline std::enable_if_t>::value, future<>> do_void_futurize_apply_tuple(Func&& func, std::tuple&& args) noexcept { try { - ::apply(std::forward(func), std::move(args)); + ::seastar::apply(std::forward(func), std::move(args)); return make_ready_future<>(); } catch (...) { return make_exception_future(std::current_exception()); @@ -1278,7 +1279,7 @@ inline std::enable_if_t>::value, future<>> do_void_futurize_apply_tuple(Func&& func, std::tuple&& args) noexcept { try { - return ::apply(std::forward(func), std::move(args)); + return ::seastar::apply(std::forward(func), std::move(args)); } catch (...) { return make_exception_future(std::current_exception()); } @@ -1298,7 +1299,7 @@ template template typename futurize>::type futurize>::apply(Func&& func, std::tuple&& args) noexcept { try { - return ::apply(std::forward(func), std::move(args)); + return ::seastar::apply(std::forward(func), std::move(args)); } catch (...) { return make_exception_future(std::current_exception()); } @@ -1319,7 +1320,7 @@ template inline future futurize::make_exception_future(Arg&& arg) { - return ::make_exception_future(std::forward(arg)); + return ::seastar::make_exception_future(std::forward(arg)); } template @@ -1327,14 +1328,14 @@ template inline future futurize>::make_exception_future(Arg&& arg) { - return ::make_exception_future(std::forward(arg)); + return ::seastar::make_exception_future(std::forward(arg)); } template inline future<> futurize::make_exception_future(Arg&& arg) { - return ::make_exception_future<>(std::forward(arg)); + return ::seastar::make_exception_future<>(std::forward(arg)); } template @@ -1371,4 +1372,6 @@ auto futurize_apply(Func&& func, Args&&... args) { /// \endcond +} + #endif /* FUTURE_HH_ */ diff --git a/core/iostream-impl.hh b/core/iostream-impl.hh index 07e57a36532..a371adfa642 100644 --- a/core/iostream-impl.hh +++ b/core/iostream-impl.hh @@ -27,6 +27,8 @@ #include "net/packet.hh" #include "core/future-util.hh" +namespace seastar { + inline future> data_source_impl::skip(uint64_t n) { return do_with(uint64_t(n), [this] (uint64_t& n) { @@ -195,7 +197,7 @@ future<> input_stream::consume(Consumer& consumer) { return repeat([&consumer, this] { if (_buf.empty() && !_eof) { - return _fd.get().then([this, &consumer] (tmp_buf buf) { + return _fd.get().then([this] (tmp_buf buf) { _buf = std::move(buf); _eof = _buf.empty(); return make_ready_future(stop_iteration::no); @@ -221,7 +223,7 @@ input_stream::consume(Consumer& consumer) { // TODO: here we wait for the consumer to finish the previous // buffer (fulfilling "unconsumed") before starting to read the // next one. Consider reading ahead. - return unconsumed.then([this, &consumer] (unconsumed_remainder u) { + return unconsumed.then([this] (unconsumed_remainder u) { if (u) { // consumer is done _buf = std::move(u.value()); @@ -466,3 +468,5 @@ output_stream::close() { return _fd.close(); }); } + +} diff --git a/core/iostream.hh b/core/iostream.hh index ad58802a57d..5e833995138 100644 --- a/core/iostream.hh +++ b/core/iostream.hh @@ -39,6 +39,8 @@ #include "temporary_buffer.hh" #include "scattered_message.hh" +namespace seastar { + namespace net { class packet; } class data_source_impl { @@ -224,4 +226,6 @@ private: friend class reactor; }; +} + #include "iostream-impl.hh" diff --git a/core/lowres_clock.hh b/core/lowres_clock.hh index b79f7935574..a92ab333eb3 100644 --- a/core/lowres_clock.hh +++ b/core/lowres_clock.hh @@ -25,6 +25,8 @@ #include #include "timer.hh" +namespace seastar { + class lowres_clock { public: typedef int64_t rep; @@ -49,3 +51,6 @@ private: // High resolution timer expires every 10 milliseconds static constexpr std::chrono::milliseconds _granularity{10}; }; + +} + diff --git a/core/manual_clock.hh b/core/manual_clock.hh index cfdaf3d5f52..34e490573ae 100644 --- a/core/manual_clock.hh +++ b/core/manual_clock.hh @@ -23,6 +23,8 @@ #include +namespace seastar { + class manual_clock { public: using rep = int64_t; @@ -39,3 +41,6 @@ public: } static void advance(duration d); }; + +} + diff --git a/core/memory.cc b/core/memory.cc index 4b65c3e4fb7..9333f915cda 100644 --- a/core/memory.cc +++ b/core/memory.cc @@ -53,6 +53,8 @@ #include "memory.hh" #include "reactor.hh" +namespace seastar { + namespace memory { static thread_local int abort_on_alloc_failure_suppressed = 0; @@ -67,6 +69,8 @@ disable_abort_on_alloc_failure_temporarily::~disable_abort_on_alloc_failure_temp } +} + #ifndef DEFAULT_ALLOCATOR #include "bitops.hh" @@ -92,6 +96,8 @@ disable_abort_on_alloc_failure_temporarily::~disable_abort_on_alloc_failure_temp #include #endif +namespace seastar { + struct allocation_site { mutable size_t count = 0; // number of live objects allocated at backtrace. mutable size_t size = 0; // amount of bytes in live objects allocated at backtrace. @@ -107,17 +113,21 @@ struct allocation_site { } }; +} + namespace std { template<> -struct hash<::allocation_site> { - size_t operator()(const ::allocation_site& bi) const { - return std::hash()(bi.backtrace); +struct hash { + size_t operator()(const seastar::allocation_site& bi) const { + return std::hash()(bi.backtrace); } }; } +namespace seastar { + using allocation_site_ptr = const allocation_site*; namespace memory { @@ -883,7 +893,7 @@ bool cpu_pages::initialize() { } mmap_area -allocate_anonymous_memory(optional where, size_t how_much) { +allocate_anonymous_memory(std::experimental::optional where, size_t how_much) { return mmap_anonymous(where.value_or(nullptr), how_much, PROT_READ | PROT_WRITE, @@ -891,7 +901,7 @@ allocate_anonymous_memory(optional where, size_t how_much) { } mmap_area -allocate_hugetlbfs_memory(file_desc& fd, optional where, size_t how_much) { +allocate_hugetlbfs_memory(file_desc& fd, std::experimental::optional where, size_t how_much) { auto pos = fd.size(); fd.truncate(pos + how_much); auto ret = fd.map( @@ -1269,7 +1279,7 @@ reclaimer::~reclaimer() { r.erase(std::find(r.begin(), r.end(), this)); } -void configure(std::vector m, +void configure(std::vector m, bool mbind, optional hugetlbfs_path) { size_t total = 0; for (auto&& x : m) { @@ -1290,16 +1300,18 @@ void configure(std::vector m, for (auto&& x : m) { #ifdef HAVE_NUMA unsigned long nodemask = 1UL << x.nodeid; - auto r = ::mbind(cpu_mem.mem() + pos, x.bytes, - MPOL_PREFERRED, - &nodemask, std::numeric_limits::digits, - MPOL_MF_MOVE); - - if (r == -1) { - char err[1000] = {}; - strerror_r(errno, err, sizeof(err)); - std::cerr << "WARNING: unable to mbind shard memory; performance may suffer: " - << err << std::endl; + if (mbind) { + auto r = ::mbind(cpu_mem.mem() + pos, x.bytes, + MPOL_PREFERRED, + &nodemask, std::numeric_limits::digits, + MPOL_MF_MOVE); + + if (r == -1) { + char err[1000] = {}; + strerror_r(errno, err, sizeof(err)); + std::cerr << "WARNING: unable to mbind shard memory; performance may suffer: " + << err << std::endl; + } } #endif pos += x.bytes; @@ -1345,7 +1357,9 @@ void set_min_free_pages(size_t pages) { } -using namespace memory; +} + +using namespace seastar::memory; extern "C" [[gnu::visibility("default")]] @@ -1369,7 +1383,7 @@ extern "C" [[gnu::externally_visible]] void free(void* ptr) { if (ptr) { - memory::free(ptr); + seastar::memory::free(ptr); } } @@ -1408,7 +1422,7 @@ void* realloc(void* ptr, size_t size) { return nullptr; } if (size < old_size) { - memory::shrink(ptr, size); + seastar::memory::shrink(ptr, size); return ptr; } auto nptr = malloc(size); @@ -1523,28 +1537,28 @@ void* operator new[](size_t size) { [[gnu::visibility("default")]] void operator delete(void* ptr) throw () { if (ptr) { - memory::free(ptr); + seastar::memory::free(ptr); } } [[gnu::visibility("default")]] void operator delete[](void* ptr) throw () { if (ptr) { - memory::free(ptr); + seastar::memory::free(ptr); } } [[gnu::visibility("default")]] void operator delete(void* ptr, size_t size) throw () { if (ptr) { - memory::free(ptr, size); + seastar::memory::free(ptr, size); } } [[gnu::visibility("default")]] void operator delete[](void* ptr, size_t size) throw () { if (ptr) { - memory::free(ptr, size); + seastar::memory::free(ptr, size); } } @@ -1575,51 +1589,55 @@ void* operator new[](size_t size, std::nothrow_t) throw () { [[gnu::visibility("default")]] void operator delete(void* ptr, std::nothrow_t) throw () { if (ptr) { - memory::free(ptr); + seastar::memory::free(ptr); } } [[gnu::visibility("default")]] void operator delete[](void* ptr, std::nothrow_t) throw () { if (ptr) { - memory::free(ptr); + seastar::memory::free(ptr); } } [[gnu::visibility("default")]] void operator delete(void* ptr, size_t size, std::nothrow_t) throw () { if (ptr) { - memory::free(ptr, size); + seastar::memory::free(ptr, size); } } [[gnu::visibility("default")]] void operator delete[](void* ptr, size_t size, std::nothrow_t) throw () { if (ptr) { - memory::free(ptr, size); + seastar::memory::free(ptr, size); } } -void* operator new(size_t size, with_alignment wa) { +void* operator new(size_t size, seastar::with_alignment wa) { return allocate_aligned(wa.alignment(), size); } -void* operator new[](size_t size, with_alignment wa) { +void* operator new[](size_t size, seastar::with_alignment wa) { return allocate_aligned(wa.alignment(), size); } -void operator delete(void* ptr, with_alignment wa) { +void operator delete(void* ptr, seastar::with_alignment wa) { // only called for matching operator new, so we know ptr != nullptr - return memory::free(ptr); + return seastar::memory::free(ptr); } -void operator delete[](void* ptr, with_alignment wa) { +void operator delete[](void* ptr, seastar::with_alignment wa) { // only called for matching operator new, so we know ptr != nullptr - return memory::free(ptr); + return seastar::memory::free(ptr); } +namespace seastar { + #else +namespace seastar { + namespace memory { void set_heap_profiling_enabled(bool enabled) { @@ -1639,7 +1657,7 @@ reclaimer::~reclaimer() { void set_reclaim_hook(std::function)> hook) { } -void configure(std::vector m, std::experimental::optional hugepages_path) { +void configure(std::vector m, bool mbind, std::experimental::optional hugepages_path) { } statistics stats() { @@ -1669,7 +1687,9 @@ void set_min_free_pages(size_t pages) { } -void* operator new(size_t size, with_alignment wa) { +} + +void* operator new(size_t size, seastar::with_alignment wa) { void* ret; if (posix_memalign(&ret, wa.alignment(), size) != 0) { throw std::bad_alloc(); @@ -1677,7 +1697,7 @@ void* operator new(size_t size, with_alignment wa) { return ret; } -void* operator new[](size_t size, with_alignment wa) { +void* operator new[](size_t size, seastar::with_alignment wa) { void* ret; if (posix_memalign(&ret, wa.alignment(), size) != 0) { throw std::bad_alloc(); @@ -1685,14 +1705,19 @@ void* operator new[](size_t size, with_alignment wa) { return ret; } -void operator delete(void* ptr, with_alignment wa) { +void operator delete(void* ptr, seastar::with_alignment wa) { return ::free(ptr); } -void operator delete[](void* ptr, with_alignment wa) { +void operator delete[](void* ptr, seastar::with_alignment wa) { return ::free(ptr); } +namespace seastar { + #endif /// \endcond + +} + diff --git a/core/memory.hh b/core/memory.hh index 6f9940ac1c3..2e6ff5be73b 100644 --- a/core/memory.hh +++ b/core/memory.hh @@ -27,6 +27,7 @@ #include #include +namespace seastar { /// \defgroup memory-module Memory management /// @@ -57,7 +58,7 @@ static constexpr size_t page_bits = 12; static constexpr size_t page_size = 1 << page_bits; // 4K static constexpr size_t huge_page_size = 512 * page_size; // 2M -void configure(std::vector m, +void configure(std::vector m, bool mbind, std::experimental::optional hugetlbfs_path = {}); void enable_abort_on_allocation_failure(); @@ -207,9 +208,11 @@ public: size_t alignment() const { return _align; } }; -void* operator new(size_t size, with_alignment wa); -void* operator new[](size_t size, with_alignment wa); -void operator delete(void* ptr, with_alignment wa); -void operator delete[](void* ptr, with_alignment wa); +} + +void* operator new(size_t size, seastar::with_alignment wa); +void* operator new[](size_t size, seastar::with_alignment wa); +void operator delete(void* ptr, seastar::with_alignment wa); +void operator delete[](void* ptr, seastar::with_alignment wa); #endif /* MEMORY_HH_ */ diff --git a/core/metrics.cc b/core/metrics.cc index 503c3f40733..2f90fead1fe 100644 --- a/core/metrics.cc +++ b/core/metrics.cc @@ -175,7 +175,7 @@ metric_groups_impl& metric_groups_impl::add_metric(group_name_type name, const m metric_id id(name, md._impl->name, md._impl->labels); shared_ptr rm = - ::make_shared(id, md._impl->type.base_type, md._impl->f, md._impl->d, md._impl->enabled); + ::seastar::make_shared(id, md._impl->type.base_type, md._impl->f, md._impl->d, md._impl->enabled); get_local_impl()->add_registration(id, rm); diff --git a/core/metrics_api.hh b/core/metrics_api.hh index f794263db17..6b85bdad8ad 100644 --- a/core/metrics_api.hh +++ b/core/metrics_api.hh @@ -50,7 +50,7 @@ struct hash { result_type operator()(argument_type const& s) const { result_type h = 0; for (auto&& i : s) { - boost::hash_combine(h, std::hash{}(i.second)); + boost::hash_combine(h, std::hash{}(i.second)); } return h; } @@ -133,8 +133,8 @@ struct hash typedef ::std::size_t result_type; result_type operator()(argument_type const& s) const { - result_type const h1 ( std::hash{}(s.group_name()) ); - result_type const h2 ( std::hash{}(s.instance_id()) ); + result_type const h1 ( std::hash{}(s.group_name()) ); + result_type const h2 ( std::hash{}(s.instance_id()) ); return h1 ^ (h2 << 1); // or use boost::hash_combine } }; diff --git a/core/metrics_registration.hh b/core/metrics_registration.hh index 0cab83e22fb..a936959acfe 100644 --- a/core/metrics_registration.hh +++ b/core/metrics_registration.hh @@ -87,6 +87,7 @@ public: metric_groups() noexcept; metric_groups(metric_groups&&) = default; virtual ~metric_groups(); + metric_groups& operator=(metric_groups&&) = default; /*! * \brief add metrics belong to the same group in the constructor. * @@ -130,6 +131,7 @@ public: metric_group(const metric_group&) = delete; metric_group(metric_group&&) = default; virtual ~metric_group(); + metric_group& operator=(metric_group&&) = default; /*! * \brief add metrics belong to the same group in the constructor. diff --git a/core/posix.cc b/core/posix.cc index 7aa7ea96494..7dae9804a70 100644 --- a/core/posix.cc +++ b/core/posix.cc @@ -23,6 +23,8 @@ #include "align.hh" #include +namespace seastar { + file_desc file_desc::temporary(sstring directory) { // FIXME: add O_TMPFILE support one day @@ -100,3 +102,6 @@ void posix_thread::join() { pthread_join(_pthread, NULL); _valid = false; } + +} + diff --git a/core/posix.hh b/core/posix.hh index 87965eda556..1a363771d53 100644 --- a/core/posix.hh +++ b/core/posix.hh @@ -44,6 +44,8 @@ #include #include "net/socket_defs.hh" +namespace seastar { + /// \file /// \defgroup posix-support POSIX Support /// @@ -317,8 +319,6 @@ private: }; -namespace seastar { - namespace posix { /// Converts a duration value to a `timespec` @@ -363,8 +363,6 @@ to_absolute_itimerspec(std::chrono::time_point base, std::chron } -} - class posix_thread { public: class attr; @@ -462,4 +460,6 @@ void pin_this_thread(unsigned cpu_id) { /// @} +} + #endif /* FILE_DESC_HH_ */ diff --git a/core/preempt.hh b/core/preempt.hh index 92a0b54e1de..e821473706d 100644 --- a/core/preempt.hh +++ b/core/preempt.hh @@ -22,6 +22,8 @@ #pragma once #include +namespace seastar { + extern __thread bool g_need_preempt; inline bool need_preempt() { @@ -34,4 +36,4 @@ inline bool need_preempt() { #endif } - +} diff --git a/core/prefetch.hh b/core/prefetch.hh index 296f22f6648..71f6d2d6d8c 100644 --- a/core/prefetch.hh +++ b/core/prefetch.hh @@ -27,6 +27,8 @@ #include #include "align.hh" +namespace seastar { + static constexpr size_t cacheline_size = 64; template struct prefetcher; @@ -111,4 +113,6 @@ void prefetchw_n(T** pptr) { boost::mpl::for_each< boost::mpl::range_c >( [pptr] (size_t x) { prefetchw(*(pptr + x)); } ); } +} + #endif diff --git a/core/print.hh b/core/print.hh index f07fc604a56..b2536bbac74 100644 --- a/core/print.hh +++ b/core/print.hh @@ -47,6 +47,8 @@ operator<<(std::ostream&& os, const void* ptr) { return os << ptr; // selects non-rvalue version } +namespace seastar { + template std::ostream& fprint(std::ostream& os, const char* fmt, A&&... a) { @@ -119,7 +121,6 @@ log(A&&... a) { print(std::forward(a)...); } -namespace seastar { /** * Evaluate the formatted string in a native fmt library format * diff --git a/core/prometheus.cc b/core/prometheus.cc index 4b6c36dff86..d7717a19532 100644 --- a/core/prometheus.cc +++ b/core/prometheus.cc @@ -33,7 +33,7 @@ #include #include -using namespace seastar; +namespace seastar { namespace prometheus { namespace pm = io::prometheus::client; @@ -63,6 +63,7 @@ static bool write_delimited_to(const google::protobuf::MessageLite& message, } static pm::Metric* add_label(pm::Metric* mt, const metrics::impl::metric_id & id, const config& ctx) { + mt->mutable_label()->Reserve(id.labels().size() + 1); auto label = mt->add_label(); label->set_name("instance"); label->set_value(ctx.hostname); @@ -233,6 +234,7 @@ std::string get_protobuf_representation(const metrics_families& families, const auto&& metrics = name_metrics.second; pm::MetricFamily mtf; mtf.set_name(ctx.prefix + "_" + name); + mtf.mutable_metric()->Reserve(metrics.size()); for (auto pmetric : metrics) { const seastar::metrics::impl::registered_metric& reg = *std::get(pmetric); auto&& id = reg.get_id(); @@ -292,3 +294,4 @@ future<> start(httpd::http_server_control& http_server, config ctx) { } } +} diff --git a/core/prometheus.hh b/core/prometheus.hh index 32e61d4cbb6..7cc031e6ba4 100644 --- a/core/prometheus.hh +++ b/core/prometheus.hh @@ -23,6 +23,8 @@ #include "http/httpd.hh" +namespace seastar { + namespace prometheus { /*! @@ -36,3 +38,5 @@ struct config { future<> start(httpd::http_server_control& http_server, config ctx); } + +} diff --git a/core/queue.hh b/core/queue.hh index 98de0751279..b3ed8f4d21b 100644 --- a/core/queue.hh +++ b/core/queue.hh @@ -27,6 +27,8 @@ #include #include +namespace seastar { + template class queue { std::queue> _q; @@ -235,4 +237,6 @@ future<> queue::not_full() { } } +} + #endif /* QUEUE_HH_ */ diff --git a/core/ragel.hh b/core/ragel.hh index daefca6cf82..7a7ed801019 100644 --- a/core/ragel.hh +++ b/core/ragel.hh @@ -31,6 +31,8 @@ #include #include "future.hh" +namespace seastar { + // Support classes for Ragel parsers // Builds an sstring that can be scattered across multiple packets. @@ -137,4 +139,6 @@ public: } }; +} + #endif /* RAGEL_HH_ */ diff --git a/core/reactor.cc b/core/reactor.cc index 4faf580df81..dd8bbf90687 100644 --- a/core/reactor.cc +++ b/core/reactor.cc @@ -94,10 +94,11 @@ #include "core/metrics.hh" #include "execution_stage.hh" +namespace seastar { + using namespace std::chrono_literals; using namespace net; -using namespace seastar; seastar::logger seastar_logger("seastar"); @@ -113,7 +114,7 @@ timespec to_timespec(steady_clock_type::time_point t) { lowres_clock::lowres_clock() { update(); - _timer.set_callback([this] { update(); }); + _timer.set_callback(&lowres_clock::update); _timer.arm_periodic(_granularity); } @@ -1428,7 +1429,7 @@ append_challenged_posix_file_impl::flush() { _committed_size = _logical_size; } return posix_file_impl::flush(); - }).then_wrapped([this, pr] (future<> f) { + }).then_wrapped([pr] (future<> f) { f.forward_to(std::move(*pr)); }); } @@ -1635,7 +1636,7 @@ reactor::open_file_dma(sstring name, open_flags flags, file_open_options options future<> reactor::remove_file(sstring pathname) { - return engine()._thread_pool.submit>([this, pathname] { + return engine()._thread_pool.submit>([pathname] { return wrap_syscall(::remove(pathname.c_str())); }).then([] (syscall_result sr) { sr.throw_if_error(); @@ -1645,7 +1646,7 @@ reactor::remove_file(sstring pathname) { future<> reactor::rename_file(sstring old_pathname, sstring new_pathname) { - return engine()._thread_pool.submit>([this, old_pathname, new_pathname] { + return engine()._thread_pool.submit>([old_pathname, new_pathname] { return wrap_syscall(::rename(old_pathname.c_str(), new_pathname.c_str())); }).then([] (syscall_result sr) { sr.throw_if_error(); @@ -1655,7 +1656,7 @@ reactor::rename_file(sstring old_pathname, sstring new_pathname) { future<> reactor::link_file(sstring oldpath, sstring newpath) { - return engine()._thread_pool.submit>([this, oldpath = std::move(oldpath), newpath = std::move(newpath)] { + return engine()._thread_pool.submit>([oldpath = std::move(oldpath), newpath = std::move(newpath)] { return wrap_syscall(::link(oldpath.c_str(), newpath.c_str())); }).then([] (syscall_result sr) { sr.throw_if_error(); @@ -1786,8 +1787,6 @@ reactor::touch_directory(sstring name) { }); } -namespace seastar { - file_handle::file_handle(const file_handle& x) : _impl(x._impl ? x._impl->clone() : std::unique_ptr()) { } @@ -1812,8 +1811,6 @@ file_handle::to_file() && { return file(std::move(*_impl).to_file()); } -} - file::file(seastar::file_handle&& handle) : _file_impl(std::move(std::move(handle).to_file()._file_impl)) { } @@ -1860,7 +1857,7 @@ posix_file_handle_impl::clone() const { shared_ptr posix_file_handle_impl::to_file() && { - auto ret = ::make_shared(_fd, _refcount); + auto ret = ::seastar::make_shared(_fd, _refcount); _fd = -1; _refcount = nullptr; return ret; @@ -1993,7 +1990,7 @@ posix_file_impl::close() noexcept { return make_ready_future>(wrap_syscall(::close(fd))); } }(); - return closed.then([this] (syscall_result sr) { + return closed.then([] (syscall_result sr) { sr.throw_if_error(); }); } @@ -2271,11 +2268,11 @@ void reactor::register_metrics() { description( "Counts reads from disk file streams. A high rate indicates high disk activity." " Contrast with other fstream_read* counters to locate bottlenecks.")), - make_derive("fstream_read_bytes", _io_stats.fstream_reads, + make_derive("fstream_read_bytes", _io_stats.fstream_read_bytes, description( "Counts bytes read from disk file streams. A high rate indicates high disk activity." " Divide by fstream_reads to determine average read size.")), - make_counter("fstream_reads_blocked", _io_stats.fstream_read_bytes, + make_counter("fstream_reads_blocked", _io_stats.fstream_reads_blocked, description( "Counts the number of times a disk read could not be satisfied from read-ahead buffers, and had to block." " Indicates short streams, or incorrect read ahead configuration.")), @@ -3256,10 +3253,14 @@ void schedule_urgent(std::unique_ptr t) { engine().add_urgent_task(std::move(t)); } +} + bool operator==(const ::sockaddr_in a, const ::sockaddr_in b) { return (a.sin_addr.s_addr == b.sin_addr.s_addr) && (a.sin_port == b.sin_port); } +namespace seastar { + void network_stack_registry::register_stack(sstring name, boost::program_options::options_description opts, std::function> (options opts)> create, bool make_default) { @@ -3350,6 +3351,7 @@ smp::get_options_description() #else ("max-io-requests", bpo::value(), "Maximum amount of concurrent requests to be sent to the disk. Defaults to 128 times the number of processors") #endif + ("mbind", bpo::value()->default_value(true), "enable mbind") ; return opts; } @@ -3522,6 +3524,10 @@ void smp::configure(boost::program_options::variables_map configuration) if (!thread_affinity && _using_dpdk) { print("warning: --thread-affinity 0 ignored in dpdk mode\n"); } + auto mbind = configuration["mbind"].as(); + if (!thread_affinity) { + mbind = false; + } smp::count = 1; smp::_tmain = std::this_thread::get_id(); @@ -3597,7 +3603,7 @@ void smp::configure(boost::program_options::variables_map configuration) if (thread_affinity) { smp::pin(allocations[0].cpu_id); } - memory::configure(allocations[0].mem, hugepages_path); + memory::configure(allocations[0].mem, mbind, hugepages_path); if (configuration.count("abort-on-seastar-bad-alloc")) { memory::enable_abort_on_allocation_failure(); @@ -3657,13 +3663,13 @@ void smp::configure(boost::program_options::variables_map configuration) unsigned i; for (i = 1; i < smp::count; i++) { auto allocation = allocations[i]; - create_thread([configuration, hugepages_path, i, allocation, assign_io_queue, alloc_io_queue, thread_affinity, heapprof_enabled] { + create_thread([configuration, hugepages_path, i, allocation, assign_io_queue, alloc_io_queue, thread_affinity, heapprof_enabled, mbind] { auto thread_name = seastar::format("reactor-{}", i); pthread_setname_np(pthread_self(), thread_name.c_str()); if (thread_affinity) { smp::pin(allocation.cpu_id); } - memory::configure(allocation.mem, hugepages_path); + memory::configure(allocation.mem, mbind, hugepages_path); memory::set_heap_profiling_enabled(heapprof_enabled); sigset_t mask; sigfillset(&mask); @@ -3760,7 +3766,7 @@ class reactor_notifier_epoll : public reactor_notifier { } virtual future<> wait() override { // convert _read.wait(), a future, to a future<>: - return _read.wait().then([this] (size_t ignore) { + return _read.wait().then([] (size_t ignore) { return make_ready_future<>(); }); } @@ -4126,6 +4132,9 @@ network_stack_registrator nsr_posix{"posix", }; #ifndef NO_EXCEPTION_INTERCEPT + +} + #include extern "C" @@ -4138,12 +4147,14 @@ int _Unwind_RaiseException(void *h) { if (!org) { org = (throw_fn)dlsym (RTLD_NEXT, "_Unwind_RaiseException"); } - if (local_engine) { - engine()._cxx_exceptions++; + if (seastar::local_engine) { + seastar::engine()._cxx_exceptions++; } return org(h); } +namespace seastar { + #endif steady_clock_type::duration reactor::total_idle_time() { @@ -4153,3 +4164,5 @@ steady_clock_type::duration reactor::total_idle_time() { steady_clock_type::duration reactor::total_busy_time() { return steady_clock_type::now() - _start_time - _total_idle; } + +} diff --git a/core/reactor.hh b/core/reactor.hh index d48ca04d465..af7eb32b161 100644 --- a/core/reactor.hh +++ b/core/reactor.hh @@ -84,6 +84,8 @@ extern "C" int _Unwind_RaiseException(void *h); +namespace seastar { + using shard_id = unsigned; @@ -158,6 +160,7 @@ private: std::unique_ptr _s; }; +} namespace std { @@ -172,6 +175,8 @@ struct hash<::sockaddr_in> { bool operator==(const ::sockaddr_in a, const ::sockaddr_in b); +namespace seastar { + class network_stack_registrator { public: using options = boost::program_options::variables_map; @@ -303,7 +308,7 @@ class smp_message_queue { // this makes sure that they have at least one cache line // between them, so hw prefecther will not accidentally prefetch // cache line used by aother cpu. - seastar::metrics::metric_groups _metrics; + metrics::metric_groups _metrics; struct alignas(64) { size_t _received = 0; size_t _last_rcv_batch = 0; @@ -529,7 +534,7 @@ private: uint64_t ops; uint32_t nr_queued; std::chrono::duration queue_time; - seastar::metrics::metric_groups _metric_groups; + metrics::metric_groups _metric_groups; priority_class_data(sstring name, priority_class_ptr ptr, shard_id owner); }; @@ -700,12 +705,12 @@ private: unsigned _tasks_processed_report_threshold; unsigned _max_task_backlog = 1000; - seastar::timer_set, &timer<>::_link> _timers; - seastar::timer_set, &timer<>::_link>::timer_list_t _expired_timers; - seastar::timer_set, &timer::_link> _lowres_timers; - seastar::timer_set, &timer::_link>::timer_list_t _expired_lowres_timers; - seastar::timer_set, &timer::_link> _manual_timers; - seastar::timer_set, &timer::_link>::timer_list_t _expired_manual_timers; + timer_set, &timer<>::_link> _timers; + timer_set, &timer<>::_link>::timer_list_t _expired_timers; + timer_set, &timer::_link> _lowres_timers; + timer_set, &timer::_link>::timer_list_t _expired_lowres_timers; + timer_set, &timer::_link> _manual_timers; + timer_set, &timer::_link>::timer_list_t _expired_manual_timers; io_context_t _io_context; std::vector _pending_aio; semaphore _io_context_available; @@ -816,13 +821,13 @@ public: server_socket listen(socket_address sa, listen_options opts = {}); future connect(socket_address sa); - future connect(socket_address, socket_address, seastar::transport proto = seastar::transport::TCP); + future connect(socket_address, socket_address, transport proto = transport::TCP); pollable_fd posix_listen(socket_address sa, listen_options opts = {}); bool posix_reuseport_available() const { return _reuseport; } - lw_shared_ptr make_pollable_fd(socket_address sa, seastar::transport proto = seastar::transport::TCP); + lw_shared_ptr make_pollable_fd(socket_address sa, transport proto = transport::TCP); future<> posix_connect(lw_shared_ptr pfd, socket_address sa, socket_address local); future accept(pollable_fd_state& listen_fd); @@ -946,8 +951,8 @@ private: friend class smp_message_queue; friend class poller; friend void add_to_flush_poller(output_stream* os); - friend int _Unwind_RaiseException(void *h); - seastar::metrics::metric_groups _metric_groups; + friend int ::_Unwind_RaiseException(void *h); + metrics::metric_groups _metric_groups; public: bool wait_and_process(int timeout = 0, const sigset_t* active_sigmask = nullptr) { return _backend.wait_and_process(timeout, active_sigmask); @@ -1117,7 +1122,7 @@ size_t iovec_len(const iovec* begin, size_t len) inline future reactor::accept(pollable_fd_state& listenfd) { - return readable(listenfd).then([this, &listenfd] () mutable { + return readable(listenfd).then([&listenfd] () mutable { socket_address sa; socklen_t sl = sizeof(&sa.u.sas); file_desc fd = listenfd.fd.accept(sa.u.sa, sl, SOCK_NONBLOCK | SOCK_CLOEXEC); @@ -1419,6 +1424,8 @@ typename timer::time_point timer::get_timeout() { return _expiry; } -extern seastar::logger seastar_logger; +extern logger seastar_logger; + +} #endif /* REACTOR_HH_ */ diff --git a/core/report_exception.hh b/core/report_exception.hh index 65f1cb4eabb..af2de3b6e79 100644 --- a/core/report_exception.hh +++ b/core/report_exception.hh @@ -23,4 +23,9 @@ #include +namespace seastar { + void report_exception(std::experimental::string_view message, std::exception_ptr) noexcept; + +} + diff --git a/core/resource.cc b/core/resource.cc index 0a9127977eb..1b6fa49dbce 100644 --- a/core/resource.cc +++ b/core/resource.cc @@ -25,6 +25,8 @@ #include "resource.hh" #include "core/align.hh" +namespace seastar { + // Overload for boost program options parsing/validation void validate(boost::any& v, const std::vector& values, @@ -84,6 +86,8 @@ size_t calculate_memory(configuration c, size_t available_memory, float panic_fa } +} + #ifdef HAVE_HWLOC #include "util/defer.hh" @@ -92,6 +96,8 @@ size_t calculate_memory(configuration c, size_t available_memory, float panic_fa #include #include +namespace seastar { + cpu_set_t cpuid_to_cpuset(unsigned cpuid) { cpu_set_t cs; CPU_ZERO(&cs); @@ -329,11 +335,15 @@ unsigned nr_processing_units() { } +} + #else #include "resource.hh" #include +namespace seastar { + namespace resource { // Without hwloc, we don't support tuning the number of IO queues. So each CPU gets their. @@ -378,4 +388,6 @@ unsigned nr_processing_units() { } +} + #endif diff --git a/core/resource.hh b/core/resource.hh index 27dffc232ce..6f77634b504 100644 --- a/core/resource.hh +++ b/core/resource.hh @@ -30,6 +30,8 @@ #include #include +namespace seastar { + cpu_set_t cpuid_to_cpuset(unsigned cpuid); namespace resource { @@ -91,4 +93,7 @@ extern void validate(boost::any& v, const std::vector& values, cpuset_bpo_wrapper* target_type, int); + +} + #endif /* RESOURCE_HH_ */ diff --git a/core/rwlock.hh b/core/rwlock.hh index 492f22b4320..bf66be93126 100644 --- a/core/rwlock.hh +++ b/core/rwlock.hh @@ -24,6 +24,8 @@ #include "semaphore.hh" +namespace seastar { + /// \cond internal // lock / unlock semantics for rwlock, so it can be used with with_lock() class rwlock; @@ -141,4 +143,6 @@ inline void rwlock_for_write::unlock() { /// @} +} + #endif /* CORE_RWLOCK_HH_ */ diff --git a/core/scattered_message.hh b/core/scattered_message.hh index edb04feb8a4..109f3993834 100644 --- a/core/scattered_message.hh +++ b/core/scattered_message.hh @@ -30,6 +30,8 @@ #include #include +namespace seastar { + template class scattered_message { private: @@ -102,4 +104,6 @@ public: } }; +} + #endif diff --git a/core/scollectd-impl.hh b/core/scollectd-impl.hh index 6fa8319fd50..bb21705f2e8 100644 --- a/core/scollectd-impl.hh +++ b/core/scollectd-impl.hh @@ -25,6 +25,8 @@ #include "core/reactor.hh" #include "core/metrics_api.hh" +namespace seastar { + namespace scollectd { using namespace std::chrono_literals; @@ -84,3 +86,5 @@ private: impl & get_impl(); }; + +} diff --git a/core/scollectd.cc b/core/scollectd.cc index a561582ebfa..49cace8c313 100644 --- a/core/scollectd.cc +++ b/core/scollectd.cc @@ -34,6 +34,8 @@ #include "core/metrics_api.hh" #include "core/byteorder.hh" +namespace seastar { + bool scollectd::type_instance_id::operator<( const scollectd::type_instance_id& id2) const { auto& id1 = *this; @@ -53,7 +55,7 @@ bool scollectd::type_instance_id::operator==( namespace scollectd { -seastar::logger logger("scollectd"); +::seastar::logger logger("scollectd"); thread_local unsigned type_instance_id::_next_truncated_idx = 0; registration::~registration() { @@ -396,7 +398,7 @@ void impl::run() { std::get(*ctxt) = std::get(*ctxt)->second.begin(); } - auto stop_when = [this, ctxt, vals]() { + auto stop_when = [ctxt, vals]() { auto done = std::get(*ctxt) == vals->end(); return done; }; @@ -945,8 +947,10 @@ type_id type_id_for(known_type t) { } } -seastar::metrics::impl::value_map get_value_map() { - return seastar::metrics::impl::get_value_map(); +metrics::impl::value_map get_value_map() { + return metrics::impl::get_value_map(); +} + } } diff --git a/core/scollectd.hh b/core/scollectd.hh index 20533c95ab8..2fced1dbe18 100644 --- a/core/scollectd.hh +++ b/core/scollectd.hh @@ -43,6 +43,8 @@ #include "core/metrics_api.hh" +namespace seastar { + /** * Implementation of rudimentary collectd data gathering. * @@ -449,7 +451,7 @@ struct typed_value { private: type_id _type_id; scollectd::type_instance _type_instance; - ::shared_ptr _values; + shared_ptr _values; }; class plugin_instance_metrics { @@ -793,7 +795,7 @@ template Arg&& arg, bool enabled = true) { namespace sm = seastar::metrics::impl; shared_ptr rm = - ::make_shared(arg.type, sm::make_function(arg.value, arg.type), d, enabled); + make_shared(arg.type, sm::make_function(arg.value, arg.type), d, enabled); seastar::metrics::impl::get_local_impl()->add_registration(to_metrics_id(id), rm); return id; } @@ -843,11 +845,13 @@ template typed_value::typed_value(const type_id& tid, const scollectd::type_instance& ti, description d, Args&&... args) : _type_id(tid) , _type_instance(ti) - , _values(::make_shared(args)...))>(make_type_instance(std::move(d), std::forward(args)...))) + , _values(::seastar::make_shared(args)...))>(make_type_instance(std::move(d), std::forward(args)...))) {} // Send a message packet (string) future<> send_notification(const type_instance_id & id, const sstring & msg); }; +} + #endif /* SCOLLECTD_HH_ */ diff --git a/core/scollectd_api.hh b/core/scollectd_api.hh index 91ace8889e7..9baf7463f53 100644 --- a/core/scollectd_api.hh +++ b/core/scollectd_api.hh @@ -8,6 +8,8 @@ #include "core/scollectd.hh" #include "core/metrics_api.hh" +namespace seastar { + namespace scollectd { using collectd_value = seastar::metrics::impl::metric_value; @@ -28,7 +30,9 @@ bool is_enabled(const scollectd::type_instance_id& id); void enable(const scollectd::type_instance_id& id, bool enable); -seastar::metrics::impl::value_map get_value_map(); +metrics::impl::value_map get_value_map(); +} + } #endif /* CORE_SCOLLECTD_API_HH_ */ diff --git a/core/seastar.hh b/core/seastar.hh index 0654b99c23c..241dd214582 100644 --- a/core/seastar.hh +++ b/core/seastar.hh @@ -44,6 +44,8 @@ #include "sstring.hh" #include "future.hh" +namespace seastar { + // iostream.hh template class input_stream; template class output_stream; @@ -53,9 +55,7 @@ class server_socket; class connected_socket; class socket_address; class listen_options; -namespace seastar { enum class transport; -} // file.hh class file; @@ -121,7 +121,7 @@ future connect(socket_address sa); /// \param proto transport protocol (TCP or SCTP) /// /// \return a \ref connected_socket object, or an exception -future connect(socket_address sa, socket_address local, seastar::transport proto); +future connect(socket_address sa, socket_address local, transport proto); /// @} @@ -281,3 +281,5 @@ future<> link_file(sstring oldpath, sstring newpath); future file_system_at(sstring name); /// @} + +} diff --git a/core/semaphore.hh b/core/semaphore.hh index 4ec99465f13..8ad99b41275 100644 --- a/core/semaphore.hh +++ b/core/semaphore.hh @@ -29,6 +29,8 @@ #include "timer.hh" #include "expiring_fifo.hh" +namespace seastar { + /// \addtogroup fiber-module /// @{ @@ -416,4 +418,6 @@ using semaphore = basic_semaphore; /// @} +} + #endif /* CORE_SEMAPHORE_HH_ */ diff --git a/core/sharded.hh b/core/sharded.hh index 64913245adb..b15d6f944a9 100644 --- a/core/sharded.hh +++ b/core/sharded.hh @@ -79,7 +79,7 @@ public: template class sharded { struct entry { - ::shared_ptr service; + shared_ptr service; promise<> freed; }; std::vector _instances; @@ -163,7 +163,7 @@ public: map_reduce(Reducer&& r, Ret (Service::*func)(FuncArgs...), Args&&... args) -> typename reducer_traits::future_type { - return ::map_reduce(boost::make_counting_iterator(0), + return ::seastar::map_reduce(boost::make_counting_iterator(0), boost::make_counting_iterator(_instances.size()), [this, func, args = std::make_tuple(std::forward(args)...)] (unsigned c) mutable { return smp::submit_to(c, [this, func, args] () mutable { @@ -187,7 +187,7 @@ public: inline auto map_reduce(Reducer&& r, Func&& func) -> typename reducer_traits::future_type { - return ::map_reduce(boost::make_counting_iterator(0), + return ::seastar::map_reduce(boost::make_counting_iterator(0), boost::make_counting_iterator(_instances.size()), [this, &func] (unsigned c) mutable { return smp::submit_to(c, [this, func] () mutable { @@ -223,7 +223,7 @@ public: return map(*inst); }); }; - return ::map_reduce(smp::all_cpus().begin(), smp::all_cpus().end(), + return ::seastar::map_reduce(smp::all_cpus().begin(), smp::all_cpus().end(), std::move(wrapped_map), std::move(initial), std::move(reduce)); @@ -297,18 +297,18 @@ public: bool local_is_initialized(); private: - void track_deletion(::shared_ptr& s, std::false_type) { + void track_deletion(shared_ptr& s, std::false_type) { // do not wait for instance to be deleted since it is not going to notify us service_deleted(); } - void track_deletion(::shared_ptr& s, std::true_type) { + void track_deletion(shared_ptr& s, std::true_type) { s->_delete_cb = std::bind(std::mem_fn(&sharded::service_deleted), this); } template shared_ptr create_local_service(Args&&... args) { - auto s = ::make_shared(std::forward(args)...); + auto s = ::seastar::make_shared(std::forward(args)...); track_deletion(s, std::is_base_of, Service>()); return s; } @@ -452,8 +452,6 @@ inline bool sharded::local_is_initialized() { _instances[engine().cpu_id()].service; } -} - /// Smart pointer wrapper which makes it safe to move across CPUs. /// /// \c foreign_ptr<> is a smart pointer wrapper which, unlike @@ -514,6 +512,13 @@ public: }); } } + /// Creates a copy of this foreign ptr. Only works if the stored ptr is copyable. + future copy() const { + return smp::submit_to(_cpu, [this] () mutable { + auto v = _value; + return make_foreign(std::move(v)); + }); + } /// Accesses the wrapped object. element_type& operator*() const { return *_value; } /// Accesses the wrapped object. @@ -533,7 +538,9 @@ foreign_ptr make_foreign(T ptr) { } template -struct is_smart_ptr<::foreign_ptr> : std::true_type {}; +struct is_smart_ptr> : std::true_type {}; + +} /// @} diff --git a/core/shared_future.hh b/core/shared_future.hh index ba96dacdc3b..1d407255b59 100644 --- a/core/shared_future.hh +++ b/core/shared_future.hh @@ -28,6 +28,8 @@ /// \addtogroup future-module /// @{ +namespace seastar { + /// Changes the clock used by shared_future<> and shared_promise<> when passed as the first template parameter. template struct with_clock {}; @@ -248,3 +250,5 @@ public: }; /// @} + +} diff --git a/core/shared_ptr.hh b/core/shared_ptr.hh index 0d3898ab52d..4b8b0700c0e 100644 --- a/core/shared_ptr.hh +++ b/core/shared_ptr.hh @@ -30,6 +30,8 @@ #include "util/is_smart_ptr.hh" #include "util/indirect.hh" +namespace seastar { + // This header defines two shared pointer facilities, lw_shared_ptr<> and // shared_ptr<>, both modeled after std::shared_ptr<>. // @@ -211,7 +213,13 @@ public: T& operator*() const noexcept { return *_p->to_value(); } T* operator->() const noexcept { return _p->to_value(); } - T* get() const noexcept { return _p->to_value(); } + T* get() const noexcept { + if (_p) { + return _p->to_value(); + } else { + return nullptr; + } + } long int use_count() const noexcept { if (_p) { @@ -698,28 +706,34 @@ using shared_ptr_equal_by_value = indirect_equal_to>; template using shared_ptr_value_hash = indirect_hash>; +} + namespace std { template -struct hash> : private hash { - size_t operator()(const lw_shared_ptr& p) const { +struct hash> : private hash { + size_t operator()(const seastar::lw_shared_ptr& p) const { return hash::operator()(p.get()); } }; template -struct hash<::shared_ptr> : private hash { - size_t operator()(const ::shared_ptr& p) const { +struct hash> : private hash { + size_t operator()(const seastar::shared_ptr& p) const { return hash::operator()(p.get()); } }; } +namespace seastar { + template -struct is_smart_ptr<::shared_ptr> : std::true_type {}; +struct is_smart_ptr> : std::true_type {}; template -struct is_smart_ptr<::lw_shared_ptr> : std::true_type {}; +struct is_smart_ptr> : std::true_type {}; + +} #endif /* SHARED_PTR_HH_ */ diff --git a/core/shared_ptr_debug_helper.hh b/core/shared_ptr_debug_helper.hh index 869fb96348a..ab2295891aa 100644 --- a/core/shared_ptr_debug_helper.hh +++ b/core/shared_ptr_debug_helper.hh @@ -26,6 +26,8 @@ #include #include +namespace seastar { + // A counter that is only comfortable being incremented on the cpu // it was created on. Useful for verifying that a shared_ptr // or lw_shared_ptr isn't misued across cores. @@ -62,5 +64,7 @@ private: } }; +} + #endif diff --git a/core/slab.hh b/core/slab.hh index 9b356f019f4..30fd15c7391 100644 --- a/core/slab.hh +++ b/core/slab.hh @@ -34,9 +34,9 @@ #include "core/align.hh" #include "core/memory.hh" -static constexpr uint16_t SLAB_MAGIC_NUMBER = 0x51AB; // meant to be 'SLAB' :-) +namespace seastar { -namespace bi = boost::intrusive; +static constexpr uint16_t SLAB_MAGIC_NUMBER = 0x51AB; // meant to be 'SLAB' :-) /* * Item requirements @@ -52,8 +52,8 @@ namespace bi = boost::intrusive; */ struct slab_page_desc { private: - bi::list_member_hook<> _lru_link; - bi::list_member_hook<> _free_pages_link; + boost::intrusive::list_member_hook<> _lru_link; + boost::intrusive::list_member_hook<> _free_pages_link; void *_slab_page; std::vector _free_objects; uint32_t _refcnt; @@ -126,7 +126,7 @@ public: }; class slab_item_base { - bi::list_member_hook<> _lru_link; + boost::intrusive::list_member_hook<> _lru_link; template friend class slab_class; @@ -135,11 +135,11 @@ class slab_item_base { template class slab_class { private: - bi::list, + boost::intrusive::list, &slab_page_desc::_free_pages_link>> _free_slab_pages; - bi::list, + boost::intrusive::list, &slab_item_base::_lru_link>> _lru; size_t _size; // size of objects uint8_t _slab_class_id; @@ -282,8 +282,8 @@ private: // erase_func() is used to remove the item from the cache using slab. std::function _erase_func; std::vector _slab_pages_vector; - bi::list, + boost::intrusive::list, &slab_page_desc::_lru_link>> _slab_page_desc_lru; uint64_t _max_object_size; uint64_t _available_slab_pages; @@ -565,4 +565,6 @@ public: } }; +} + #endif /* __SLAB_ALLOCATOR__ */ diff --git a/core/sleep.hh b/core/sleep.hh index 405a88e91cd..f48d923317d 100644 --- a/core/sleep.hh +++ b/core/sleep.hh @@ -29,6 +29,7 @@ #include "core/reactor.hh" #include "core/future.hh" +namespace seastar { /// \file @@ -78,3 +79,5 @@ future<> sleep_abortable(std::chrono::duration dur) { } catch(condition_variable_timed_out&) {}; }); } + +} diff --git a/core/sstring.hh b/core/sstring.hh index 2bd165ad9ff..d3a4389e991 100644 --- a/core/sstring.hh +++ b/core/sstring.hh @@ -37,6 +37,8 @@ #include #include "core/temporary_buffer.hh" +namespace seastar { + template class basic_sstring; @@ -655,17 +657,21 @@ operator>>(std::basic_istream& is, return is; } +} + namespace std { template -struct hash> { - size_t operator()(const basic_sstring& s) const { +struct hash> { + size_t operator()(const seastar::basic_sstring& s) const { return std::hash>()(s); } }; } +namespace seastar { + static inline char* copy_str_to(char* dst) { return dst; @@ -690,6 +696,8 @@ inline string_type to_sstring(T value) { return sstring::to_sstring(value); } +} + namespace std { template inline diff --git a/core/stream.hh b/core/stream.hh index c6c9c248725..a7f26122103 100644 --- a/core/stream.hh +++ b/core/stream.hh @@ -27,6 +27,8 @@ #include #include +namespace seastar { + // A stream/subscription pair is similar to a promise/future pair, // but apply to a sequence of values instead of a single value. // @@ -235,4 +237,6 @@ subscription::done() { return _stream->_done.get_future(); } +} + #endif /* STREAM_HH_ */ diff --git a/core/systemwide_memory_barrier.cc b/core/systemwide_memory_barrier.cc index a0f8800978b..2a0ec12c313 100644 --- a/core/systemwide_memory_barrier.cc +++ b/core/systemwide_memory_barrier.cc @@ -24,6 +24,8 @@ #include #include +namespace seastar { + // cause all threads to invoke a full memory barrier void systemwide_memory_barrier() { @@ -47,3 +49,6 @@ systemwide_memory_barrier() { int r2 = mprotect(mem, getpagesize(), PROT_READ); assert(r2 == 0); } + +} + diff --git a/core/systemwide_memory_barrier.hh b/core/systemwide_memory_barrier.hh index abb09882ad6..a408310d6f2 100644 --- a/core/systemwide_memory_barrier.hh +++ b/core/systemwide_memory_barrier.hh @@ -21,6 +21,8 @@ #pragma once +namespace seastar { + /// \cond internal // cause all threads to invoke a full memory barrier @@ -28,3 +30,6 @@ void systemwide_memory_barrier(); /// \endcond + +} + diff --git a/core/task.hh b/core/task.hh index e510f2af065..05955013632 100644 --- a/core/task.hh +++ b/core/task.hh @@ -23,6 +23,8 @@ #include +namespace seastar { + class task { public: virtual ~task() noexcept {} @@ -47,3 +49,5 @@ std::unique_ptr make_task(Func&& func) { return std::make_unique>(std::forward(func)); } + +} diff --git a/core/temporary_buffer.hh b/core/temporary_buffer.hh index 8f7cc3d2172..c0dd9e6d426 100644 --- a/core/temporary_buffer.hh +++ b/core/temporary_buffer.hh @@ -27,6 +27,8 @@ #include #include +namespace seastar { + /// \addtogroup memory-module /// @{ @@ -222,4 +224,6 @@ public: /// @} +} + #endif /* TEMPORARY_BUFFER_HH_ */ diff --git a/core/thread.cc b/core/thread.cc index 1df156affb9..a923364aeb8 100644 --- a/core/thread.cc +++ b/core/thread.cc @@ -260,10 +260,10 @@ thread_context::yield() { } bool thread::try_run_one_yielded_thread() { - if (seastar::thread_context::_preempted_threads.empty()) { + if (thread_context::_preempted_threads.empty()) { return false; } - auto&& t = seastar::thread_context::_preempted_threads.front(); + auto&& t = thread_context::_preempted_threads.front(); t._sched_timer.cancel(); t._sched_promise->set_value(); thread_context::_preempted_threads.pop_front(); diff --git a/core/thread.hh b/core/thread.hh index fa7938803eb..26c2845129b 100644 --- a/core/thread.hh +++ b/core/thread.hh @@ -70,7 +70,6 @@ namespace seastar { namespace stdx = std::experimental; -namespace bi = boost::intrusive; /// \addtogroup thread-module /// @{ @@ -107,17 +106,17 @@ class thread_context { timer<> _sched_timer{[this] { reschedule(); }}; stdx::optional> _sched_promise; - bi::list_member_hook<> _preempted_link; - using preempted_thread_list = bi::list, + boost::intrusive::list_member_hook<> _preempted_link; + using preempted_thread_list = boost::intrusive::list, &thread_context::_preempted_link>, - bi::constant_time_size>; + boost::intrusive::constant_time_size>; - bi::list_member_hook<> _all_link; - using all_thread_list = bi::list, + boost::intrusive::list_member_hook<> _all_link; + using all_thread_list = boost::intrusive::list, &thread_context::_all_link>, - bi::constant_time_size>; + boost::intrusive::constant_time_size>; static thread_local preempted_thread_list _preempted_threads; static thread_local all_thread_list _all_threads; @@ -193,10 +192,10 @@ public: static bool should_yield(); static bool running_in_thread() { - return seastar::thread_impl::get() != nullptr; + return thread_impl::get() != nullptr; } private: - friend class ::reactor; + friend class reactor; // To be used by seastar reactor only. static bool try_run_one_yielded_thread(); }; diff --git a/core/timer-set.hh b/core/timer-set.hh index 7a29906bed7..3a6ac45c14f 100644 --- a/core/timer-set.hh +++ b/core/timer-set.hh @@ -21,9 +21,8 @@ #include #include "bitset-iter.hh" -namespace bi = boost::intrusive; - namespace seastar { + /** * A data structure designed for holding and expiring timers. It's * optimized for timer non-delivery by deferring sorting cost until @@ -35,11 +34,11 @@ namespace seastar { * get_timeout() which returns Timer::time_point which denotes * timer's expiration. */ -template Timer::*link> +template Timer::*link> class timer_set { public: using time_point = typename Timer::time_point; - using timer_list_t = bi::list, link>>; + using timer_list_t = boost::intrusive::list, link>>; private: using duration = typename Timer::duration; using timestamp_t = typename Timer::duration::rep; diff --git a/core/timer.hh b/core/timer.hh index ea7f118f598..3fdb180b7d6 100644 --- a/core/timer.hh +++ b/core/timer.hh @@ -28,6 +28,8 @@ #include "future.hh" #include "timer-set.hh" +namespace seastar { + using steady_clock_type = std::chrono::steady_clock; template @@ -67,6 +69,8 @@ public: bool cancel(); time_point get_timeout(); friend class reactor; - friend class seastar::timer_set; + friend class timer_set; }; +} + diff --git a/core/transfer.hh b/core/transfer.hh index c04fff80f1b..d912e52bc85 100644 --- a/core/transfer.hh +++ b/core/transfer.hh @@ -37,6 +37,8 @@ #include #include +namespace seastar { + template inline void @@ -69,4 +71,6 @@ transfer_pass2(Alloc& a, T* from, T* to, a.destroy(from); } +} + #endif /* TRANSFER_HH_ */ diff --git a/core/unaligned.hh b/core/unaligned.hh index d9fb92c0416..470390c6e0e 100644 --- a/core/unaligned.hh +++ b/core/unaligned.hh @@ -46,6 +46,8 @@ // cause the sanitizer not to generate runtime alignment checks for this // access. +namespace seastar { + template struct unaligned { T raw; @@ -67,3 +69,5 @@ template inline auto unaligned_cast(const F* p) { return reinterpret_cast>*>(p); } + +} diff --git a/core/units.hh b/core/units.hh index cca368b5bb2..8bb503b761d 100644 --- a/core/units.hh +++ b/core/units.hh @@ -22,8 +22,12 @@ #ifndef UNITS_HH_ #define UNITS_HH_ +namespace seastar { + static constexpr size_t KB = 1 << 10; static constexpr size_t MB = 1 << 20; static constexpr size_t GB = 1 << 30; +} + #endif diff --git a/core/vector-data-sink.hh b/core/vector-data-sink.hh index c45b0f93fa6..76cb46c6727 100644 --- a/core/vector-data-sink.hh +++ b/core/vector-data-sink.hh @@ -24,6 +24,8 @@ #include "core/reactor.hh" +namespace seastar { + class vector_data_sink final : public data_sink_impl { public: using vector_type = std::vector; @@ -43,4 +45,6 @@ public: } }; +} + #endif diff --git a/core/vla.hh b/core/vla.hh index e6cd71eee5a..11a569b640b 100644 --- a/core/vla.hh +++ b/core/vla.hh @@ -28,6 +28,8 @@ #include #include +namespace seastar { + // Some C APIs have a structure with a variable length array at the end. // This is a helper function to help allocate it. // @@ -56,6 +58,6 @@ make_struct_with_vla(E S::*last, size_t nr) { return s; } - +} #endif /* VLA_HH_ */ diff --git a/core/weak_ptr.hh b/core/weak_ptr.hh index df3d930a885..272696bfabb 100644 --- a/core/weak_ptr.hh +++ b/core/weak_ptr.hh @@ -23,6 +23,8 @@ #include +namespace seastar { + template class weak_ptr { template @@ -94,3 +96,6 @@ public: return ptr; } }; + +} + diff --git a/core/xen/evtchn.cc b/core/xen/evtchn.cc index 4730870d3f3..752a5218002 100644 --- a/core/xen/evtchn.cc +++ b/core/xen/evtchn.cc @@ -27,6 +27,8 @@ #include "evtchn.hh" #include "osv_xen.hh" +namespace seastar { + namespace xen { void evtchn::make_ready_port(int port) { @@ -217,3 +219,5 @@ evtchn *evtchn::instance(bool userspace, unsigned otherend) } } + +} diff --git a/core/xen/evtchn.hh b/core/xen/evtchn.hh index 63d59c34d40..2335cfd7c0c 100644 --- a/core/xen/evtchn.hh +++ b/core/xen/evtchn.hh @@ -21,6 +21,8 @@ #include "core/posix.hh" #include "core/future.hh" +namespace seastar { + namespace xen { class evtchn; @@ -64,4 +66,6 @@ public: } +} + #endif diff --git a/core/xen/gntalloc.cc b/core/xen/gntalloc.cc index c75b347dcd4..dfb8adac55e 100644 --- a/core/xen/gntalloc.cc +++ b/core/xen/gntalloc.cc @@ -25,6 +25,8 @@ #include "osv_xen.hh" #include "gntalloc.hh" +namespace seastar { + namespace xen { gntref invalid_ref; @@ -237,3 +239,5 @@ gntalloc *gntalloc::instance() { } } + +} diff --git a/core/xen/gntalloc.hh b/core/xen/gntalloc.hh index 79fb2ed6c87..e6d94de520d 100644 --- a/core/xen/gntalloc.hh +++ b/core/xen/gntalloc.hh @@ -20,6 +20,8 @@ #include "core/posix.hh" +namespace seastar { + namespace xen { class gntref { @@ -66,4 +68,6 @@ extern gntref invalid_ref; } +} + #endif diff --git a/doc/tutorial.md b/doc/tutorial.md index b7900ca1b21..1cbbf32b75e 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -14,13 +14,13 @@ Seastar offers a complete asynchronous programming framework, which uses two con Since modern multi-core and multi-socket machines have steep penalties for sharing data between cores (atomic instructions, cache line bouncing and memory fences), Seastar programs use the share-nothing programming model, i.e., the available memory is divided between the cores, each core works on data in its own part of memory, and communication between cores happens via explicit message passing (which itself happens using the SMP's shared memory hardware, of course). . ## Asynchronous programming -A server for a network protocol, such as the classic HTTP (Web) or SMTP (e-mail) servers, inherently deals with parallelism: Multiple clients send requests in parallel, and we cannot start handling one request before finishing to handle the next. A request may, and often does, need to block because of various reasons --- a full TCP window (i.e., a slow connection), disk I/O, or even the client holding on to an inactive connection --- and the server needs to handle other connections as well. +A server for a network protocol, such as the classic HTTP (Web) or SMTP (e-mail) servers, inherently deals with parallelism: Multiple clients send requests in parallel, and we cannot finish handling one request before starting to handle the next: A request may, and often does, need to block because of various reasons --- a full TCP window (i.e., a slow connection), disk I/O, or even the client holding on to an inactive connection --- and the server needs to handle other connections as well. -The most straightforward way to handle such parallel connections, employed by classic network servers such as Inetd, Apache Httpd and Sendmail, is to use a separate operating-system process per connection. This technique evolved over the years to improve its performance: At first, a new process was spawned to handle each new connection; Later, a pool of existing processes was kept and each new connection was assigned to an unemployed process from the pool; Finally, the processes were replaced by threads. However, the common idea behind all these implementations is that at each moment, each process handles exclusively a single connection. Therefore, the server code is free to use blocking system calls, such as reading or writing to a connection, or reading from disk, and if this process blocks, all is well because we have many additional processes handling other connections in parallel. +The most straightforward way to handle such parallel connections, employed by classic network servers such as Inetd, Apache Httpd and Sendmail, is to use a separate operating-system process per connection. This technique evolved over the years to improve its performance: At first, a new process was spawned to handle each new connection; Later, a pool of existing processes was kept and each new connection was assigned to an unemployed process from the pool; Finally, the processes were replaced by threads. However, the common idea behind all these implementations is that at each moment, each process handles exclusively a single connection. Therefore, the server code is free to use blocking system calls, such as reading or writing to a connection, or reading from disk, and if this process blocks, all is well because we have many additional processes ready to handle other connections. -Programming a server which uses a process (or a thread) per connection is known as *synchronous* programming, because the code is written linearly, and one line of code starting to run after the previous line finished. For example, the code may read a request from a socket, parse the request, and then piecemeal read a file from disk and write it back to the socket. Such code is easy to write, almost like traditional non-parallel programs. In fact, it's even possible to run an external non-parallel program to handle each request --- this is for example how Apache HTTPd ran "CGI" programs, the first implementation of dynamic Web-page generation. +Programming a server which uses a process (or a thread) per connection is known as *synchronous* programming, because the code is written linearly, and one line of code starts to run after the previous line finished. For example, the code may read a request from a socket, parse the request, and then piecemeal read a file from disk and write it back to the socket. Such code is easy to write, almost like traditional non-parallel programs. In fact, it's even possible to run an external non-parallel program to handle each request --- this is for example how Apache HTTPd ran "CGI" programs, the first implementation of dynamic Web-page generation. ->NOTE: although the synchronous server application is written in a linear, non-parallel, fashion, behind the scenes the kernel helps ensure that everything happens in parallel and the machine's resources --- CPUs, disk and network --- are fully utilized. Beyond the obvious parallelism (we have multiple processes handling multiple connections in parallel), the kernel may even parallelize the work of one individual connection --- for example process an outstanding disk request (e.g., read from a disk file) in parallel with handling the network connection (send buffered-but-yet-unsent data, and buffer newly-received data until the application is ready to read it). +>NOTE: although the synchronous server application is written in a linear, non-parallel, fashion, behind the scenes the kernel helps ensure that everything happens in parallel and the machine's resources --- CPUs, disk and network --- are fully utilized. Beyond the process parallelism (we have multiple processes handling multiple connections in parallel), the kernel may even parallelize the work of one individual connection --- for example process an outstanding disk request (e.g., read from a disk file) in parallel with handling the network connection (send buffered-but-yet-unsent data, and buffer newly-received data until the application is ready to read it). But synchronous, process-per-connection, server programming didn't come without disadvantages and costs. Slowly but surely, server authors realized that starting a new process is slow, context switching is slow, and each process comes with significant overheads --- most notably the size of its stack. Server and kernel authors worked hard to mitigate these overheads: They switched from processes to threads, from creating new threads to thread pools, they lowered default stack size of each thread, and increased the virtual memory size to allow more partially-utilized stacks. But still, servers with synchronous designs had unsatisfactory performance, and scaled badly as the number of concurrent connections grew. In 1999, Dan Kigel popularized "the C10K problem", the need of a single server to efficiently handle 10,000 concurrent connections --- most of them slow or even inactive. @@ -67,19 +67,20 @@ The simplest Seastar program is this: #include int main(int argc, char** argv) { - app_template app; + seastar::app_template app; app.run(argc, argv, [] { std::cout << "Hello world\n"; - return make_ready_future<>(); + return seastar::make_ready_future<>(); }); } ``` As we do in this example, each Seastar program must define and run, an `app_template` object. This object starts the main event loop (the Seastar *engine*) on one or more CPUs, and then runs the given function - in this case an unnamed function, a *lambda* - once. - The "`return make_ready_future<>();`" causes the event loop, and the whole application, to exit immediately after printing the "Hello World" message. In a more typical Seastar application, we will want event loop to remain alive and process incoming packets (for example), until explicitly exited. Such applications will return a _future_ which determines when to exit the application. We will introduce futures and how to use them below. In any case, the regular C `exit()` should not be used, because it prevents Seastar or the application from cleaning up appropriately. +As shown in this example, all Seastar functions and types live in the "`seastar`" namespace. An user can either type this namespace prefix every time, or use shortcuts like "`using seastar::app_template`" or even "`using namespace seastar`" to avoid typing this prefix. We generally recommend to use the namespace prefixes `seastar` and `std` explicitly, and will will follow this style in all the examples below. + To compile this program, first make sure you have downloaded and built Seastar. Below we'll use the symbol `$SEASTAR` to refer to the directory where Seastar was built (Seastar doesn't yet have a "`make install`" feature). Now, put the above program in a source file anywhere you want, let's call the file `getting-started.cc`. You can compile it with the following command: @@ -99,7 +100,7 @@ $ # Threads and memory ## Seastar threads -As explained in the introduction, Seastar-based programs run a single thread on each CPU. Each of these threads runs its own event loop, known as the *engine* in Seastar nomenclature. By default, the Seastar application will take over all the available cores, starting one thread per core. We can see this with the following program, printing `smp::count` which is the number of started threads: +As explained in the introduction, Seastar-based programs run a single thread on each CPU. Each of these threads runs its own event loop, known as the *engine* in Seastar nomenclature. By default, the Seastar application will take over all the available cores, starting one thread per core. We can see this with the following program, printing `seastar::smp::count` which is the number of started threads: ```cpp #include "core/app-template.hh" @@ -107,10 +108,10 @@ As explained in the introduction, Seastar-based programs run a single thread on #include int main(int argc, char** argv) { - app_template app; + seastar::app_template app; app.run(argc, argv, [] { - std::cout << smp::count << "\n"; - return make_ready_future<>(); + std::cout << seastar::smp::count << "\n"; + return seastar::make_ready_future<>(); }); } ``` @@ -144,16 +145,15 @@ The error is an exception thrown from app.run, which we did not catch, leading t ```cpp #include "core/app-template.hh" #include "core/reactor.hh" -#include "util/log.hh" #include #include int main(int argc, char** argv) { - app_template app; + seastar::app_template app; try { app.run(argc, argv, [] { - std::cout << smp::count << "\n"; - return make_ready_future<>(); + std::cout << seastar::smp::count << "\n"; + return seastar::make_ready_future<>(); }); } catch(...) { std::cerr << "Failed to start application: " @@ -213,18 +213,18 @@ A **continuation** is a callback (typically a lambda) to run when a future becom #include int main(int argc, char** argv) { - app_template app; + seastar::app_template app; app.run(argc, argv, [] { std::cout << "Sleeping... " << std::flush; using namespace std::chrono_literals; - return sleep(1s).then([] { + return seastar::sleep(1s).then([] { std::cout << "Done.\n"; }); }); } ``` -In this example we see us getting a future from `sleep(1s)`, and attaching to it a continuation which prints a "Done." message. The future will become available after 1 second has passed, at which point the continuation is executed. Running this program, we indeed see the message "Sleeping..." immediately, and one second later the message "Done." appears and the program exits. +In this example we see us getting a future from `seastar::sleep(1s)`, and attaching to it a continuation which prints a "Done." message. The future will become available after 1 second has passed, at which point the continuation is executed. Running this program, we indeed see the message "Sleeping..." immediately, and one second later the message "Done." appears and the program exits. The return value of `then()` is itself a future which is useful for chaining multiple continuations one after another, as we will explain below. But here we just note that we `return` this future from `app.run`'s function, so that the program will exit only after both the sleep and its continuation are done. @@ -236,10 +236,10 @@ To avoid repeating the boilerplate "app_engine" part in every code example in th #include #include -extern future<> f(); +extern seastar::future<> f(); int main(int argc, char** argv) { - app_template app; + seastar::app_template app; try { app.run(argc, argv, f); } catch(...) { @@ -257,10 +257,10 @@ Compiling together with this `main.cc`, the above sleep() example code becomes: #include "core/sleep.hh" #include -future<> f() { +seastar::future<> f() { std::cout << "Sleeping... " << std::flush; using namespace std::chrono_literals; - return sleep(1s).then([] { + return seastar::sleep(1s).then([] { std::cout << "Done.\n"; }); } @@ -272,12 +272,12 @@ So far, this example was not very interesting - there is no parallelism, and the #include "core/sleep.hh" #include -future<> f() { +seastar::future<> f() { std::cout << "Sleeping... " << std::flush; using namespace std::chrono_literals; - sleep(200ms).then([] { std::cout << "200ms " << std::flush; }); - sleep(100ms).then([] { std::cout << "100ms " << std::flush; }); - return sleep(1s).then([] { std::cout << "Done.\n"; }); + seastar::sleep(200ms).then([] { std::cout << "200ms " << std::flush; }); + seastar::sleep(100ms).then([] { std::cout << "100ms " << std::flush; }); + return seastar::sleep(1s).then([] { std::cout << "Done.\n"; }); } ``` @@ -293,12 +293,12 @@ Sleeping... 100ms 200ms Done. #include "core/sleep.hh" #include -future slow() { +seastar::future slow() { using namespace std::chrono_literals; - return sleep(100ms).then([] { return 3; }); + return seastar::sleep(100ms).then([] { return 3; }); } -future<> f() { +seastar::future<> f() { return slow().then([] (int val) { std::cout << "Got " << val << "\n"; }); @@ -320,11 +320,11 @@ This optimization is done *usually*, though sometimes it is avoided: The impleme #include "core/future.hh" #include -future fast() { +seastar::future fast() { return make_ready_future(3); } -future<> f() { +seastar::future<> f() { return fast().then([] (int val) { std::cout << "Got " << val << "\n"; }); @@ -340,56 +340,58 @@ We've already seen that Seastar *continuations* are lambdas, passed to the `then #include "core/sleep.hh" #include -future incr(int i) { +seastar::future incr(int i) { using namespace std::chrono_literals; - return sleep(10ms).then([i] { return i + 1; }); + return seastar::sleep(10ms).then([i] { return i + 1; }); } -future<> f() { +seastar::future<> f() { return incr(3).then([] (int val) { std::cout << "Got " << val << "\n"; }); } ``` -The future operation `incr(i)` takes some time to complete (it needs to sleep a bit first :wink:), and in that duration, it needs to save the `i` value it is working on. In the early event-driven programming models, the programmer needed to explicitly define an object for holding this state, and to manage all these objects. Everything is much simpler in Seastar, with C++11's lambdas: The *capture syntax* `[i]` in the above example means that the value of i, as it existed when incr() was called() is captured into the lambda. The lambda is not just a function - it is in fact an *object*, with both code and data. In essence, the compiler created for us automatically the state object, and we neither need to define it, nor to keep track of it (it gets saved together with the continuation, when the continuation is deferred, and gets deleted automatically after the continuation runs). +The future operation `incr(i)` takes some time to complete (it needs to sleep a bit first...), and in that duration, it needs to save the `i` value it is working on. In the early event-driven programming models, the programmer needed to explicitly define an object for holding this state, and to manage all these objects. Everything is much simpler in Seastar, with C++11's lambdas: The *capture syntax* "`[i]`" in the above example means that the value of i, as it existed when incr() was called() is captured into the lambda. The lambda is not just a function - it is in fact an *object*, with both code and data. In essence, the compiler created for us automatically the state object, and we neither need to define it, nor to keep track of it (it gets saved together with the continuation, when the continuation is deferred, and gets deleted automatically after the continuation runs). -One implementation detail worth understanding is that when a continuation has captured state and is run immediately, this capture incurs no runtime overhead. However, when the continuation cannot be run immediately (because the future is not yet ready) and needs to be saved till later, memory needs to be allocated on the heap for this data, and the continuation's captured data needs to be copied there. This has runtime overhead, but it is unavoidable, and is very small compared to the parallel overhead in the threaded programming model (in a threaded program, this sort of state usually resides on the stack of the blocked thread, but the stack is much larger than our tiny capture state, takes up a lot of memory and causes a lot of cache pollution on context switches between those threads). +One implementation detail worth understanding is that when a continuation has captured state and is run immediately, this capture incurs no runtime overhead. However, when the continuation cannot be run immediately (because the future is not yet ready) and needs to be saved till later, memory needs to be allocated on the heap for this data, and the continuation's captured data needs to be copied there. This has runtime overhead, but it is unavoidable, and is very small compared to the related overhead in the threaded programming model (in a threaded program, this sort of state usually resides on the stack of the blocked thread, but the stack is much larger than our tiny capture state, takes up a lot of memory and causes a lot of cache pollution on context switches between those threads). In the above example, we captured `i` *by value* - i.e., a copy of the value of `i` was saved into the continuation. C++ has two additional capture options: capturing by *reference* and capturing by *move*: -Using capture-by-reference in a continuation is almost always a mistake, and would lead to serious bugs. For example, if in the above example we captured a reference to i, instead of copying it, +Using capture-by-reference in a continuation is usually a mistake, and can lead to serious bugs. For example, if in the above example we captured a reference to i, instead of copying it, ```cpp -future incr(int i) { +seastar::future incr(int i) { using namespace std::chrono_literals; - return sleep(10ms).then([&i] { return i + 1; }); // Oops, the "&" here is wrong. + // Oops, the "&" below is wrong: + return seastar::sleep(10ms).then([&i] { return i + 1; }); } ``` this would have meant that the continuation would contain the address of `i`, not its value. But `i` is a stack variable, and the incr() function returns immediately, so when the continuation eventually gets to run, long after incr() returns, this address will contain unrelated content. -An exception to this rule is the `do_with()` idiom, which we will introduce later, which ensures that an object lives throughout the life of the continuation. This makes capture-by-reference possible, and very convenient. +An exception to the capture-by-reference-is-usually-a-mistake rule is the `do_with()` idiom, which we will introduce later. This idiom ensures that an object lives throughout the life of the continuation, and makes capture-by-reference possible, and very convenient. -Using capture-by-*move* in continuations, on the other hand, is valid and very useful in Seastar applications. By **moving** an object into a continuation, we transfer ownership of this object to the continuation, and make it easy for the object to be automatically deleted when the continuation ends. For example, consider a traditional function taking a std::unique_ptr. +Using capture-by-*move* in continuations is also very useful in Seastar applications. By **moving** an object into a continuation, we transfer ownership of this object to the continuation, and make it easy for the object to be automatically deleted when the continuation ends. For example, consider a traditional function taking a std::unique_ptr. ```cpp int do_something(std::unique_ptr obj) { // do some computation based on the contents of obj, let's say the result is 17 return 17; // at this point, obj goes out of scope so the compiler delete()s it. ``` -By using unique_ptr in this way, the caller passes an object to the function, but tells it the object is now its exclusive responsibility - and when the function is done with the object, it should delete the object. How do we use unique_ptr in a continuation? The following won't work: +By using unique_ptr in this way, the caller passes an object to the function, but tells it the object is now its exclusive responsibility - and when the function is done with the object, it automatically deletes it. How do we use unique_ptr in a continuation? The following won't work: ```cpp -future slow_do_something(std::unique_ptr obj) { +seastar::future slow_do_something(std::unique_ptr obj) { using namespace std::chrono_literals; - return sleep(10ms).then([obj] { return do_something(std::move(obj))}); // WON'T COMPILE + // The following line won't compile... + return seastar::sleep(10ms).then([obj] { return do_something(std::move(obj))}); } ``` The problem is that a unique_ptr cannot be passed into a continuation by value, as this would require copying it, which is forbidden because it violates the guarantee that only one copy of this pointer exists. We can, however, *move* obj into the continuation: ```cpp -future slow_do_something(std::unique_ptr obj) { +seastar::future slow_do_something(std::unique_ptr obj) { using namespace std::chrono_literals; - return sleep(10ms).then([obj = std::move(obj)] { + return seastar::sleep(10ms).then([obj = std::move(obj)] { return do_something(std::move(obj))}); } ``` @@ -449,11 +451,11 @@ class my_exception : public std::exception { virtual const char* what() const noexcept override { return "my exception"; } }; -future<> fail() { - return make_exception_future<>(my_exception()); +seastar::future<> fail() { + return seastar::make_exception_future<>(my_exception()); } -future<> f() { +seastar::future<> f() { return fail().finally([] { std::cout << "cleaning up\n"; }); @@ -465,7 +467,7 @@ This code will, as expected, print the "cleaning up" message - the asynchronous Now consider that in the above example we had a different definition for `fail()`: ```cpp -future<> fail() { +seastar::future<> fail() { throw my_exception(); } ```` @@ -478,13 +480,13 @@ We recommend that to reduce the chance for such errors, asynchronous functions s void inner() { throw my_exception(); } -future<> fail() { +seastar::future<> fail() { try { inner(); } catch(...) { - return make_exception_future(std::current_exception()); + return seastar::make_exception_future(std::current_exception()); } - return make_ready_future<>(); + return seastar::make_ready_future<>(); } ``` @@ -493,11 +495,11 @@ Here, `fail()` catches the exception thrown by `inner()`, whatever it might be, Seastar has a convenient generic function, `futurize_apply()`, which can be useful here. `futurize_apply(func, args...)` runs a function which may return either a future value or an immediate value, and in both cases convert the result into a future value. `futurize_apply()` also converts an immediate exception thrown by the function, if any, into a failed future, just like we did above. So using `futurize_apply()` we can make the above example work even if `fail()` did throw exceptions: ```cpp -future<> fail() { +seastar::future<> fail() { throw my_exception(); } -future<> f() { - return futurize_apply(fail).finally([] { +seastar::future<> f() { + return seastar::futurize_apply(fail).finally([] { std::cout << "cleaning up\n"; }); } @@ -506,8 +508,8 @@ future<> f() { Note that most of this discussion becomes moot if the risk of exception is inside a _continuation_. Consider the following code: ```cpp -future<> f() { - return sleep(1s).then([] { +seastar::future<> f() { + return seastar::sleep(1s).then([] { throw my_exception(); }).finally([] { std::cout << "cleaning up\n"; @@ -538,8 +540,8 @@ A common use for a semaphore in Seastar is for limiting parallelism, i.e., limit Consider a case where an external source of events (e.g., incoming network requests) causes an asynchronous function ```g()``` to be called. Imagine that we want to limit the number of concurrent ```g()``` operations to 100. I.e., If g() is started when 100 other invocations are still ongoing, we want it to delay its real work until one of the other invocations has completed. We can do this with a semaphore: ```c++ -future<> g() { - static thread_local semaphore limit(100); +seastar::future<> g() { + static thread_local seastar::semaphore limit(100); return limit.wait(1).then([] { return slow(); // do the real work of g() }).finally([] { @@ -557,8 +559,8 @@ Unfortunately, the above example code is actually _incorrect_. Counter-intuitive To solve this problem, we need the `finally()` to chain to the `slow()` call only, not to `limit.wait(1)`: ```c++ -future<> g() { - static thread_local semaphore limit(100); +seastar::future<> g() { + static thread_local seastar::semaphore limit(100); return limit.wait(1).then([] { return slow().finally([] { limit.signal(1); }); }); @@ -571,9 +573,9 @@ To correctly support the case that `slow()` throws an exception, and also the ca ```c++ future<> g() { - static thread_local semaphore limit(100); + static thread_local seastar::semaphore limit(100); return limit.wait(1).then([] { - return futurize_apply(slow).finally([] { limit.signal(1); }); + return seastar::futurize_apply(slow).finally([] { limit.signal(1); }); }); } ``` @@ -586,9 +588,9 @@ As we saw now, it is not easy to safely use the separate `semaphore::wait()` and The lambda-based solution is an exception-safe shortcut ```with_semaphore()``` that replaces exactly the last example above: ```c++ -future<> g() { - static thread_local semaphore limit(100); - return with_semaphore(limit, 1, [] { +seastar::future<> g() { + static thread_local seastar::semaphore limit(100); + return seastar::with_semaphore(limit, 1, [] { return slow(); // do the real work of g() }); } @@ -599,10 +601,10 @@ future<> g() { The function `get_units()` provides a safer alternative to `semaphore`'s separate `wait()` and `signal()` functions, based on C++'s RAII philosophy: It returns an opaque units object, which while held, keeps the semaphore's counter decreased - and as soon as this object is destructed, the counter is increased back. With this interface you cannot forget to increase the counter, or increase it twice, or increase without decreasing: The counter will always be decreased once when the units object is created, and if that succeeded, increased when the object is destructed. When the units object is moved into a continuation, no matter how this continuation ends, when the continuation is destructed, the units object is destructed and the units are returned to the semaphore's counter. The above examples, written with `get_units()`, looks like this: ```c++ -future<> g() { +seastar::future<> g() { static thread_local semaphore limit(100); - return get_units(limit, 1).then([] (auto units) { - return futurize_apply(slow).finally([units = std::move(units)] {}); + return seastar::get_units(limit, 1).then([] (auto units) { + return seastar::futurize_apply(slow).finally([units = std::move(units)] {}); }); } ``` @@ -611,9 +613,9 @@ future<> g() { Because semaphores support waiting for any number of units, not just 1, we can use them for more than simple limiting of the *number* of parallel invocation. For example, consider we have an asynchronous function ```using_lots_of_memory(size_t bytes)```, which uses ```bytes``` bytes of memory, and we want to ensure that not more than 1 MB of memory is used by all parallel invocations of this function --- and that additional calls are delayed until previous calls have finished. We can do this with a semaphore: ```c++ -future<> using_lots_of_memory(size_t bytes) { - static thread_local semaphore limit(1000000); // limit to 1MB - return with_semaphore(limit, bytes, [bytes] { +seastar::future<> using_lots_of_memory(size_t bytes) { + static thread_local seastar::semaphore limit(1000000); // limit to 1MB + return seastar::with_semaphore(limit, bytes, [bytes] { // do something allocating 'bytes' bytes of memory }); } @@ -627,13 +629,13 @@ Consider the following simple loop: ```cpp #include "core/sleep.hh" -future<> slow() { +seastar::future<> slow() { std::cerr << "."; - return sleep(std::chrono::seconds(1)); + return seastar::sleep(std::chrono::seconds(1)); } -future<> f() { - return repeat([] { - return slow().then([] { return stop_iteration::no; }); +seastar::future<> f() { + return seastar::repeat([] { + return slow().then([] { return seastar::stop_iteration::no; }); }); } ``` @@ -642,27 +644,27 @@ This loop runs the ```slow()``` function (taking one second to complete) without Naively, we could achieve more parallelism, by starting the next call to ```slow()``` right after the previous call --- ignoring the future returned by the previous call to ```slow()``` and not waiting for it to resolve: ```cpp -future<> f() { - return repeat([] { +seastar::future<> f() { + return seastar::repeat([] { slow(); - return stop_iteration::no; + return seastar::stop_iteration::no; }); } ``` -But in this loop, there is no limit to the amount of parallelism --- millions of ```sleep()``` calls might be active in parallel, before the first one ever returned. Eventually, this loop will consume all available memory and crash. +But in this loop, there is no limit to the amount of parallelism --- millions of ```sleep()``` calls might be active in parallel, before the first one ever returned. Eventually, this loop may consume all available memory and crash. Using a semaphore allows us to run many instances of ```slow()``` in parallel, but limit the number of these parallel instances to, in the following example, 100: ```cpp -future<> f() { - return do_with(semaphore(100), [] (auto& limit) { - return repeat([&limit] { +seastar::future<> f() { + return seastar::do_with(seastar::semaphore(100), [] (auto& limit) { + return seastar::repeat([&limit] { return limit.wait(1).then([&limit] { slow().finally([&limit] { limit.signal(1); }); - return stop_iteration::no; + return seastar::stop_iteration::no; }); }); }); @@ -674,9 +676,9 @@ Note how in this code we do not wait for `slow()` to complete before continuing The above example is incomplete, because it has a never-ending loop and the future returned by `f()` will never resolve. In more realistic cases, the loop has an end, and at the end of the loop we need to wait for all the background operations which the loop started. We can do this by ```wait()```ing on the original count of the semaphore: When the full count is finally available, it means that *all* the operations have completed. For example, the following loop ends after 456 iterations: ```cpp -future<> f() { - return do_with(semaphore(100), [] (auto& limit) { - return do_for_each(boost::irange(0, 456), [&limit] (int i) { +seastar::future<> f() { + return seastar::do_with(seastar::semaphore(100), [] (auto& limit) { + return seastar::do_for_each(boost::irange(0, 456), [&limit] (int i) { return limit.wait(1).then([&limit] { slow().finally([&limit] { limit.signal(1); }); }); @@ -692,10 +694,10 @@ The last `finally` is what ensures we wait for the last operations to complete: In the idiom we saw in the above example, the same semaphore is used both for limiting the number of background operations, and later to wait for all of them to complete. Sometimes, we want several different loops to use the same semaphore to limit their total parallelism. In that case we must use a separate mechanism for waiting for the completion of the background operations started by the loop. The most convenient way to wait for ongoing operations is using a gate, which we will describe in detail later. A typical example of a loop whose parallelism is limited by an external semaphore: ```cpp -thread_local semaphore limit(100); -future<> f() { - return do_with(seastar::gate(), [] (auto& gate) { - return do_for_each(boost::irange(0, 456), [&gate] (int i) { +thread_local seastar::semaphore limit(100); +seastar::future<> f() { + return seastar::do_with(seastar::gate(), [] (auto& gate) { + return seastar::do_for_each(boost::irange(0, 456), [&gate] (int i) { return limit.wait(1).then([&gate] { gate.enter(); slow().finally([&gate] { @@ -728,7 +730,6 @@ A `pipe` resembles a Unix pipe, in that it has a read side, a write side, and The pipe's read and write interfaces are future-based blocking. I.e., the write() and read() methods return a future which is fulfilled when the operation is complete. The pipe is single-reader single-writer, meaning that until the future returned by read() is fulfilled, read() must not be called again (and same for write). Note: The pipe reader and writer are movable, but *not* copyable. It is often convenient to wrap each end in a shared pointer, so it can be copied (e.g., used in an std::function which needs to be copyable) or easily captured into multiple continuations. - # Shutting down a service with a gate Consider an application which has some long operation `slow()`, and many such operations may be started at any time. A number of `slow()` operations may even even be active in parallel. Now, you want to shut down this service, but want to make sure that before that, all outstanding operations are completed. Moreover, you don't want to allow new `slow()` operations to start while the shut-down is in progress. @@ -751,25 +752,23 @@ Here is a typical example of using a gate: #include "core/gate.hh" #include -future<> slow(int i) { +seastar::future<> slow(int i) { std::cerr << "starting " << i << "\n"; - return sleep(std::chrono::seconds(10)).then([i] { + return seastar::sleep(std::chrono::seconds(10)).then([i] { std::cerr << "done " << i << "\n"; }); } - -future<> f() { - return do_with(seastar::gate(), [] (auto& g) { - return do_for_each(boost::counting_iterator(1), +seastar::future<> f() { + return seastar::do_with(seastar::gate(), [] (auto& g) { + return seastar::do_for_each(boost::counting_iterator(1), boost::counting_iterator(6), [&g] (int i) { seastar::with_gate(g, [i] { return slow(i); }); // wait one second before starting the next iteration - return sleep(std::chrono::seconds(1)); + return seastar::sleep(std::chrono::seconds(1)); }).then([&g] { - sleep(std::chrono::seconds(1)).then([&g] { + seastar::sleep(std::chrono::seconds(1)).then([&g] { // This will fail, because it will be after the close() - g.enter(); seastar::with_gate(g, [] { return slow(6); }); }); return g.close(); @@ -802,13 +801,13 @@ As explained so far, a gate can prevent new invocations of an operation, and wai In the previous example code, we had an un-interruptible operation `slow()` which slept for 10 seconds. Let's replace it by a loop of 10 one-second sleeps, calling `g.check()` each second: ```cpp -future<> slow(int i, seastar::gate &g) { +seastar::future<> slow(int i, seastar::gate &g) { std::cerr << "starting " << i << "\n"; - return do_for_each(boost::counting_iterator(0), - boost::counting_iterator(10), + return seastar::do_for_each(boost::counting_iterator(0), + boost::counting_iterator(10), [&g] (int) { g.check(); - return sleep(std::chrono::seconds(1)); + return seastar::sleep(std::chrono::seconds(1)); }).finally([i] { std::cerr << "done " << i << "\n"; }); @@ -827,7 +826,7 @@ TODO: Introduce our shared_ptr (and lw_shared_ptr) and sstring and say the stand # More about Seastar's event loop TODO: Mention the event loop (scheduler). remind that continuations on the same thread do not run in parallel, so do not need locks, atomic variables, etc (different threads shouldn't access the same data - more on that below). continuations obviously must not use blocking operations, or they block the whole thread. -Talk about polling that we currently do, and how today even sleep() or waiting for incoming connections or whatever, takes 100% of all CPUs. +TODO: Talk about polling that we currently do, and how today even sleep() or waiting for incoming connections or whatever, takes 100% of all CPUs. # Introducing Seastar's network stack @@ -838,12 +837,12 @@ For optimal performance, Seastar's network stack is sharded just like Seastar ap In the examples we saw earlier, `main()` ran our function `f()` only once, on the first thread. Unless the server is run with the `"-c1"` option (one thread only), this will mean that any connection arriving to a different thread will not be handled. So in all the examples below, we will need to run the same service loop on all cores. We can easily do this with the `smp::submit_to` function: ```cpp -future<> service_loop(); +seastar::future<> service_loop(); -future<> f() { - return parallel_for_each(boost::irange(0, smp::count), +seastar::future<> f() { + return seastar::parallel_for_each(boost::irange(0, seastar::smp::count), [] (unsigned c) { - return smp::submit_to(c, service_loop); + return seastar::smp::submit_to(c, service_loop); }); } ``` @@ -859,10 +858,11 @@ We begin with a simple example of a TCP network server written in Seastar. This #include future<> service_loop() { - return do_with(listen(make_ipv4_address({1234})), [] (auto& listener) { - return keep_doing([&listener] () { + return seastar::do_with(seastar::listen(seastar::make_ipv4_address({1234})), + [] (auto& listener) { + return seastar::keep_doing([&listener] () { return listener.accept().then( - [] (connected_socket s, socket_address a) { + [] (seastar::connected_socket s, seastar::socket_address a) { std::cout << "Accepted connection from " << a << "\n"; }); }); @@ -872,9 +872,9 @@ future<> service_loop() { This code works as follows: 1. The ```listen()``` call creates a ```server_socket``` object, ```listener```, which listens on TCP port 1234 (on any network interface). -2. To handle one connection, we call ```listener```'s ```accept()``` method. This method returns a ```future```, i.e., is eventually resolved with an incoming TCP connection from a client (```connected_socket```) and the client's IP address and port (```socket_address```). -3. To repeatedly accept new connections, we use the ```keep_doing()``` loop idiom. ```keep_doing()``` runs its lambda parameter over and over, starting the next iteration as soon as the future returned by the previous iteration completes. The iterations only stop if an exception is encountered. The future returned by ```keep_doing()``` itself completes only when the iteration stops (i.e., only on exception). -4. We use ```do_with()``` to ensure that the listener socket lives throughout the loop. +2. We use ```do_with()``` to ensure that the listener socket lives throughout the loop. +3. To handle one connection, we call ```listener```'s ```accept()``` method. This method returns a ```future```, i.e., is eventually resolved with an incoming TCP connection from a client (```connected_socket```) and the client's IP address and port (```socket_address```). +4. To repeatedly accept new connections, we use the ```keep_doing()``` loop idiom. ```keep_doing()``` runs its lambda parameter over and over, starting the next iteration as soon as the future returned by the previous iteration completes. The iterations only stop if an exception is encountered. The future returned by ```keep_doing()``` itself completes only when the iteration stops (i.e., only on exception). Output from this server looks like the following example: @@ -895,9 +895,9 @@ program failed with uncaught exception: bind: Address already in use This happens because by default, Seastar refuses to reuse the local port if there are any vestiges of old connections using that port. In our silly server, because the server is the side which first closes the connection, each connection lingers for a while in the "```TIME_WAIT```" state after being closed, and these prevent ```listen()``` on the same port from succeeding. Luckily, we can give listen an option to work despite these remaining ```TIME_WAIT```. This option is analogous to ```socket(7)```'s ```SO_REUSEADDR``` option: ```cpp - listen_options lo; + seastar::listen_options lo; lo.reuse_address = true; - return do_with(listen(make_ipv4_address({1234}), lo), [] (auto& listener) { + return seastar::do_with(seastar::listen(seastar::make_ipv4_address({1234}), lo), ``` Most servers will always turn on this ```reuse_address``` listen option. Stevens' book "Unix Network Programming" even says that "All TCP servers should specify this socket option to allow the server to be restarted". Therefore in the future Seastar should probably default to this option being on --- even if for historic reasons this is not the default in Linux's socket API. @@ -912,15 +912,16 @@ Let's advance our example server by outputting some canned response to each conn const char* canned_response = "Seastar is the future!\n"; -future<> service_loop() { - listen_options lo; +seastar::future<> service_loop() { + seastar::listen_options lo; lo.reuse_address = true; - return do_with(listen(make_ipv4_address({1234}), lo), [] (auto& listener) { - return keep_doing([&listener] () { + return seastar::do_with(seastar::listen(seastar::make_ipv4_address({1234}), lo), + [] (auto& listener) { + return seastar::keep_doing([&listener] () { return listener.accept().then( - [] (connected_socket s, socket_address a) { + [] (seastar::connected_socket s, seastar::socket_address a) { auto out = s.output(); - return do_with(std::move(s), std::move(out), + return seastar::do_with(std::move(s), std::move(out), [] (auto& s, auto& out) { return out.write(canned_response).then([&out] { return out.close(); @@ -932,7 +933,7 @@ future<> service_loop() { } ``` -The new part of this code begins by taking the ```connected_socket```'s ```output()```, which returns an ```output_stream``` object. On this output stream ```out``` we can write our response using the ```write()``` method. The simple-looking ```write()``` operation is in fact a complex asynchronous operation behind the scenes, possibly causing multiple packets to be sent, retransmitted, etc., as needed. ```write()``` returns a future saying when it is ok to ```write()``` again to this output stream; This does not necessarily guarantee that the remote peer received all the data we sent it, but it guarantees that the output stream has enough buffer space to allow another write to begin. +The new part of this code begins by taking the ```connected_socket```'s ```output()```, which returns an ```output_stream``` object. On this output stream ```out``` we can write our response using the ```write()``` method. The simple-looking ```write()``` operation is in fact a complex asynchronous operation behind the scenes, possibly causing multiple packets to be sent, retransmitted, etc., as needed. ```write()``` returns a future saying when it is ok to ```write()``` again to this output stream; This does not necessarily guarantee that the remote peer received all the data we sent it, but it guarantees that the output stream has enough buffer space (or in the TCP case, there is enough room in the TCP congestion window) to allow another write to begin. After ```write()```ing the response to ```out```, the example code calls ```out.close()``` and waits for the future it returns. This is necessary, because ```write()``` attempts to batch writes so might not have yet written anything to the TCP stack at this point, and only when close() concludes can we be sure that all the data we wrote to the output stream has actually reached the TCP stack --- and only at this point we may finally dispose of the ```out``` and ```s``` objects. @@ -956,12 +957,13 @@ Let's look at a simple example server involving both reads an writes. This is a #include "core/reactor.hh" #include "core/future-util.hh" -future<> handle_connection(connected_socket s, socket_address a) { +seastar::future<> handle_connection(seastar::connected_socket s, + seastar::socket_address a) { auto out = s.output(); auto in = s.input(); return do_with(std::move(s), std::move(out), std::move(in), [] (auto& s, auto& out, auto& in) { - return repeat([&out, &in] { + return seastar::repeat([&out, &in] { return in.read().then([&out] (auto buf) { if (buf) { return out.write(std::move(buf)).then([&out] { @@ -970,7 +972,8 @@ future<> handle_connection(connected_socket s, socket_address a) { return stop_iteration::no; }); } else { - return make_ready_future(stop_iteration::yes); + return seastar::make_ready_future( + seastar::stop_iteration::yes); } }); }).then([&out] { @@ -979,13 +982,14 @@ future<> handle_connection(connected_socket s, socket_address a) { }); } -future<> service_loop() { - listen_options lo; +seastar::future<> service_loop() { + seastar::listen_options lo; lo.reuse_address = true; - return do_with(listen(make_ipv4_address({1234}), lo), [] (auto& listener) { - return keep_doing([&listener] () { + return seastar::do_with(seastar::listen(seastar::make_ipv4_address({1234}), lo), + [] (auto& listener) { + return seastar::keep_doing([&listener] () { return listener.accept().then( - [] (connected_socket s, socket_address a) { + [] (seastar::connected_socket s, seastar::socket_address a) { // Note we ignore, not return, the future returned by // handle_connection(), so we do not wait for one // connection to be handled before accepting the next one. @@ -1011,7 +1015,9 @@ Discuss `sharded<>`, its benefits over directly using smp::submit_to() as above, # Shutting down cleanly -Handling interrupt, shutting down services, etc. +TODO: Handling interrupt, shutting down services, etc. + +Move the seastar::gate section here. # Command line options ## Standard Seastar command-line options @@ -1030,14 +1036,14 @@ Rather, applications which want to have command-line options of their own should #include #include int main(int argc, char** argv) { - app_template app; + seastar::app_template app; namespace bpo = boost::program_options; app.add_options() ("flag", "some optional flag") ("size,s", bpo::value()->default_value(100), "size") ; app.add_positional_options({ - { "filename", bpo::value>()->default_value({}), + { "filename", bpo::value>()->default_value({}), "sstable files to verify", -1} }); app.run(argc, argv, [&app] { @@ -1046,11 +1052,11 @@ int main(int argc, char** argv) { std::cout << "Flag is on\n"; } std::cout << "Size is " << args["size"].as() << "\n"; - auto& filenames = args["filename"].as>(); + auto& filenames = args["filename"].as>(); for (auto&& fn : filenames) { std::cout << fn << "\n"; } - return make_ready_future<>(); + return seastar::make_ready_future<>(); }); return 0; } @@ -1088,9 +1094,9 @@ handle SIGALRM pass noprint As we already defined above, An **asynchronous function**, also called a **promise**, is a function which returns a future and arranges for this future to be eventually resolved. As we already saw, an asynchronous function is usually written in terms of other asynchronous functions, for example we saw the function `slow()` which waits for the existing asynchronous function `sleep()` to complete, and then returns 3: ```cpp -future slow() { +seastar::future slow() { using namespace std::chrono_literals; - return sleep(100ms).then([] { return 3; }); + return seastar::sleep(100ms).then([] { return 3; }); } ``` diff --git a/docker/dev/Dockerfile b/docker/dev/Dockerfile index 6cd4968f6c5..619612b7f09 100644 --- a/docker/dev/Dockerfile +++ b/docker/dev/Dockerfile @@ -2,4 +2,5 @@ FROM fedora:23 RUN dnf install -y gcc-c++ clang libasan libubsan gnutls-devel hwloc hwloc-devel numactl-devel \ python3 libaio-devel ninja-build boost-devel git ragel xen-devel \ - cryptopp-devel libpciaccess-devel libxml2-devel zlib-devel xfsprogs-devel + cryptopp-devel libpciaccess-devel libxml2-devel zlib-devel xfsprogs-devel \ + cmake lz4-devel libunwind-devel systemtap-sdt-devel protobuf-devel lksctp-tools-devel diff --git a/http/api_docs.cc b/http/api_docs.cc index 473ec7fb15b..32ffbca04da 100644 --- a/http/api_docs.cc +++ b/http/api_docs.cc @@ -26,9 +26,13 @@ using namespace std; +namespace seastar { + namespace httpd { const sstring api_registry_builder::DEFAULT_PATH = "/api-doc"; const sstring api_registry_builder::DEFAULT_DIR = "."; } + +} diff --git a/http/api_docs.hh b/http/api_docs.hh index facf059a0f8..16410fd8201 100644 --- a/http/api_docs.hh +++ b/http/api_docs.hh @@ -27,6 +27,8 @@ #include "transformers.hh" #include +namespace seastar { + namespace httpd { struct api_doc : public json::json_base { @@ -156,4 +158,6 @@ public: } +} + #endif /* API_DOCS_HH_ */ diff --git a/http/common.cc b/http/common.cc index e6966680a4c..1612e0ef119 100644 --- a/http/common.cc +++ b/http/common.cc @@ -21,6 +21,8 @@ #include "common.hh" +namespace seastar { + namespace httpd { operation_type str2type(const sstring& type) { @@ -37,3 +39,6 @@ operation_type str2type(const sstring& type) { } } + +} + diff --git a/http/common.hh b/http/common.hh index 56f83263665..d78db8cde53 100644 --- a/http/common.hh +++ b/http/common.hh @@ -25,6 +25,8 @@ #include #include "core/sstring.hh" +namespace seastar { + namespace httpd { @@ -70,4 +72,6 @@ operation_type str2type(const sstring& type); } +} + #endif /* COMMON_HH_ */ diff --git a/http/exception.hh b/http/exception.hh index e79d025bc17..66410af7aa2 100644 --- a/http/exception.hh +++ b/http/exception.hh @@ -25,6 +25,8 @@ #include "reply.hh" #include "json/json_elements.hh" +namespace seastar { + namespace httpd { /** @@ -136,4 +138,6 @@ private: } +} + #endif /* EXCEPTION_HH_ */ diff --git a/http/file_handler.cc b/http/file_handler.cc index 98b2938a62f..db611cefa2b 100644 --- a/http/file_handler.cc +++ b/http/file_handler.cc @@ -28,6 +28,8 @@ #include "core/app-template.hh" #include "exception.hh" +namespace seastar { + namespace httpd { directory_handler::directory_handler(const sstring& doc_root, @@ -132,3 +134,5 @@ future> file_handler::handle(const sstring& path, } } + +} diff --git a/http/file_handler.hh b/http/file_handler.hh index 2c2bb66c931..afed2debca7 100644 --- a/http/file_handler.hh +++ b/http/file_handler.hh @@ -24,6 +24,8 @@ #include "handlers.hh" +namespace seastar { + namespace httpd { /** * This is a base class for file transformer. @@ -158,4 +160,6 @@ private: } +} + #endif /* HTTP_FILE_HANDLER_HH_ */ diff --git a/http/function_handlers.hh b/http/function_handlers.hh index 3f5fce7ab3a..48e390782a4 100644 --- a/http/function_handlers.hh +++ b/http/function_handlers.hh @@ -25,6 +25,8 @@ #include #include "json/json_elements.hh" +namespace seastar { + namespace httpd { /** @@ -120,3 +122,5 @@ protected: }; } + +} diff --git a/http/handlers.hh b/http/handlers.hh index 9a9888af61d..f90c3b9390d 100644 --- a/http/handlers.hh +++ b/http/handlers.hh @@ -26,8 +26,12 @@ #include "common.hh" #include "reply.hh" #include "core/future-util.hh" +#include "websocket.hh" #include +#include + +namespace seastar { namespace httpd { @@ -68,6 +72,42 @@ public: }; +/** + * handlers holds the logic for serving an incoming request. + * All handlers inherit from the base handler_websocket_base and + * implement the handle method. + */ +class handler_websocket_base { +public: + /** + * All handlers should implement this method. + * It fill the reply according to the request. + * @param path the url path used in this call + * @param params optional parameter object + * @param req the original request + * @param rep the reply + */ + virtual future<> handle(const sstring& path, websocket::connected_websocket& ws, + std::unique_ptr request) = 0; + + virtual ~handler_websocket_base() = default; + + /** + * Add a mandatory parameter + * @param param a parameter name + * @return a reference to the handler + */ + handler_websocket_base& mandatory(const sstring& param) { + _mandatory_param.push_back(param); + return *this; + } + + std::vector _mandatory_param; + +}; + +} + } #endif /* HANDLERS_HH_ */ diff --git a/http/http_response_parser.rl b/http/http_response_parser.rl index 832223eeee2..3b109fd025f 100644 --- a/http/http_response_parser.rl +++ b/http/http_response_parser.rl @@ -23,6 +23,8 @@ #include #include +namespace seastar { + struct http_response { sstring _version; std::unordered_map _headers; @@ -127,3 +129,5 @@ public: return _state == state::eof; } }; + +} diff --git a/http/httpd.cc b/http/httpd.cc index 527e57141b0..b15f28eddef 100644 --- a/http/httpd.cc +++ b/http/httpd.cc @@ -39,6 +39,8 @@ using namespace std::chrono_literals; +namespace seastar { + namespace httpd { http_stats::http_stats(http_server& server, const sstring& name) { @@ -60,3 +62,5 @@ sstring http_server_control::generate_server_name() { return seastar::format("http-{}", idgen++); } } + +} diff --git a/http/httpd.hh b/http/httpd.hh index a977232e5d3..470c62795c2 100644 --- a/http/httpd.hh +++ b/http/httpd.hh @@ -33,7 +33,6 @@ #include "core/queue.hh" #include "core/future-util.hh" #include "core/metrics_registration.hh" -#include #include #include #include @@ -42,12 +41,18 @@ #include #include #include +#include +#include #include "reply.hh" #include "http/routes.hh" +#include "http/websocket.hh" + +namespace seastar { namespace httpd { class http_server; + class http_stats; using namespace std::chrono_literals; @@ -68,7 +73,7 @@ class http_server { uint64_t _read_errors = 0; uint64_t _respond_errors = 0; sstring _date = http_date(); - timer<> _date_format_timer { [this] {_date = http_date();} }; + timer<> _date_format_timer { [this] { _date = http_date(); }}; bool _stopping = false; promise<> _all_connections_stopped; future<> _stopped = _all_connections_stopped.get_future(); @@ -78,12 +83,14 @@ private: _all_connections_stopped.set_value(); } } + public: routes _routes; explicit http_server(const sstring& name) : _stats(*this, name) { _date_format_timer.arm_periodic(1s); } + future<> listen(ipv4_addr addr) { listen_options lo; lo.reuse_address = true; @@ -91,6 +98,7 @@ public: _stopped = when_all(std::move(_stopped), do_accepts(_listeners.size() - 1)).discard_result(); return make_ready_future<>(); } + future<> stop() { _stopping = true; for (auto&& l : _listeners) { @@ -101,27 +109,29 @@ public: } return std::move(_stopped); } + future<> do_accepts(int which) { ++_connections_being_accepted; return _listeners[which].accept().then_wrapped( - [this, which] (future f_cs_sa) mutable { + [this, which](future f_cs_sa) mutable { --_connections_being_accepted; if (_stopping) { maybe_idle(); return; } auto cs_sa = f_cs_sa.get(); - auto conn = new connection(*this, std::get<0>(std::move(cs_sa)), std::get<1>(std::move(cs_sa))); - conn->process().then_wrapped([this, conn] (auto&& f) { - delete conn; - try { - f.get(); - } catch (std::exception& ex) { - std::cerr << "request error " << ex.what() << std::endl; - } - }); + auto conn = new connection(*this, std::get<0>(std::move(cs_sa)), + std::get<1>(std::move(cs_sa))); + conn->process().then_wrapped([conn](auto&& f) { + delete conn; + try { + f.get(); + } catch (std::exception& ex) { + std::cerr << "request error " << ex.what() << std::endl; + } + }); do_accepts(which); - }).then_wrapped([] (auto f) { + }).then_wrapped([](auto f) { try { f.get(); } catch (std::exception& ex) { @@ -129,9 +139,18 @@ public: } }); } + class connection : public boost::intrusive::list_base_hook<> { + enum connection_status { + keep_open = 0, + close, + detach + }; + http_server& _server; - connected_socket _fd; + //The underlying sockets can become websockets + //FIXME boost::variant makes the code verbose and ugly + boost::variant> _fd; input_stream _read_buf; output_stream _write_buf; static constexpr size_t limit = 4096; @@ -139,93 +158,132 @@ public: http_request_parser _parser; std::unique_ptr _req; std::unique_ptr _resp; + socket_address _addr; // null element marks eof - queue> _replies { 10 };bool _done = false; + queue> _replies { 10 }; + // connection_status disctates how the connection should be treated after the request as been served + connection_status _done = keep_open; public: + connection(http_server& server, connected_socket&& fd, socket_address addr) - : _server(server), _fd(std::move(fd)), _read_buf(_fd.input()), _write_buf( - _fd.output()) { + : _server(server), _fd(std::move(fd)), _read_buf(boost::get(_fd).input()), + _write_buf(boost::get(_fd).output()), _addr(addr) { ++_server._total_connections; ++_server._current_connections; _server._connections.push_back(*this); } + ~connection() { --_server._current_connections; _server._connections.erase(_server._connections.iterator_to(*this)); _server.maybe_idle(); } + future<> process() { // Launch read and write "threads" simultaneously: return when_all(read(), respond()).then( - [] (std::tuple, future<>> joined) { + [this](std::tuple, future<>> joined) { + if (_done == detach) { + // The connection is now detached. It still exists but outside of read and write fibers + sstring url = set_query_param(*_req.get()); + return _write_buf.flush().then([this, url] { + _fd = std::move(websocket::connected_websocket( + std::move(boost::get(_fd)), _addr)); + return _server._routes.handle_ws(url, + boost::get> + (_fd), std::move(_req)); + }).then_wrapped([](future<> f) { + if (f.failed()) { + f.get_exception(); + } + }); + } // FIXME: notify any exceptions in joined? return make_ready_future<>(); }); } + void shutdown() { - _fd.shutdown_input(); - _fd.shutdown_output(); + + if (_fd.which() == 0) { + boost::get(_fd).shutdown_input(); + boost::get(_fd).shutdown_output(); + } else { + boost::get> (_fd).shutdown_input(); + boost::get> (_fd).shutdown_output(); + } } + future<> read() { - return do_until([this] {return _done;}, [this] { + return do_until([this] { return _done != keep_open; }, [this] { return read_one(); - }).then_wrapped([this] (future<> f) { + }).then_wrapped([this](future<> f) { // swallow error - if (f.failed()) { + if (f.failed()) _server._read_errors++; - } f.ignore_ready_future(); - return _replies.push_eventually( {}); + if (_done == detach) + return make_ready_future(); + return _replies.push_eventually({}); }).finally([this] { + if (_done == detach) + return make_ready_future<>(); return _read_buf.close(); }); } + future<> read_one() { _parser.init(); - return _read_buf.consume(_parser).then([this] () mutable { + return _read_buf.consume(_parser).then([this]() mutable { if (_parser.eof()) { - _done = true; + _done = close; return make_ready_future<>(); } ++_server._requests_served; std::unique_ptr req = _parser.get_parsed_request(); - return _replies.not_full().then([req = std::move(req), this] () mutable { + return _replies.not_full().then([req = std::move(req), this]() mutable { return generate_reply(std::move(req)); - }).then([this](bool done) { + }).then([this](connection_status done) { _done = done; }); }); } + future<> respond() { - return do_response_loop().then_wrapped([this] (future<> f) { + return do_response_loop().then_wrapped([this](future<> f) { // swallow error if (f.failed()) { _server._respond_errors++; } f.ignore_ready_future(); + if (_done == detach) + return make_ready_future<>(); return _write_buf.close(); }); } + future<> do_response_loop() { return _replies.pop_eventually().then( - [this] (std::unique_ptr resp) { + [this](std::unique_ptr resp) { if (!resp) { // eof return make_ready_future<>(); } _resp = std::move(resp); return start_response().then([this] { - return do_response_loop(); - }); + if (_done == keep_open) + return do_response_loop(); + return make_ready_future<>(); + }); }); } + future<> start_response() { _resp->_headers["Server"] = "Seastar httpd"; _resp->_headers["Date"] = _server._date; - _resp->_headers["Content-Length"] = to_sstring( - _resp->_content.size()); + _resp->_headers["Content-Length"] = to_sstring(_resp->_content.size()); return _write_buf.write(_resp->_response_line.begin(), _resp->_response_line.size()).then([this] { return write_reply_headers(_resp->_headers.begin()); @@ -239,6 +297,7 @@ public: _resp.reset(); }); } + future<> write_reply_headers( std::unordered_map::iterator hi) { if (hi == _resp->_headers.end()) { @@ -251,15 +310,15 @@ public: return _write_buf.write(hi->second.begin(), hi->second.size()); }).then([this] { return _write_buf.write("\r\n", 2); - }).then([hi, this] () mutable { + }).then([hi, this]() mutable { return write_reply_headers(++hi); }); } static short hex_to_byte(char c) { - if (c >='a' && c <= 'z') { + if (c >= 'a' && c <= 'z') { return c - 'a' + 10; - } else if (c >='A' && c <= 'Z') { + } else if (c >= 'A' && c <= 'Z') { return c - 'A' + 10; } return c - '0'; @@ -269,7 +328,6 @@ public: * Convert a hex encoded 2 bytes substring to char */ static char hexstr_to_char(const std::experimental::string_view& in, size_t from) { - return static_cast(hex_to_byte(in[from]) * 16 + hex_to_byte(in[from + 1])); } @@ -305,18 +363,17 @@ public: if (split >= param.length() - 1) { sstring key; - if (url_decode(param.substr(0,split) , key)) { + if (url_decode(param.substr(0, split), key)) { req.query_parameters[key] = ""; } } else { sstring key; sstring value; - if (url_decode(param.substr(0,split), key) + if (url_decode(param.substr(0, split), key) && url_decode(param.substr(split + 1), value)) { req.query_parameters[key] = value; } } - } /** @@ -333,32 +390,38 @@ public: size_t end_param; std::experimental::string_view url = req._url; while ((end_param = req._url.find('&', curr)) != sstring::npos) { - add_param(req, url.substr(curr, end_param - curr) ); + add_param(req, url.substr(curr, end_param - curr)); curr = end_param + 1; } add_param(req, url.substr(curr)); return req._url.substr(0, pos); } - future generate_reply(std::unique_ptr req) { + future generate_reply(std::unique_ptr req) { auto resp = std::make_unique(); bool conn_keep_alive = false; bool conn_close = false; + auto it = req->_headers.find("Connection"); if (it != req->_headers.end()) { if (it->second == "Keep-Alive") { conn_keep_alive = true; } else if (it->second == "Close") { conn_close = true; + } else if (it->second.find("Upgrade") != std::string::npos) { + auto upgrade = req->_headers.find("Upgrade"); + if (upgrade != req->_headers.end() && boost::iequals(upgrade->second.begin(), "websocket")) + return upgrade_websocket(std::move(req)); //websocket upgrade } } bool should_close; - // TODO: Handle HTTP/2.0 when it releases + // TODO: Handle HTTP/2.0 when it released resp->set_version(req->_version); if (req->_version == "1.0") { if (conn_keep_alive) { resp->_headers["Connection"] = "Keep-Alive"; + std::cout << "keep alive" << std::endl; } should_close = !conn_keep_alive; } else if (req->_version == "1.1") { @@ -369,34 +432,71 @@ public: } sstring url = set_query_param(*req.get()); sstring version = req->_version; - return _server._routes.handle(url, std::move(req), std::move(resp)). - // Caller guarantees enough room - then([this, should_close, version = std::move(version)](std::unique_ptr rep) { - rep->set_version(version).done(); - this->_replies.push(std::move(rep)); - return make_ready_future(should_close); - }); + + return _server._routes.handle(url, std::move(req), std::move(resp)).then( + [this, should_close, version = std::move(version)](std::unique_ptr rep) { + rep->set_version(version).done(); + this->_replies.push(std::move(rep)); + if (should_close) + return make_ready_future(close); + return make_ready_future(keep_open); + }); } + + future upgrade_websocket(std::unique_ptr req) { + connection_status done; + + sstring url = set_query_param(*req.get()); + auto resp = std::make_unique(); + resp->set_version(req->_version); + + // We search for the websocket nonce in the request headers + auto it = req->_headers.find("Sec-WebSocket-Key"); + if (it != req->_headers.end() && _server._routes.get_ws_handler(url, _req)) { + // Found it, so we compute the websocket handshake key, reply and mark the connection as detached + resp->_headers["Upgrade"] = "websocket"; + resp->_headers["Connection"] = "Upgrade"; + + resp->_headers["Sec-WebSocket-Accept"] = httpd::websocket::encode_handshake_key(it->second); + resp->set_status(reply::status_type::switching_protocols).done(); + _req = std::move(req); + _done = done = detach; + } else { + // The upgrade request is invalid + _done = done = close; + resp->set_status(reply::status_type::bad_request); + } + resp->done(); + _replies.push(std::move(resp)); + return make_ready_future(done); + } + future<> write_body() { return _write_buf.write(_resp->_content.begin(), _resp->_content.size()); } }; + uint64_t total_connections() const { return _total_connections; } + uint64_t current_connections() const { return _current_connections; } + uint64_t requests_served() const { return _requests_served; } + uint64_t read_errors() const { return _read_errors; } + uint64_t reply_errors() const { return _respond_errors; } + static sstring http_date() { auto t = ::time(nullptr); struct tm tm; @@ -405,6 +505,7 @@ public: strftime(tmp, sizeof(tmp), "%d %b %Y %H:%M:%S GMT", &tm); return tmp; } + private: boost::intrusive::list _connections; }; @@ -426,10 +527,11 @@ class http_server_control { distributed* _server_dist; private: static sstring generate_server_name(); + public: http_server_control() : _server_dist(new distributed) { - } + } future<> start(const sstring& name = generate_server_name()) { return _server_dist->start(name); @@ -454,6 +556,7 @@ public: } }; +} } -#endif /* APPS_HTTPD_HTTPD_HH_ */ +#endif /* APPS_HTTPD_HTTPD_HH_ */ \ No newline at end of file diff --git a/http/json_path.cc b/http/json_path.cc index 695dd859b5e..12721c8a61d 100644 --- a/http/json_path.cc +++ b/http/json_path.cc @@ -21,6 +21,8 @@ #include "json_path.hh" +namespace seastar { + namespace httpd { using namespace std; @@ -66,3 +68,5 @@ path_description::path_description(const sstring& path, operation_type method, } } + +} diff --git a/http/json_path.hh b/http/json_path.hh index 8f234ba0063..456ca2aa751 100644 --- a/http/json_path.hh +++ b/http/json_path.hh @@ -30,6 +30,8 @@ #include "routes.hh" #include "function_handlers.hh" +namespace seastar { + namespace httpd { /** @@ -134,4 +136,7 @@ struct path_description { }; } + +} + #endif /* JSON_PATH_HH_ */ diff --git a/http/matcher.cc b/http/matcher.cc index 8e766c6d65d..3dc706e46c4 100644 --- a/http/matcher.cc +++ b/http/matcher.cc @@ -23,6 +23,8 @@ #include +namespace seastar { + namespace httpd { using namespace std; @@ -68,3 +70,5 @@ size_t str_matcher::match(const sstring& url, size_t ind, parameters& param) { } } + +} diff --git a/http/matcher.hh b/http/matcher.hh index 10d781b928d..a990949b13a 100644 --- a/http/matcher.hh +++ b/http/matcher.hh @@ -26,6 +26,8 @@ #include "core/sstring.hh" +namespace seastar { + namespace httpd { /** @@ -107,4 +109,6 @@ private: } +} + #endif /* MATCHER_HH_ */ diff --git a/http/matchrules.hh b/http/matchrules.hh index f2e575ef5cb..30245f4d183 100644 --- a/http/matchrules.hh +++ b/http/matchrules.hh @@ -29,6 +29,8 @@ #include "core/sstring.hh" #include +namespace seastar { + namespace httpd { /** @@ -115,4 +117,6 @@ private: } +} + #endif /* MATCH_RULES_HH_ */ diff --git a/http/mime_types.cc b/http/mime_types.cc index 3ffc84d7dd2..f516dbc06c1 100644 --- a/http/mime_types.cc +++ b/http/mime_types.cc @@ -10,6 +10,8 @@ #include "mime_types.hh" +namespace seastar { + namespace httpd { namespace mime_types { @@ -43,3 +45,5 @@ const char* extension_to_type(const sstring& extension) } // namespace mime_types } // httpd + +} diff --git a/http/mime_types.hh b/http/mime_types.hh index 7241887de6b..02ce81e47cf 100644 --- a/http/mime_types.hh +++ b/http/mime_types.hh @@ -13,6 +13,8 @@ #include "core/sstring.hh" +namespace seastar { + namespace httpd { namespace mime_types { @@ -29,4 +31,6 @@ const char* extension_to_type(const sstring& extension); } // namespace httpd +} + #endif // HTTP_MIME_TYPES_HH diff --git a/http/reply.cc b/http/reply.cc index dea9c291bf1..db366c31ac7 100644 --- a/http/reply.cc +++ b/http/reply.cc @@ -30,6 +30,8 @@ // #include "reply.hh" +namespace seastar { + namespace httpd { namespace status_strings { @@ -50,6 +52,7 @@ const sstring internal_server_error = " 500 Internal Server Error\r\n"; const sstring not_implemented = " 501 Not Implemented\r\n"; const sstring bad_gateway = " 502 Bad Gateway\r\n"; const sstring service_unavailable = " 503 Service Unavailable\r\n"; +const sstring switching_protocols = " 101 Switching Protocols\r\n"; static const sstring& to_string(reply::status_type status) { switch (status) { @@ -85,6 +88,8 @@ static const sstring& to_string(reply::status_type status) { return bad_gateway; case reply::status_type::service_unavailable: return service_unavailable; + case reply::status_type::switching_protocols: + return switching_protocols; default: return internal_server_error; } @@ -96,3 +101,5 @@ sstring reply::response_line() { } } // namespace server + +} diff --git a/http/reply.hh b/http/reply.hh index 4361ba578ee..c76c5ea78a7 100644 --- a/http/reply.hh +++ b/http/reply.hh @@ -35,6 +35,8 @@ #include #include "http/mime_types.hh" +namespace seastar { + namespace httpd { /** * A reply to be sent to a client. @@ -59,7 +61,8 @@ struct reply { internal_server_error = 500, //!< internal_server_error not_implemented = 501, //!< not_implemented bad_gateway = 502, //!< bad_gateway - service_unavailable = 503 //!< service_unavailable + service_unavailable = 503, //!< service_unavailable + switching_protocols = 101 //!< switching_protocols } _status; /** @@ -130,3 +133,5 @@ struct reply { }; } // namespace httpd + +} diff --git a/http/request.hh b/http/request.hh index 2c3417f9153..cef7111b56d 100644 --- a/http/request.hh +++ b/http/request.hh @@ -37,6 +37,8 @@ #include #include "common.hh" +namespace seastar { + namespace httpd { class connection; @@ -116,4 +118,6 @@ struct request { } // namespace httpd +} + #endif // HTTP_REQUEST_HPP diff --git a/http/request_parser.rl b/http/request_parser.rl index b06c82b65b9..91b522bb11d 100644 --- a/http/request_parser.rl +++ b/http/request_parser.rl @@ -26,6 +26,8 @@ #include #include "http/request.hh" +namespace seastar { + using namespace httpd; %% machine request; @@ -137,3 +139,5 @@ public: return _state == state::eof; } }; + +} diff --git a/http/routes.cc b/http/routes.cc index 18d973b5f04..39816c4228d 100644 --- a/http/routes.cc +++ b/http/routes.cc @@ -23,12 +23,14 @@ #include "reply.hh" #include "exception.hh" +namespace seastar { + namespace httpd { using namespace std; -void verify_param(const request& req, const sstring& param) { - if (req.get_query_param(param) == "") { +void verify_param(const std::unique_ptr& req, const sstring& param) { + if (req->get_query_param(param) == "") { throw missing_param_exception(param); } } @@ -86,7 +88,7 @@ future > routes::handle(const sstring& path, std::unique_ if (handler != nullptr) { try { for (auto& i : handler->_mandatory_param) { - verify_param(*req.get(), i); + verify_param(req, i); } auto r = handler->handle(path, std::move(req), std::move(rep)); return r.handle_exception(_general_handler); @@ -106,6 +108,18 @@ future > routes::handle(const sstring& path, std::unique_ return make_ready_future>(std::move(rep)); } +future<> routes::handle_ws(const sstring &path, websocket::connected_websocket& ws, + std::unique_ptr request) { + handler_websocket_base* handler = get_ws_handler(normalize_url(path), request); + if (handler != nullptr) { + for (auto& i : handler->_mandatory_param) { + verify_param(request, i); + } + return handler->handle(path, ws, std::move(request)); + } + return make_ready_future(); +} + sstring routes::normalize_url(const sstring& url) { if (url.length() < 2 || url.at(url.length() - 1) != '/') { return url; @@ -131,6 +145,22 @@ handler_base* routes::get_handler(operation_type type, const sstring& url, return nullptr; } +handler_websocket_base *routes::get_ws_handler(const sstring &url, const std::unique_ptr& req) { + handler_websocket_base* handler = (_map_ws.find(url) == _map_ws.end()) ? nullptr : _map_ws[url]; + if (handler != nullptr) { + try { + for (auto& i : handler->_mandatory_param) { + verify_param(req, i); + } + } + catch (const missing_param_exception &e) { + return nullptr; + } + return handler; + } + return nullptr; +} + routes& routes::add(operation_type type, const url& url, handler_base* handler) { match_rule* rule = new match_rule(handler); @@ -142,3 +172,5 @@ routes& routes::add(operation_type type, const url& url, } } + +} diff --git a/http/routes.hh b/http/routes.hh index 187b7e960a8..fbe736d919e 100644 --- a/http/routes.hh +++ b/http/routes.hh @@ -31,6 +31,9 @@ #include #include #include "core/future-util.hh" +#include "websocket.hh" + +namespace seastar { namespace httpd { @@ -94,6 +97,19 @@ public: return *this; } + /** + * adding a handler as an exact match + * @param url the url to match (note that url should start with /) + * @param handler the desire handler + * @return it self + */ + routes& put(const sstring& url, handler_websocket_base* handler) { + //FIXME if a handler is already exists, it need to be + // deleted to prevent memory leak + _map_ws[url] = handler; + return *this; + } + /** * add a rule to be used. * rules are search only if an exact match was not found. @@ -130,6 +146,29 @@ public: */ future > handle(const sstring& path, std::unique_ptr req, std::unique_ptr rep); + + /** + * the main entry point. + * the general handler calls this method with the request + * the method takes the headers from the request and find the + * right handler. + * It then call the handler with the parameters (if they exists) found in the url + * @param path the url path found + * @param req the http request + * @param rep the http reply + */ + future<> handle_ws(const sstring& path, websocket::connected_websocket& ws, + std::unique_ptr request); + + /** + * Search and return a handler by the operation type and url + * @param type the http operation type + * @param url the request url + * @param params a parameter object that will be filled during the match + * @return a handler based on the type/url match + */ + handler_websocket_base* get_ws_handler(const sstring& url, const std::unique_ptr& req); + /** * Search and return an exact match * @param url the request url @@ -163,6 +202,10 @@ private: std::unordered_map _map[NUM_OPERATION]; std::vector _rules[NUM_OPERATION]; + + //Websocket + std::unordered_map _map_ws; + public: using exception_handler_fun = std::function(std::exception_ptr eptr)>; using exception_handler_id = size_t; @@ -204,4 +247,6 @@ void verify_param(const httpd::request& req, const sstring& param); } +} + #endif /* ROUTES_HH_ */ diff --git a/http/transformers.cc b/http/transformers.cc index a08991a6da3..781e154cb2e 100644 --- a/http/transformers.cc +++ b/http/transformers.cc @@ -23,6 +23,8 @@ #include "transformers.hh" #include +namespace seastar { + namespace httpd { using namespace std; @@ -38,3 +40,5 @@ void content_replace::transform(sstring& content, const request& req, } } + +} diff --git a/http/transformers.hh b/http/transformers.hh index 52a3488c4b7..7db13ef2f1f 100644 --- a/http/transformers.hh +++ b/http/transformers.hh @@ -25,6 +25,8 @@ #include "handlers.hh" #include "file_handler.hh" +namespace seastar { + namespace httpd { /** @@ -54,4 +56,7 @@ private: }; } + +} + #endif /* TRANSFORMERS_HH_ */ diff --git a/http/websocket.cc b/http/websocket.cc new file mode 100644 index 00000000000..d8695ca0f96 --- /dev/null +++ b/http/websocket.cc @@ -0,0 +1,95 @@ +// +// Created by hippolyteb on 3/9/17. +// + +#include "websocket.hh" +#include +#include +#include +#include + +namespace seastar { +namespace httpd { +namespace websocket { +sstring encode_handshake_key(sstring nonce) { + constexpr char uuid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + constexpr size_t uuid_len = 36; + + CryptoPP::SHA hash; + byte digest[CryptoPP::SHA::DIGESTSIZE]; + hash.Update((byte*) nonce.data(), nonce.size()); + hash.Update((byte*) uuid, uuid_len); + hash.Final(digest); + + sstring base64; + + CryptoPP::Base64Encoder encoder; + encoder.Put(digest, sizeof(digest)); + encoder.MessageEnd(); + CryptoPP::word64 size = encoder.MaxRetrievable(); + if (size) { + base64.resize(size); + encoder.Get((byte*) base64.data(), base64.size()); + } + + return base64.substr(0, base64.size() - 1); //fixme useless cpy +} + +future +connect(socket_address sa, socket_address local) { + return engine().net().connect(sa, local).then([local](connected_socket fd) { + seastar::input_stream in = std::move(fd.input()); + seastar::output_stream out = std::move(fd.output()); + return do_with(std::move(fd), std::move(in), std::move(out), [local](connected_socket& fd, + auto& in, + auto& out) { + using random_bytes_engine = std::independent_bits_engine< + std::default_random_engine, std::numeric_limits::digits, unsigned char>; + + random_bytes_engine rbe; + sstring key(16, '\0'); + std::generate(key.begin(), key.end(), std::ref(rbe)); + + sstring nonce; + CryptoPP::Base64Encoder encoder; + encoder.Put((byte*) key.data(), key.size()); + encoder.MessageEnd(); + CryptoPP::word64 size = encoder.MaxRetrievable(); + if (size) { + nonce.resize(size); + encoder.Get((byte*) nonce.data(), nonce.size()); + } else { + //fixme throw ? + } + nonce = nonce.substr(0, nonce.size() - 1); + + std::stringstream stream; + //FIXME construct correct request header + stream + << "GET / HTTP/1.1\r\nHost: 127.0.0.1:10000\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n" + << "Sec-WebSocket-Key: " << nonce + << "\r\nSec-WebSocket-Protocol: default\r\nSec-WebSocket-Version: 13\r\n\r\n"; + + return out.write(stream.str()).then([local, nonce, &out, &in, &fd] { + return out.flush(); + }).then([local, nonce, &in, &fd] { + //FIXME extend http request parser to support header only payload (no HTTP verb) + return in.read().then([local, nonce, &fd](temporary_buffer response) { + if (!response) + throw std::exception(); //FIXME : proper failure + if (std::experimental::string_view(response.begin(), response.size()) + .find(encode_handshake_key(nonce)) != std::string::npos) { + return std::move(fd); + } else { + fd.shutdown_input(); + fd.shutdown_output(); + throw std::exception(); //FIXME : proper failure + } + }); + }); + }); + }); +} +} +} +} \ No newline at end of file diff --git a/http/websocket.hh b/http/websocket.hh new file mode 100644 index 00000000000..0a619e01a9c --- /dev/null +++ b/http/websocket.hh @@ -0,0 +1,280 @@ +/* + * This file is open source software, licensed to you under the terms + * of the Apache License, Version 2.0 (the "License"). See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. You may not use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +/* + * Copyright 2015 Cloudius Systems + */ + +#pragma once + +#include +#include "request.hh" +#include "websocket_fragment.hh" +#include "websocket_message.hh" + +namespace seastar { +namespace httpd { +namespace websocket { + +class output_stream_base { +private: + seastar::output_stream _stream; +public: + + output_stream_base() noexcept = delete; + + output_stream_base(seastar::output_stream&& stream) noexcept: _stream(std::move(stream)) {} + + output_stream_base(output_stream_base&&) noexcept = default; + + output_stream_base& operator=(output_stream_base&&) noexcept = default; + + future<> close() { return _stream.close(); }; + + future<> flush() { return _stream.flush(); }; + +protected: + future<> write(temporary_buffer header, message_base message) { + return _stream.write(std::move(header)).then([this, message = std::move(message)]() mutable -> future<> { + return _stream.write(std::move(message.payload)); + }); + } + + friend class reactor; +}; + +template +class output_stream final : public output_stream_base { + using output_stream_base::output_stream_base; +public: + future<> write(websocket::message message) { + auto header = message.get_header(); + return output_stream_base::write(std::move(header), std::move(message)); + }; +}; + +class input_stream_base { +protected: + seastar::input_stream _stream; + +public: + input_stream_base() noexcept = delete; + + input_stream_base(seastar::input_stream&& stream) noexcept: _stream(std::move(stream)) {} + + input_stream_base(input_stream_base&&) noexcept = default; + + input_stream_base& operator=(input_stream_base&&) noexcept = default; + + future<> close() { return _stream.close(); } +}; + +template +class input_stream final : public input_stream_base { + using input_stream_base::input_stream_base; +private: + ///Represent a fragmented message + std::vector> _fragmented_message; + ///Used for non-fragmented message + websocket::message _message; + +public: + /** + * Websocket fragments can be sent in chops. Using read_exactly leverages seastar network stack to make + * the exception the general case. So, we read_exactly(2) to get the next frame header and from there we read + * accordingly. + * As the RFC states, websocket frames are atomic/indivisible. They cannot be sent as multiple TCP packets, so when + * the first read_exactly() returns, the next ones will return immediately because there are already buffered. + */ + future> read_fragment() { + return _stream.read_exactly(sizeof(uint16_t)).then([this](temporary_buffer&& header) { + if (!header) + throw websocket_exception(NORMAL_CLOSURE); //EOF + fragment_header fragment_header(header); + if (fragment_header.extended_header_size() > 0) { + // The frame has an extended header (bigger payload size and/or there is a masking key) + return _stream.read_exactly(fragment_header.extended_header_size()).then( + [this, fragment_header](temporary_buffer extended_header) mutable { + if (!extended_header) + throw websocket_exception(NORMAL_CLOSURE); //EOF + fragment_header.feed_extended_header(extended_header); + // We now know exactly how much to read to get the full frame payload + return _stream.read_exactly(fragment_header.length).then( + [this, fragment_header](temporary_buffer&& payload) { + // Because empty frames are OK, an empty buffer does not necessarally means EOF. + if (!payload && fragment_header.length > 0) + throw websocket_exception(NORMAL_CLOSURE); //EOF + return inbound_fragment(fragment_header, payload); + }); + }); + } + // The frame doesn't have an extended header, so it's payload directly follows. + return _stream.read_exactly(fragment_header.length).then( + [this, fragment_header](temporary_buffer&& payload) { + // Because empty frames are OK, an empty buffer does not necessarally means EOF. + if (!payload && fragment_header.length > 0) + throw websocket_exception(NORMAL_CLOSURE); //EOF + return inbound_fragment(fragment_header, payload); + }); + }); + } + + /** + * Because empty websocket frames/messages are semantically valid, we cannot reproduce seastar standard behavior + * with dealing with EOF. So, we use exception instead to signal errors/eof. + * An exception always lead to closing the connection, so it should not hurt performance. + */ + future> read() { + return repeat([this] { // gather all fragments + return read_fragment().then([this](inbound_fragment&& fragment) { + if (!fragment) { throw websocket_exception(PROTOCOL_ERROR); } + switch (fragment.header.opcode) { + case websocket::CONTINUATION: { + if (!_fragmented_message.empty()) { _fragmented_message.emplace_back(std::move(fragment)); } + else { throw websocket_exception(PROTOCOL_ERROR); } //protocol error, close connection + if (fragment.header.fin) { + _message = websocket::message(_fragmented_message); + _fragmented_message.clear(); + } + return stop_iteration(fragment.header.fin); + } + + case TEXT: + case BINARY: { + if (fragment.header.fin && _fragmented_message.empty()) { + _message = websocket::message(fragment); + } else if (!fragment.header.fin && _fragmented_message.empty()) { + _fragmented_message.emplace_back(std::move(fragment)); + } else { throw websocket_exception(PROTOCOL_ERROR); } //protocol error, close connection + return stop_iteration(fragment.header.fin); + } + + case PING: + case PONG: { + if (fragment.header.fin) { _message = websocket::message(fragment); } + else { throw websocket_exception(PROTOCOL_ERROR); } //protocol error, close connection + return stop_iteration::yes; + } + + case CLOSE: //remote pair asked for close + throw websocket_exception(NONE); //protocol error, close connection + + case RESERVED: //protocol error, close connection + throw websocket_exception(PROTOCOL_ERROR); + default: + throw websocket_exception(UNEXPECTED_CONDITION); //"Hum.. this is embarrassing" + } + }); + }).then([this] { + return std::move(_message); + }); + } +}; + +/** + * The websocket protocol specifies that, when closing a connection, a CLOSE frame must be sent. Hence the need for a + * duplex stream able to send messages if a CLOSE frame is received, or if errors occurred when reading from the + * underlying stream. + */ +template +class duplex_stream { +private: + input_stream _input_stream; + output_stream _output_stream; + +public: + duplex_stream(input_stream&& input_stream, + output_stream&& output_stream) noexcept: + _input_stream(std::move(input_stream)), + _output_stream(std::move(output_stream)) {} + + duplex_stream(duplex_stream&&) noexcept = default; + + duplex_stream& operator=(duplex_stream&&) noexcept = default; + + future> read() { + return _input_stream.read().handle_exception_type([this] (websocket_exception& ex) { + return close(ex.status_code).then([ex = std::move(ex)]() -> future> { + return make_exception_future>(ex); + }); + }).then([](websocket::message message) { + return make_ready_future>(std::move(message)); + }); + } + + future<> write(websocket::message message) { + return _output_stream.write(std::move(message)); + }; + + future<> close(close_status_code code = NORMAL_CLOSURE) { + return write(websocket::make_close_message(code)).then([this] { + return _output_stream.flush(); + }).finally([this] { + return when_all(_input_stream.close(), _output_stream.close()).discard_result(); + }); + }; + + future<> flush() { return _output_stream.flush(); }; +}; + +template +class connected_websocket { +private: + seastar::connected_socket _socket; + +public: + socket_address remote_adress; + + connected_websocket(connected_socket socket, const socket_address remote_adress) noexcept : + _socket(std::move(socket)), remote_adress(remote_adress) { + } + + connected_websocket(connected_websocket&& cs) noexcept : _socket( + std::move(cs._socket)), remote_adress(cs.remote_adress) { + } + + connected_websocket& operator=(connected_websocket&& cs) noexcept { + _socket = std::move(cs._socket); + remote_adress = std::move(cs.remote_adress); + return *this; + }; + + duplex_stream stream() { + return duplex_stream(websocket::input_stream(std::move(_socket.input())), + websocket::output_stream(std::move(_socket.output()))); + } + + void shutdown_output() { _socket.shutdown_output(); } + + void shutdown_input() { _socket.shutdown_input(); } +}; + +sstring encode_handshake_key(sstring nonce); + +/** + * Connect a websocket. Can throw if unable to reach and/or establish the connection to the remote host. + * This function is provided for seawreck only, it has not been tested thoroughly. + * @param sa remote host to connect to + * @param local local address + * @return a connected_socket that can then be wrapped inside a connected_websocket + */ +future +connect(socket_address sa, socket_address local = socket_address(::sockaddr_in{AF_INET, INADDR_ANY, {0}})); + +} +} +} \ No newline at end of file diff --git a/http/websocket_fragment.hh b/http/websocket_fragment.hh new file mode 100644 index 00000000000..ca5c885e241 --- /dev/null +++ b/http/websocket_fragment.hh @@ -0,0 +1,195 @@ +/* + * This file is open source software, licensed to you under the terms + * of the Apache License, Version 2.0 (the "License"). See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. You may not use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +/* + * Copyright 2015 Cloudius Systems + */ + +#pragma once + +#include +#include + +namespace seastar { +namespace httpd { +namespace websocket { + +/* + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-------+-+-------------+-------------------------------+ +|F|R|R|R| opcode|M| Payload len | Extended payload length | +|I|S|S|S| (4) |A| (7) | (16/64) | +|N|V|V|V| |S| | (if payload len==126/127) | +| |1|2|3| |K| | | ++-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + +| Extended payload length continued, if payload len == 127 | ++ - - - - - - - - - - - - - - - +-------------------------------+ +| |Masking-key, if MASK set to 1 | ++-------------------------------+-------------------------------+ +| Masking-key (continued) | Payload Data | ++-------------------------------- - - - - - - - - - - - - - - - + +: Payload Data continued ... : ++ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +| Payload Data continued ... | ++---------------------------------------------------------------+ +*/ + +enum close_status_code : uint16_t { + NORMAL_CLOSURE = 1000, + GOING_AWAY = 1001, + PROTOCOL_ERROR = 1002, + CANNOT_ACCEPT = 1003, + INCONSISTENT_DATA = 1007, + POLICY_VIOLATION = 1008, + MESSAGE_TOO_BIG = 1009, + EXPECTED_EXTENSION = 1010, + UNEXPECTED_CONDITION = 1011, + NONE +}; + +enum endpoint_type { + SERVER, + CLIENT +}; + +enum opcode : uint8_t { + CONTINUATION = 0x0, + TEXT = 0x1, + BINARY = 0x2, + CLOSE = 0x8, + PING = 0x9, + PONG = 0xA, + RESERVED = 0xB +}; + +class websocket_exception final : public std::exception { +public: + close_status_code status_code; + + websocket_exception(close_status_code status_code = NORMAL_CLOSURE) noexcept : + status_code(status_code) {} +}; + +/** + * Represent a full fragment header. + * It's initialized with a 2 bytes buffer. If necessary, it can be extended to support longer fragment headers. + */ +class fragment_header { +public: + /// Is this the last fragment + bool fin; + /// Reserved bit 1 (used for extensions) + bool rsv1; + /// Reserved bits 2 and 3 (reserved, never set) + bool rsv23; + /// Opcode of the fragment + websocket::opcode opcode; + /// Is the fragment payload masked + bool masked; + /// The total length of the fragment's payload. Can change when feeding an extended header + uint64_t length; + /// The masking key, if payload is masked + uint32_t mask_key = 0; + + fragment_header() = default; + + fragment_header(temporary_buffer& header) noexcept : + fin(static_cast(header[0] & 128)), + rsv1(static_cast(header[0] & 64)), + rsv23(static_cast(header[0] & 48)), + opcode(static_cast(header[0] & 15)), + masked(static_cast(header[1] & 128)), + length(static_cast(header[1] & 127)) {} + + uint8_t extended_header_length_size() { + uint8_t ret = 0; + if (length == 126) ret += sizeof(uint16_t); // Extended length is 16 bits. + else if (length == 127) ret += sizeof(uint64_t); // Extended length is 64 bits. + return ret; + } + + uint8_t extended_header_size() { + uint8_t ret = extended_header_length_size(); + if (masked) ret += sizeof(uint32_t); // Mask key is 32bits. + return ret; + } + + void feed_extended_header(temporary_buffer& extended_header) { + if (length == 126 && extended_header.size() >= sizeof(uint16_t)) { + uint16_t len; + std::memcpy(&len, extended_header.get(), sizeof(uint16_t)); + length = net::ntoh(len); + } else if (length == 127 && extended_header.size() >= sizeof(uint64_t)) { + std::memcpy(&length, extended_header.get(), sizeof(uint64_t)); + length = net::ntoh(length); + } + if (masked) { + std::memcpy(&mask_key, extended_header.end() - sizeof(uint32_t), sizeof(uint32_t)); + } + } +}; + +class inbound_fragment_base { + friend class input_stream_base; + +public: + fragment_header header; + temporary_buffer message; + + inbound_fragment_base(fragment_header const& header, temporary_buffer& payload) noexcept : + header(header), message(std::move(payload)) { } + + inbound_fragment_base(const inbound_fragment_base&) = delete; + + inbound_fragment_base(inbound_fragment_base&& fragment) noexcept : + header(fragment.header), message(std::move(fragment.message)) {} + + inbound_fragment_base() = default; + + inbound_fragment_base& operator=(const inbound_fragment_base&) = delete; + + inbound_fragment_base& operator=(inbound_fragment_base&& fragment) noexcept { + if (*this != fragment) { + header = fragment.header; + message = std::move(fragment.message); + } + return *this; + } + + /** + * Basic fragment protocol check. Does NOT means that the fragment payload is empty nor advertises EOF. + * @return true if fragment is valid, false otherwise + */ + operator bool() { + return !((header.rsv1 || header.rsv23 || (header.opcode > 2 && header.opcode < 8) + || header.opcode > 10 || (header.opcode > 2 && (!header.fin || message.size() > 125)))); + } +}; + +/** + * This specialization is unused right now but could prove useful when extension comes into play. + */ +template +class inbound_fragment final : public inbound_fragment_base { + using inbound_fragment_base::inbound_fragment_base; + +}; + +} +} +} + diff --git a/http/websocket_handler.hh b/http/websocket_handler.hh new file mode 100644 index 00000000000..a3b55936ec3 --- /dev/null +++ b/http/websocket_handler.hh @@ -0,0 +1,220 @@ +/* + * This file is open source software, licensed to you under the terms + * of the Apache License, Version 2.0 (the "License"). See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. You may not use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +/* + * Copyright 2015 Cloudius Systems + */ + +#pragma once + +#include "handlers.hh" + +namespace seastar { +namespace httpd { +namespace websocket { + +/** + * The most basic handler possible. Your function is called when a new connection arrives, passing the + * connected_websocket. + * Note that, when using this handler, you need to properly support PING/PONG. + * @tparam type whether the underlying connected_websocket is a SERVER or CLIENT one + */ +template +class ws_function_handler : public httpd::handler_websocket_base { + typedef std::function(std::unique_ptr, + websocket::connected_websocket&)> future_ws_handler_function; +public: + ws_function_handler(const future_ws_handler_function& f_handle) + : _f_handle(f_handle) { + } + + future<> handle(const sstring& path, connected_websocket& ws, std::unique_ptr request) override { + return _f_handle(std::move(request), ws); + } + +protected: + future_ws_handler_function _f_handle; +}; + +/** + * Standard websocket handler, enables "event" programming. + * It provides automatic support for responding to PING message, but you can "override" this behavior. + * @tparam type whether the underlying connected_websocket is a SERVER or CLIENT one + */ +template +class ws_handler : public httpd::handler_websocket_base { + typedef std::function(const std::unique_ptr&)> future_ws_on_disconnected; + typedef std::function(const std::unique_ptr&, duplex_stream&)> future_ws_on_connected; + typedef std::function(const std::unique_ptr&, duplex_stream&, + message)> future_ws_on_message; + + typedef std::function&)> void_ws_on_disconnected; + typedef std::function&)> void_ws_on_connected; + typedef std::function&, message message)> void_ws_on_message; + +public: + ws_handler() : _on_connection( + [](const std::unique_ptr&, duplex_stream&) { return make_ready_future(); }), + _on_message([](const std::unique_ptr&, duplex_stream&, + message message) { return make_ready_future(); }), + _on_disconnection([](const std::unique_ptr&) { return make_ready_future(); }), + _on_pong([](const std::unique_ptr&, duplex_stream&, + message message) { return make_ready_future(); }), + _on_ping([](const std::unique_ptr&, duplex_stream& stream, message message) { + message.opcode = opcode::PONG; + return stream.write(std::move(message)).then([&stream] { + return stream.flush(); + }); + }) { + + } + + future<> handle(const sstring& path, connected_websocket& ws, std::unique_ptr req) override { + return do_with(ws.stream(), std::move(req), + [this](duplex_stream& stream, std::unique_ptr& req) { + return _on_connection(req, stream).then([this, &stream, &req] { + return repeat([this, &stream, &req] { + return stream.read().then([this, &req, &stream](message message) { + return on_message_internal(req, stream, message).then([] { + return stop_iteration::no; + }); + }); + }); + }).finally([this, &req] { + _on_disconnection(req); + }); + }); + } + + /** + * Sets the handler for TEXT/BINARY messages. + * This should only be used for non-blocking handler. + * @param handler function that is to be called + */ + void on_message(const void_ws_on_message& handler) { + _on_message = [handler](const std::unique_ptr& req, duplex_stream&, message message) { + handler(req, std::move(message)); + return make_ready_future(); + }; + } + + /** + * Sets the handler for PING messages. + * This should only be used for non-blocking handler. + * @param handler function that is to be called + */ + void on_ping(const void_ws_on_message& handler) { + _on_ping = [handler](const std::unique_ptr& req, duplex_stream&, message message) { + handler(req, std::move(message)); + return make_ready_future(); + }; + } + + /** + * Sets the handler for PONG messages. + * This should only be used for non-blocking handler. + * @param handler function that is to be called + */ + void on_pong(const void_ws_on_message& handler) { + _on_pong = [handler](const std::unique_ptr& req, duplex_stream&, message message) { + handler(req, std::move(message)); + return make_ready_future(); + }; + } + + /** + * Sets the handler for new connections. + * This should only be used for non-blocking handler. + * @param handler function that is to be called + */ + void on_connection(const void_ws_on_connected& handler) { + _on_connection = [handler](const std::unique_ptr& req, duplex_stream&) { + handler(req); + return make_ready_future(); + }; + } + + /** + * Sets the handler for closed connections. + * This should only be used for non-blocking handler. + * @param handler function that is to be called + */ + void on_disconnection(const void_ws_on_disconnected& handler) { + _on_disconnection = [handler](const std::unique_ptr& req) { + handler(req); + return make_ready_future(); + }; + } + + /** + * Sets the handler for TEXT/BINARY messages. + * @param handler the future that is to be called + */ + void on_message_future(const future_ws_on_message& handler) { _on_message = handler; } + + /** + * Sets the handler for PING messages. + * @param handler the future that is to be called + */ + void on_ping_future(const future_ws_on_message& handler) { _on_ping = handler; } + + /** + * Sets the handler for PONG messages. + * @param handler the future that is to be called + */ + void on_pong_future(const future_ws_on_message& handler) { _on_pong = handler; } + + /** + * Sets the handler for new connections. + * @param handler the future that is to be called + */ + void on_connection_future(const future_ws_on_connected& handler) { _on_connection = handler; } + + /** + * Sets the handler for closed connections. + * @param handler the future that is to be called + */ + void on_disconnection_future(const future_ws_on_disconnected& handler) { _on_disconnection = handler; } + +private: + + future<> on_message_internal(const std::unique_ptr& req, duplex_stream& stream, + message& message) { + switch (message.opcode) { + case TEXT: + case BINARY: + return _on_message(req, stream, std::move(message)); + case PING: + return _on_ping(req, stream, std::move(message)); + case PONG: + return _on_pong(req, stream, std::move(message)); + default: //Other opcode are handled at a lower, protocol level. + return stream.close(); //Hum... This is embarrassing + } + } + +protected: + future_ws_on_connected _on_connection; + future_ws_on_message _on_message; + future_ws_on_disconnected _on_disconnection; + future_ws_on_message _on_pong; + future_ws_on_message _on_ping; +}; + +} +} +} \ No newline at end of file diff --git a/http/websocket_message.cc b/http/websocket_message.cc new file mode 100644 index 00000000000..8b943789def --- /dev/null +++ b/http/websocket_message.cc @@ -0,0 +1,82 @@ +/* + * This file is open source software, licensed to you under the terms + * of the Apache License, Version 2.0 (the "License"). See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. You may not use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +/* + * Copyright 2015 Cloudius Systems + */ + +#include "websocket_message.hh" + +namespace seastar { +namespace httpd { +namespace websocket { + +uint8_t message_base::write_header(char* header) { + uint8_t advertised_size = 0; + header[0] = opcode ^ (fin ? 0x80 : 0x0); + + if (payload.size() < 126) { //Size fits 7bits + advertised_size = (uint8_t)payload.size(); + header_size = 2; + } else if (payload.size() <= std::numeric_limits::max()) { //Size is extended to 16bits + advertised_size = 126; + auto s = net::hton(static_cast(payload.size())); + std::memmove(header + sizeof(uint16_t), &s, sizeof(uint16_t)); + header_size = 4; + } else { //Size extended to 64bits + advertised_size = 127; + auto l = net::hton(payload.size()); + std::memmove(header + sizeof(uint16_t), &l, sizeof(uint64_t)); + header_size = 10; + } + return advertised_size; +} + +// Extracted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c +// Licence : https://www.cl.cam.ac.uk/~mgk25/short-license.html +bool utf8_check(const unsigned char* s, size_t length) { + for (const unsigned char* e = s + length; s != e;) { + if (s + 4 <= e && ((*(uint32_t*) s) & 0x80808080) == 0) { + s += 4; + } else { + while (!(*s & 0x80)) { if (++s == e) { return true; } } + if ((s[0] & 0x60) == 0x40) { + if (s + 1 >= e || (s[1] & 0xc0) != 0x80 || (s[0] & 0xfe) == 0xc0) { + return false; + } + s += 2; + } else if ((s[0] & 0xf0) == 0xe0) { + if (s + 2 >= e || (s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 || + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || (s[0] == 0xed && (s[1] & 0xe0) == 0xa0)) { + return false; + } + s += 3; + } else if ((s[0] & 0xf8) == 0xf0) { + if (s + 3 >= e || (s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 || (s[3] & 0xc0) != 0x80 || + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) { + return false; + } + s += 4; + } else { return false; } + } + } + return true; +} + +} +} +} \ No newline at end of file diff --git a/http/websocket_message.hh b/http/websocket_message.hh new file mode 100644 index 00000000000..84d6a39f956 --- /dev/null +++ b/http/websocket_message.hh @@ -0,0 +1,211 @@ +/* + * This file is open source software, licensed to you under the terms + * of the Apache License, Version 2.0 (the "License"). See the NOTICE file + * distributed with this work for additional information regarding copyright + * ownership. You may not use this file except in compliance with the License. + * + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +/* + * Copyright 2015 Cloudius Systems + */ + +#include "websocket_fragment.hh" + +#pragma once + +namespace seastar { + +namespace httpd { + +namespace websocket { + +/** + * Mask or unmask a buffer with the provided masking key. + * Also, it is used as a concatenation utility, effectively masking/unmasking _and_ copying buffers to concatenate + * fragmented messages. + * @param dst pointer to the destination buffer where data are to be copied + * @param src pointer to the currently masked data buffer. + * @param mask pointer to the 4-bytes masking key. + * @param length how many bytes to mask/unmask. + */ +inline void un_mask(char* dst, const char* src, const char* mask, uint64_t length) { + //TODO good candidate for SIMD + // Did try to process 32 bits at a time but got aliasing errors. + for (uint64_t j = 0; j < length; ++j) { + dst[j] = src[j] ^ mask[j % 4]; + } +} + +/** + * Utility function. Check for UTF-8 encoding + * Extracted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c + * Licence : https://www.cl.cam.ac.uk/~mgk25/short-license.html + * @param s pointer to the buffer that is to be checked against UTF-8 encoding + * @param length how many bytes to check. + * @return returns true if the data is a valid UTF-8 encoded string, false otherwise. + */ +bool utf8_check(const unsigned char* s, size_t length); + +class message_base { +public: + websocket::opcode opcode = RESERVED; + uint8_t header_size = 0; + temporary_buffer payload; + ///Only use when sending message, always true when receiving. + bool fin = true; + + message_base(websocket::opcode opcode, temporary_buffer payload, bool fin = true) noexcept : + opcode(opcode), payload(std::move(payload)), fin(fin) { + }; + + message_base(websocket::opcode opcode, sstring message = "", bool fin = true) noexcept : + opcode(opcode), payload(std::move(std::move(message).release())), fin(fin) { + }; + + message_base() = default; + + message_base(const message_base&) = delete; + + message_base(message_base&& other) noexcept : + opcode(other.opcode), header_size(other.header_size), payload(std::move(other.payload)), fin(other.fin) { + } + + void operator=(const message_base&) = delete; + + message_base& operator=(message_base&& other) noexcept { + if (this != &other) { + opcode = other.opcode; + header_size = other.header_size; + payload = std::move(other.payload); + fin = other.fin; + } + return *this; + } + + explicit operator bool() const { return !payload.empty(); } + + uint8_t write_header(char* header); +}; + +template +class message final : public message_base { +}; + +template<> +class message final : public message_base { + using message_base::message_base; +public: + message() = default; + + message(std::vector>& fragments) : + message_base(fragments.front().header.opcode, temporary_buffer( + std::accumulate(fragments.begin(), fragments.end(), 0, + [](size_t x, inbound_fragment & y) { + return x + y.message.size(); + }))) { + uint64_t k = 0; + char* buf = payload.get_write(); + for (unsigned int j = 0; j < fragments.size(); ++j) { + //SERVER never mask data, so we concatenate all fragments to assemble the final message + std::memcpy(buf + k, fragments[j].message.get(), fragments[j].message.size()); + k += fragments[j].message.size(); + } + if (opcode == websocket::opcode::TEXT && !utf8_check((const unsigned char*)buf, payload.size())) { + throw websocket_exception(INCONSISTENT_DATA); + } + } + + message(inbound_fragment & fragment) : message_base(fragment.header.opcode, std::move(fragment.message)) { + if (opcode == websocket::opcode::TEXT && !utf8_check((const unsigned char*)payload.get(), payload.size())) { + throw websocket_exception(INCONSISTENT_DATA); + } + } + + temporary_buffer get_header() { + temporary_buffer header(14); + auto wr = header.get_write(); + + wr[1] = (char)(128 | write_header(wr)); + //FIXME Constructing an independent_bits_engine is expensive. static thread_local ? + static thread_local std::independent_bits_engine::digits, uint32_t> rbe; + uint32_t mask = rbe(); + std::memcpy(wr + header_size, &mask, sizeof(uint32_t)); + header_size += sizeof(uint32_t); + un_mask(payload.get_write(), payload.get(), (char*)(&mask), payload.size()); + header.trim(header_size); + return std::move(header); + } +}; + +template<> +class message final : public message_base { + using message_base::message_base; +public: + + message() = default; + + message(std::vector>& fragments) : + message_base(fragments.front().header.opcode, temporary_buffer( + std::accumulate(fragments.begin(), fragments.end(), 0, + [](size_t x, inbound_fragment & y) { + return x + y.message.size(); + }))) { + uint64_t k = 0; + char* buf = payload.get_write(); + for (unsigned int j = 0; j < fragments.size(); ++j) { + un_mask(buf + k, fragments[j].message.get(), (char*)(&fragments[j].header.mask_key), + fragments[j].message.size()); + k += fragments[j].message.size(); + } + if (opcode == websocket::opcode::TEXT && !utf8_check((const unsigned char*)buf, payload.size())) { + throw websocket_exception(INCONSISTENT_DATA); + } + } + + message(inbound_fragment & fragment) : + message_base(fragment.header.opcode, temporary_buffer(fragment.message.size())) { + un_mask(payload.get_write(), fragment.message.get(), (char*)(&fragment.header.mask_key), payload.size()); + if (opcode == websocket::opcode::TEXT && !utf8_check((const unsigned char*)payload.get(), payload.size())) { + throw websocket_exception(INCONSISTENT_DATA); + } + } + + temporary_buffer get_header() { + temporary_buffer header(14); + auto wr = header.get_write(); + + wr[1] = write_header(wr); + header.trim(header_size); + + return std::move(header); + } +}; + +/** + * Utility function that construct a CLOSE message. + * @tparam type whether the message to create is from a SERVER or CLIENT websocket. + * @param code The close reason + * @return A new CLOSE message that has the 16-bits close reason encoded inside it's payload. + */ +template +static message make_close_message(close_status_code code = NORMAL_CLOSURE) { + if (code == NONE) + return message(CLOSE); + sstring payload(sizeof(uint16_t), '\0'); + *(reinterpret_cast(payload.begin())) = net::hton((uint16_t)code); + return message(CLOSE, std::move(payload)); +} + +} +} +} diff --git a/json/formatter.cc b/json/formatter.cc index 4862e1ad3a8..532685402fc 100644 --- a/json/formatter.cc +++ b/json/formatter.cc @@ -23,6 +23,8 @@ #include "json_elements.hh" #include +namespace seastar { + using namespace std; namespace json { @@ -102,3 +104,5 @@ sstring formatter::to_json(unsigned long l) { } } + +} diff --git a/json/formatter.hh b/json/formatter.hh index 4d55e258b7c..4b68343fdc7 100644 --- a/json/formatter.hh +++ b/json/formatter.hh @@ -30,6 +30,8 @@ #include #include "core/sstring.hh" +namespace seastar { + namespace json { struct jsonable; @@ -173,5 +175,7 @@ private: }; +} + } #endif /* FORMATTER_HH_ */ diff --git a/json/json2code.py b/json/json2code.py index ec4f90c2c21..a772ca1ae9a 100755 --- a/json/json2code.py +++ b/json/json2code.py @@ -297,6 +297,7 @@ def create_h_file(data, hfile_name, api_name, init_method, base_api): 'json_elements.hh"', '"http/json_path.hh"']) add_include(hfile, ['', '']) + open_namespace(hfile, "seastar") open_namespace(hfile, "httpd") open_namespace(hfile, api_name) @@ -417,6 +418,7 @@ def create_h_file(data, hfile_name, api_name, init_method, base_api): fprintln(hfile, '});') fprintln(hfile, enum_definitions) + close_namespace(hfile) close_namespace(hfile) close_namespace(hfile) hfile.write("#endif //__JSON_AUTO_GENERATED_HEADERS\n") diff --git a/json/json_elements.cc b/json/json_elements.cc index 2274f78d3da..33dba3d42e0 100644 --- a/json/json_elements.cc +++ b/json/json_elements.cc @@ -25,6 +25,8 @@ #include #include +namespace seastar { + using namespace std; namespace json { @@ -111,3 +113,5 @@ bool json_base::is_verify() const { } } + +} diff --git a/json/json_elements.hh b/json/json_elements.hh index 3841fe67e00..4d529eebc85 100644 --- a/json/json_elements.hh +++ b/json/json_elements.hh @@ -29,6 +29,8 @@ #include "formatter.hh" #include "core/sstring.hh" +namespace seastar { + namespace json { /** @@ -264,4 +266,6 @@ struct json_return_type { } +} + #endif /* JSON_ELEMENTS_HH_ */ diff --git a/net/api.hh b/net/api.hh index dd0b509fd8d..b6a2d8d5f7a 100644 --- a/net/api.hh +++ b/net/api.hh @@ -35,6 +35,8 @@ #include #include +namespace seastar { + static inline bool is_ip_unspecified(ipv4_addr &addr) { return addr.ip == 0; @@ -138,8 +140,6 @@ public: } /* namespace net */ -// TODO: remove from global NS - /// \addtogroup networking-module /// @{ @@ -209,29 +209,27 @@ public: /// \addtogroup networking-module /// @{ -namespace seastar { - /// The seastar socket. /// /// A \c socket that allows a connection to be established between /// two endpoints. class socket { - std::unique_ptr<::net::socket_impl> _si; + std::unique_ptr _si; public: ~socket(); /// \cond internal - explicit socket(std::unique_ptr<::net::socket_impl> si); + explicit socket(std::unique_ptr si); /// \endcond /// Moves a \c seastar::socket object. socket(socket&&) noexcept; /// Move-assigns a \c seastar::socket object. - seastar::socket& operator=(seastar::socket&&) noexcept; + socket& operator=(socket&&) noexcept; /// Attempts to establish the connection. /// /// \return a \ref connected_socket representing the connection. - future connect(socket_address sa, socket_address local = socket_address(::sockaddr_in{AF_INET, INADDR_ANY, {0}}), seastar::transport proto = seastar::transport::TCP); + future connect(socket_address sa, socket_address local = socket_address(::sockaddr_in{AF_INET, INADDR_ANY, {0}}), transport proto = transport::TCP); /// Stops any in-flight connection attempt. /// /// Cancels the connection attempt if it's still in progress, and @@ -239,8 +237,6 @@ public: void shutdown(); }; -} /* namespace seastar */ - /// @} /// \addtogroup networking-module @@ -283,15 +279,17 @@ public: virtual ~network_stack() {} virtual server_socket listen(socket_address sa, listen_options opts) = 0; // FIXME: local parameter assumes ipv4 for now, fix when adding other AF - future connect(socket_address sa, socket_address local = socket_address(::sockaddr_in{AF_INET, INADDR_ANY, {0}}), seastar::transport proto = seastar::transport::TCP) { + future connect(socket_address sa, socket_address local = socket_address(::sockaddr_in{AF_INET, INADDR_ANY, {0}}), transport proto = transport::TCP) { return socket().connect(sa, local, proto); } - virtual seastar::socket socket() = 0; - virtual ::net::udp_channel make_udp_channel(ipv4_addr addr = {}) = 0; + virtual ::seastar::socket socket() = 0; + virtual net::udp_channel make_udp_channel(ipv4_addr addr = {}) = 0; virtual future<> initialize() { return make_ready_future(); } virtual bool has_per_core_namespace() = 0; }; +} + #endif diff --git a/net/arp.cc b/net/arp.cc index 84b74cef490..9b8dc86a234 100644 --- a/net/arp.cc +++ b/net/arp.cc @@ -21,6 +21,8 @@ #include "arp.hh" +namespace seastar { + namespace net { arp_for_protocol::arp_for_protocol(arp& a, uint16_t proto_num) @@ -82,3 +84,5 @@ arp::process_packet(packet p, ethernet_address from) { } } + +} diff --git a/net/arp.hh b/net/arp.hh index 2cf72f1fb8a..d8bad451c13 100644 --- a/net/arp.hh +++ b/net/arp.hh @@ -30,6 +30,8 @@ #include "core/print.hh" #include +namespace seastar { + namespace net { class arp; @@ -293,4 +295,6 @@ arp_for::handle_request(arp_hdr* ah) { } +} + #endif /* ARP_HH_ */ diff --git a/net/byteorder.hh b/net/byteorder.hh index 52f29f76b94..8a89e9c5319 100644 --- a/net/byteorder.hh +++ b/net/byteorder.hh @@ -28,6 +28,8 @@ #include "core/unaligned.hh" +namespace seastar { + inline uint64_t ntohq(uint64_t v) { return __builtin_bswap64(v); } @@ -117,4 +119,6 @@ T hton(const T& x) { } +} + #endif /* BYTEORDER_HH_ */ diff --git a/net/const.hh b/net/const.hh index 0b9dcaead5d..44b166e25d7 100644 --- a/net/const.hh +++ b/net/const.hh @@ -21,6 +21,9 @@ #ifndef CONST_HH_ #define CONST_HH_ + +namespace seastar { + namespace net { enum class ip_protocol_num : uint8_t { @@ -38,4 +41,7 @@ const uint8_t ipv6_hdr_len_min = 40; const uint16_t ip_packet_len_max = 65535; } + +} + #endif diff --git a/net/dhcp.cc b/net/dhcp.cc index 443e5a4feeb..f13409135ef 100644 --- a/net/dhcp.cc +++ b/net/dhcp.cc @@ -29,6 +29,8 @@ #include "udp.hh" #include "stack.hh" +namespace seastar { + using namespace std::literals::chrono_literals; class net::dhcp::impl : public ip_packet_filter { @@ -465,3 +467,5 @@ net::dhcp::result_type net::dhcp::renew(const lease & l, const steady_clock_type net::ip_packet_filter* net::dhcp::get_ipv4_filter() { return _impl.get(); } + +} diff --git a/net/dhcp.hh b/net/dhcp.hh index 9e79a23b261..1f2eaf58b81 100644 --- a/net/dhcp.hh +++ b/net/dhcp.hh @@ -25,6 +25,8 @@ #include "ip.hh" #include "core/reactor.hh" +namespace seastar { + namespace net { /* @@ -80,4 +82,6 @@ private: } +} + #endif /* NET_DHCP_HH_ */ diff --git a/net/dns.cc b/net/dns.cc index 58b51aa755a..716613e951b 100644 --- a/net/dns.cc +++ b/net/dns.cc @@ -33,7 +33,9 @@ #include "core/gate.hh" #include "util/log.hh" -static seastar::logger dns_log("dns_resolver"); +namespace seastar { + +static logger dns_log("dns_resolver"); class ares_error_category : public std::error_category { public: @@ -103,11 +105,11 @@ struct ares_initializer { } }; -class seastar::net::dns_resolver::impl +class net::dns_resolver::impl : public enable_shared_from_this { public: - impl(::network_stack& stack, const options& opts) + impl(network_stack& stack, const options& opts) : _stack(stack) , _timeout(opts.timeout ? *opts.timeout : std::chrono::milliseconds(5000) /* from ares private */) , _timer(std::bind(&impl::poll_sockets, this)) @@ -188,7 +190,7 @@ class seastar::net::dns_resolver::impl ares_set_socket_functions(_channel, &callbacks, this); // just in case you need printf-debug. - // dns_log.set_level(seastar::log_level::trace); + // dns_log.set_level(log_level::trace); } ~impl() { _timer.cancel(); @@ -619,7 +621,7 @@ class seastar::net::dns_resolver::impl e.avail &= ~POLLIN; use(fd); dns_log.trace("Read {}: data unavailable", fd); - f.then_wrapped([me = shared_from_this(), &e, fd](future<::net::udp_datagram> f) { + f.then_wrapped([me = shared_from_this(), &e, fd](future f) { try { auto d = f.get0(); dns_log.trace("Read {} -> {} bytes", fd, d.get_data().len()); @@ -687,10 +689,10 @@ class seastar::net::dns_resolver::impl return -1; } - ::net::packet p; + net::packet p; p.reserve(len); for (int i = 0; i < len; ++i) { - p = ::net::packet(std::move(p), ::net::fragment{reinterpret_cast(vec[i].iov_base), vec[i].iov_len}); + p = net::packet(std::move(p), net::fragment{reinterpret_cast(vec[i].iov_base), vec[i].iov_len}); } auto bytes = p.len(); @@ -771,11 +773,11 @@ class seastar::net::dns_resolver::impl temporary_buffer indata; }; struct udp_entry { - udp_entry(::net::udp_channel c) + udp_entry(net::udp_channel c) : channel(std::move(c)) { } - ::net::udp_channel channel; - std::experimental::optional<::net::udp_datagram> in;; + net::udp_channel channel; + std::experimental::optional in;; socket_address dst; }; struct sock_entry { @@ -808,7 +810,7 @@ class seastar::net::dns_resolver::impl : tcp(tcp_entry{std::move(s)}) , typ(type::tcp) {} - sock_entry(::net::udp_channel c) + sock_entry(net::udp_channel c) : udp(udp_entry{std::move(c)}) , typ(type::udp) {} @@ -835,7 +837,7 @@ class seastar::net::dns_resolver::impl friend struct dns_call; socket_map _sockets; - ::network_stack & _stack; + network_stack & _stack; ares_channel _channel = {}; uint64_t _ops = 0, _calls = 0; @@ -845,97 +847,99 @@ class seastar::net::dns_resolver::impl bool _closed = false; }; -seastar::net::dns_resolver::dns_resolver() +net::dns_resolver::dns_resolver() : dns_resolver(options()) {} -seastar::net::dns_resolver::dns_resolver(const options& opts) +net::dns_resolver::dns_resolver(const options& opts) : dns_resolver(engine().net(), opts) {} -seastar::net::dns_resolver::dns_resolver(network_stack& stack, const options& opts) - : _impl(::make_shared(stack, opts)) +net::dns_resolver::dns_resolver(network_stack& stack, const options& opts) + : _impl(make_shared(stack, opts)) {} -seastar::net::dns_resolver::~dns_resolver() +net::dns_resolver::~dns_resolver() {} -seastar::net::dns_resolver::dns_resolver(dns_resolver&&) noexcept = default; -seastar::net::dns_resolver& seastar::net::dns_resolver::operator=(dns_resolver&&) noexcept = default; +net::dns_resolver::dns_resolver(dns_resolver&&) noexcept = default; +net::dns_resolver& net::dns_resolver::operator=(dns_resolver&&) noexcept = default; -future seastar::net::dns_resolver::get_host_by_name(const sstring& name, opt_family family) { +future net::dns_resolver::get_host_by_name(const sstring& name, opt_family family) { return _impl->get_host_by_name(name, family.value_or(inet_address::family::INET)); } -future seastar::net::dns_resolver::get_host_by_addr(const inet_address& addr) { +future net::dns_resolver::get_host_by_addr(const inet_address& addr) { return _impl->get_host_by_addr(addr); } -future seastar::net::dns_resolver::resolve_name(const sstring& name, opt_family family) { +future net::dns_resolver::resolve_name(const sstring& name, opt_family family) { return _impl->resolve_name(name, family.value_or(inet_address::family::INET)); } -future seastar::net::dns_resolver::resolve_addr(const inet_address& addr) { +future net::dns_resolver::resolve_addr(const inet_address& addr) { return _impl->resolve_addr(addr); } -future<> seastar::net::dns_resolver::close() { +future<> net::dns_resolver::close() { return _impl->close(); } -static seastar::net::dns_resolver& resolver() { - static thread_local seastar::net::dns_resolver resolver; +static net::dns_resolver& resolver() { + static thread_local net::dns_resolver resolver; return resolver; } -future seastar::net::dns::get_host_by_name(const sstring& name, opt_family family) { +future net::dns::get_host_by_name(const sstring& name, opt_family family) { return resolver().get_host_by_name(name, family.value_or(inet_address::family::INET)); } -future seastar::net::dns::get_host_by_addr(const inet_address& addr) { +future net::dns::get_host_by_addr(const inet_address& addr) { return resolver().get_host_by_addr(addr); } -future seastar::net::dns::resolve_name(const sstring& name, opt_family family) { +future net::dns::resolve_name(const sstring& name, opt_family family) { return resolver().resolve_name(name, family.value_or(inet_address::family::INET)); } -future seastar::net::dns::resolve_addr(const inet_address& addr) { +future net::dns::resolve_addr(const inet_address& addr) { return resolver().resolve_addr(addr); } -future seastar::net::inet_address::hostname() const { +future net::inet_address::hostname() const { return dns::resolve_addr(*this); } -future> seastar::net::inet_address::aliases() const { +future> net::inet_address::aliases() const { return dns::get_host_by_addr(*this).then([](hostent e) { return make_ready_future>(std::move(e.names)); }); } -future seastar::net::inet_address::find( +future net::inet_address::find( const sstring& name) { return dns::resolve_name(name); } -future seastar::net::inet_address::find( +future net::inet_address::find( const sstring& name, family f) { return dns::resolve_name(name, f); } -future> seastar::net::inet_address::find_all( +future> net::inet_address::find_all( const sstring& name) { return dns::get_host_by_name(name).then([](hostent e) { - return make_ready_future>(std::move(e.addr_list)); + return make_ready_future>(std::move(e.addr_list)); }); } -future> seastar::net::inet_address::find_all( +future> net::inet_address::find_all( const sstring& name, family f) { return dns::get_host_by_name(name, f).then([](hostent e) { - return make_ready_future>(std::move(e.addr_list)); + return make_ready_future>(std::move(e.addr_list)); }); } +} + diff --git a/net/dns.hh b/net/dns.hh index fdf402736cb..096b035e83f 100644 --- a/net/dns.hh +++ b/net/dns.hh @@ -31,6 +31,8 @@ #include "../core/shared_ptr.hh" #include "inet_address.hh" +namespace seastar { + struct ipv4_addr; class socket_address; @@ -42,7 +44,6 @@ class network_stack; * */ -namespace seastar { namespace net { /** @@ -83,7 +84,7 @@ public: dns_resolver(); dns_resolver(dns_resolver&&) noexcept; explicit dns_resolver(const options&); - explicit dns_resolver(::network_stack&, const options& = {}); + explicit dns_resolver(network_stack&, const options& = {}); ~dns_resolver(); dns_resolver& operator=(dns_resolver&&) noexcept; @@ -112,7 +113,7 @@ public: future<> close(); private: class impl; - ::shared_ptr _impl; + shared_ptr _impl; }; namespace dns { @@ -128,4 +129,5 @@ future resolve_addr(const inet_address&); } } + } diff --git a/net/dpdk.cc b/net/dpdk.cc index fa2937accc1..a3b16eab1f6 100644 --- a/net/dpdk.cc +++ b/net/dpdk.cc @@ -81,7 +81,9 @@ void* as_cookie(struct rte_pktmbuf_pool_private& p) { typedef void *MARKER[0]; /**< generic marker for a point in a structure */ #endif -using namespace net; +using namespace seastar::net; + +namespace seastar { namespace dpdk { @@ -2303,4 +2305,6 @@ get_dpdk_net_options_description() return opts; } +} + #endif // HAVE_DPDK diff --git a/net/dpdk.hh b/net/dpdk.hh index 25484f1f543..d9c2630b87c 100644 --- a/net/dpdk.hh +++ b/net/dpdk.hh @@ -28,6 +28,8 @@ #include "net.hh" #include "core/sstring.hh" +namespace seastar { + std::unique_ptr create_dpdk_net_device( uint8_t port_idx = 0, uint8_t num_queues = 1, @@ -43,6 +45,8 @@ namespace dpdk { uint32_t qp_mempool_obj_size(bool hugetlbfs_membackend); } +} + #endif // _SEASTAR_DPDK_DEV_H #endif // HAVE_DPDK diff --git a/net/ethernet.cc b/net/ethernet.cc index 2cedccefe7f..c1dfa3991d0 100644 --- a/net/ethernet.cc +++ b/net/ethernet.cc @@ -23,6 +23,8 @@ #include #include +namespace seastar { + namespace net { std::ostream& operator<<(std::ostream& os, ethernet_address ea) { @@ -50,5 +52,5 @@ ethernet_address parse_ethernet_address(std::string addr) } } - +} diff --git a/net/ethernet.hh b/net/ethernet.hh index 595b80340d3..748cfd64ca0 100644 --- a/net/ethernet.hh +++ b/net/ethernet.hh @@ -26,6 +26,8 @@ #include "byteorder.hh" #include "core/print.hh" +namespace seastar { + namespace net { struct ethernet_address { @@ -90,4 +92,6 @@ struct eth_hdr { ethernet_address parse_ethernet_address(std::string addr); } +} + #endif /* ETHERNET_HH_ */ diff --git a/net/inet_address.cc b/net/inet_address.cc index 807cc3bb055..9ae38d271bb 100644 --- a/net/inet_address.cc +++ b/net/inet_address.cc @@ -26,6 +26,8 @@ #include "socket_defs.hh" #include "dns.hh" +namespace seastar { + seastar::net::inet_address::inet_address() : inet_address(::in_addr{ 0, }) {} @@ -122,3 +124,6 @@ std::ostream& operator<<(std::ostream& os, const socket_address& a) { << ":" << a.u.in.sin_port ; } + +} + diff --git a/net/ip.cc b/net/ip.cc index 9e463eb2232..e676c130cd4 100644 --- a/net/ip.cc +++ b/net/ip.cc @@ -27,6 +27,8 @@ #include "toeplitz.hh" #include "core/metrics.hh" +namespace seastar { + namespace net { std::ostream& operator<<(std::ostream& os, ipv4_address a) { @@ -474,3 +476,5 @@ void icmp::received(packet p, ipaddr from, ipaddr to) { } } + +} diff --git a/net/ip.hh b/net/ip.hh index e9f97a33d1b..cd18b5f2289 100644 --- a/net/ip.hh +++ b/net/ip.hh @@ -43,6 +43,8 @@ #include "net/udp.hh" #include "core/metrics_registration.hh" +namespace seastar { + namespace net { class ipv4; @@ -108,15 +110,19 @@ std::ostream& operator<<(std::ostream& os, ipv4_address a); } +} + namespace std { template <> -struct hash { - size_t operator()(net::ipv4_address a) const { return a.ip; } +struct hash { + size_t operator()(seastar::net::ipv4_address a) const { return a.ip; } }; } +namespace seastar { + namespace net { struct ipv4_traits { @@ -363,7 +369,7 @@ private: timer _frag_timer; circular_buffer _packetq; unsigned _pkt_provider_idx = 0; - seastar::metrics::metric_groups _metrics; + metrics::metric_groups _metrics; private: future<> handle_received_packet(packet p, ethernet_address from); bool forward(forward_hash& out_hash_data, packet& p, size_t off); @@ -400,8 +406,8 @@ public: tcp& get_tcp() { return *_tcp._tcp; } ipv4_udp& get_udp() { return _udp; } void register_l4(proto_type id, ip_protocol* handler); - const ::net::hw_features& hw_features() const { return _netif->hw_features(); } - static bool needs_frag(packet& p, ip_protocol_num proto_num, ::net::hw_features hw_features); + const net::hw_features& hw_features() const { return _netif->hw_features(); } + static bool needs_frag(packet& p, ip_protocol_num proto_num, net::hw_features hw_features); void learn(ethernet_address l2, ipv4_address l3) { _arp.learn(l2, l3); } @@ -469,4 +475,6 @@ void arp_learn(ethernet_address l2, ipv4_address l3); } +} + #endif /* IP_HH_ */ diff --git a/net/ip_checksum.cc b/net/ip_checksum.cc index 3652d063f86..3bee20adeec 100644 --- a/net/ip_checksum.cc +++ b/net/ip_checksum.cc @@ -23,6 +23,8 @@ #include "net.hh" #include +namespace seastar { + namespace net { void checksummer::sum(const char* data, size_t len) { @@ -71,4 +73,6 @@ uint16_t ip_checksum(const void* data, size_t len) { } +} + } diff --git a/net/ip_checksum.hh b/net/ip_checksum.hh index 827f7b24ad5..bf388f8386a 100644 --- a/net/ip_checksum.hh +++ b/net/ip_checksum.hh @@ -27,6 +27,8 @@ #include #include +namespace seastar { + namespace net { uint16_t ip_checksum(const void* data, size_t len); @@ -71,4 +73,6 @@ struct checksummer { } +} + #endif /* IP_CHECKSUM_HH_ */ diff --git a/net/native-stack-impl.hh b/net/native-stack-impl.hh index 6ab76fcd922..13167f5e52a 100644 --- a/net/native-stack-impl.hh +++ b/net/native-stack-impl.hh @@ -25,6 +25,8 @@ #include "core/reactor.hh" #include "stack.hh" +namespace seastar { + namespace net { using namespace seastar; @@ -55,7 +57,7 @@ native_server_socket_impl::native_server_socket_impl(Protocol& proto, template future native_server_socket_impl::accept() { - return _listener.accept().then([this] (typename Protocol::connection conn) { + return _listener.accept().then([] (typename Protocol::connection conn) { return make_ready_future( connected_socket(std::make_unique>(make_lw_shared(std::move(conn)))), make_ipv4_address(conn.foreign_ip().ip, conn.foreign_port())); @@ -229,6 +231,6 @@ keepalive_params native_connected_socket_impl::get_keepalive_parameter } - +} #endif /* NET_NATIVE_STACK_IMPL_HH_ */ diff --git a/net/native-stack.cc b/net/native-stack.cc index 756a4c0f4ce..05c7430554e 100644 --- a/net/native-stack.cc +++ b/net/native-stack.cc @@ -41,6 +41,8 @@ #include #include +namespace seastar { + namespace net { using namespace seastar; @@ -221,12 +223,12 @@ future<> native_network_stack::run_dhcp(bool is_renew, const dhcp::lease& res) { auto & ns = static_cast(engine().net()); ns.set_ipv4_packet_filter(f); }).then([this, d = std::move(d), is_renew, res]() mutable { - ::net::dhcp::result_type fut = is_renew ? d.renew(res) : d.discover(); + net::dhcp::result_type fut = is_renew ? d.renew(res) : d.discover(); return fut.then([this, is_renew](bool success, const dhcp::lease & res) { return smp::invoke_on_all([] { auto & ns = static_cast(engine().net()); ns.set_ipv4_packet_filter(nullptr); - }).then(std::bind(&::net::native_network_stack::on_dhcp, this, success, res, is_renew)); + }).then(std::bind(&net::native_network_stack::on_dhcp, this, success, res, is_renew)); }).finally([d = std::move(d)] {}); }); } @@ -336,3 +338,5 @@ network_stack_registrator nns_registrator{ }; } + +} diff --git a/net/native-stack.hh b/net/native-stack.hh index 19646a9d68f..c90140832d4 100644 --- a/net/native-stack.hh +++ b/net/native-stack.hh @@ -25,10 +25,14 @@ #include "net/net.hh" #include +namespace seastar { + namespace net { void create_native_stack(boost::program_options::variables_map opts, std::shared_ptr dev); } +} + #endif /* STACK_HH_ */ diff --git a/net/net.cc b/net/net.cc index 554d85923a0..a0ff4fc692e 100644 --- a/net/net.cc +++ b/net/net.cc @@ -28,6 +28,8 @@ #include "core/metrics.hh" #include "inet_address.hh" +namespace seastar { + using std::move; ipv4_addr::ipv4_addr(const std::string &addr) { @@ -46,10 +48,10 @@ ipv4_addr::ipv4_addr(const std::string &addr) { ipv4_addr::ipv4_addr(const std::string &addr, uint16_t port_) : ip(boost::asio::ip::address_v4::from_string(addr).to_ulong()), port(port_) {} -ipv4_addr::ipv4_addr(const seastar::net::inet_address& a, uint16_t port) +ipv4_addr::ipv4_addr(const net::inet_address& a, uint16_t port) : ipv4_addr([&a] { ::in_addr in = a; - return ::net::ntoh(in.s_addr); + return net::ntoh(in.s_addr); }(), port) {} @@ -88,7 +90,7 @@ qp::qp(bool register_copy_stats, , _stats_plugin_name(stats_plugin_name) , _queue_name(std::string("queue") + std::to_string(qid)) { - namespace sm = seastar::metrics; + namespace sm = metrics; _metrics.add_group(_stats_plugin_name, { // @@ -126,7 +128,7 @@ qp::qp(bool register_copy_stats, // // Tx sm::make_gauge(_queue_name + "_tx_packet_queue_last_bunch", _stats.tx.good.last_bunch, - sm::description(seastar::format("Holds a number of packets sent in the bunch. " + sm::description(format("Holds a number of packets sent in the bunch. " "A high value in conjunction with a high value of a {} indicates an efficient Tx packets bulking.", _queue_name + "_tx_packet_queue"))), // Rx sm::make_gauge(_queue_name + "_rx_packet_queue_last_bunch", _stats.rx.good.last_bunch, @@ -137,10 +139,10 @@ qp::qp(bool register_copy_stats, // // Tx sm::make_derive(_queue_name + "_tx_frags", _stats.tx.good.nr_frags, - sm::description(seastar::format("Counts a number of sent fragments. Divide this value by a {} to get an average number of fragments in a Tx packet.", _queue_name + "_tx_packets"))), + sm::description(format("Counts a number of sent fragments. Divide this value by a {} to get an average number of fragments in a Tx packet.", _queue_name + "_tx_packets"))), // Rx sm::make_derive(_queue_name + "_rx_frags", _stats.rx.good.nr_frags, - sm::description(seastar::format("Counts a number of received fragments. Divide this value by a {} to get an average number of fragments in an Rx packet.", _queue_name + "_rx_packets"))), + sm::description(format("Counts a number of received fragments. Divide this value by a {} to get an average number of fragments in an Rx packet.", _queue_name + "_rx_packets"))), }); if (register_copy_stats) { @@ -150,20 +152,20 @@ qp::qp(bool register_copy_stats, // // Tx sm::make_derive(_queue_name + "_tx_copy_bytes", _stats.tx.good.copy_bytes, - sm::description(seastar::format("Counts a number of sent bytes that were handled in a non-zero-copy way. Divide this value by a {} to get a portion of data sent using a non-zero-copy flow.", _queue_name + "_tx_bytes"))), + sm::description(format("Counts a number of sent bytes that were handled in a non-zero-copy way. Divide this value by a {} to get a portion of data sent using a non-zero-copy flow.", _queue_name + "_tx_bytes"))), // Rx sm::make_derive(_queue_name + "_rx_copy_bytes", _stats.rx.good.copy_bytes, - sm::description(seastar::format("Counts a number of received bytes that were handled in a non-zero-copy way. Divide this value by an {} to get a portion of received data handled using a non-zero-copy flow.", _queue_name + "_rx_bytes"))), + sm::description(format("Counts a number of received bytes that were handled in a non-zero-copy way. Divide this value by an {} to get a portion of received data handled using a non-zero-copy flow.", _queue_name + "_rx_bytes"))), // // Non-zero-copy data fragments rate: DERIVE:0:u // // Tx sm::make_derive(_queue_name + "_tx_copy_frags", _stats.tx.good.copy_frags, - sm::description(seastar::format("Counts a number of sent fragments that were handled in a non-zero-copy way. Divide this value by a {} to get a portion of fragments sent using a non-zero-copy flow.", _queue_name + "_tx_frags"))), + sm::description(format("Counts a number of sent fragments that were handled in a non-zero-copy way. Divide this value by a {} to get a portion of fragments sent using a non-zero-copy flow.", _queue_name + "_tx_frags"))), // Rx sm::make_derive(_queue_name + "_rx_copy_frags", _stats.rx.good.copy_frags, - sm::description(seastar::format("Counts a number of received fragments that were handled in a non-zero-copy way. Divide this value by a {} to get a portion of received fragments handled using a non-zero-copy flow.", _queue_name + "_rx_frags"))), + sm::description(format("Counts a number of received fragments that were handled in a non-zero-copy way. Divide this value by a {} to get a portion of received fragments handled using a non-zero-copy flow.", _queue_name + "_rx_frags"))), }); } @@ -331,3 +333,5 @@ future<> interface::dispatch_packet(packet p) { } } + +} diff --git a/net/net.hh b/net/net.hh index 5ee3ebe7e28..c97552754b9 100644 --- a/net/net.hh +++ b/net/net.hh @@ -33,6 +33,8 @@ #include "const.hh" #include +namespace seastar { + namespace net { class packet; @@ -115,14 +117,14 @@ class interface { std::shared_ptr _dev; subscription _rx; ethernet_address _hw_address; - ::net::hw_features _hw_features; + net::hw_features _hw_features; std::vector _pkt_providers; private: future<> dispatch_packet(packet p); public: explicit interface(std::shared_ptr dev); ethernet_address hw_address() { return _hw_address; } - const ::net::hw_features& hw_features() const { return _hw_features; } + const net::hw_features& hw_features() const { return _hw_features; } subscription register_l3(eth_protocol_num proto_num, std::function (packet p, ethernet_address from)> next, std::function forward); @@ -221,7 +223,7 @@ class qp { protected: const std::string _stats_plugin_name; const std::string _queue_name; - seastar::metrics::metric_groups _metrics; + metrics::metric_groups _metrics; qp_stats _stats; public: @@ -267,7 +269,7 @@ public: void l2receive(packet p) { _queues[engine().cpu_id()]->_rx_stream.produce(std::move(p)); } subscription receive(std::function (packet)> next_packet); virtual ethernet_address hw_address() = 0; - virtual ::net::hw_features hw_features() = 0; + virtual net::hw_features hw_features() = 0; virtual const rss_key_type& rss_key() const { return default_rsskey_40bytes; } virtual uint16_t hw_queues_count() { return 1; } virtual future<> link_ready() { return make_ready_future<>(); } @@ -295,4 +297,6 @@ public: } +} + #endif /* NET_HH_ */ diff --git a/net/packet-data-source.hh b/net/packet-data-source.hh index 6b7593f9e8e..a055205b0b2 100644 --- a/net/packet-data-source.hh +++ b/net/packet-data-source.hh @@ -21,6 +21,8 @@ #include "core/reactor.hh" #include "net/packet.hh" +namespace seastar { + namespace net { class packet_data_source final : public data_source_impl { @@ -49,4 +51,6 @@ input_stream as_input_stream(packet&& p) { } +} + #endif diff --git a/net/packet-util.hh b/net/packet-util.hh index 0ca8bee8dd7..96291e74842 100644 --- a/net/packet-util.hh +++ b/net/packet-util.hh @@ -26,6 +26,8 @@ #include #include +namespace seastar { + namespace net { template @@ -153,4 +155,7 @@ public: }; } + +} + #endif diff --git a/net/packet.cc b/net/packet.cc index 090d10175fc..312e070bca8 100644 --- a/net/packet.cc +++ b/net/packet.cc @@ -25,6 +25,8 @@ #include #include +namespace seastar { + namespace net { constexpr size_t packet::internal_data_size; @@ -120,3 +122,5 @@ std::ostream& operator<<(std::ostream& os, const packet& p) { } } + +} diff --git a/net/packet.hh b/net/packet.hh index 1f0380664f2..4e81febfb13 100644 --- a/net/packet.hh +++ b/net/packet.hh @@ -32,6 +32,8 @@ #include #include +namespace seastar { + namespace net { struct fragment { @@ -617,4 +619,6 @@ packet packet::share(size_t offset, size_t len) { } +} + #endif /* PACKET_HH_ */ diff --git a/net/posix-stack.cc b/net/posix-stack.cc index e1b1ef47bcb..f0345d60956 100644 --- a/net/posix-stack.cc +++ b/net/posix-stack.cc @@ -26,6 +26,8 @@ #include #include +namespace seastar { + namespace net { using namespace seastar; @@ -104,8 +106,11 @@ template class posix_connected_socket_impl final : public connected_socket_impl, posix_connected_socket_operations { lw_shared_ptr _fd; using _ops = posix_connected_socket_operations; + conntrack::handle _handle; private: explicit posix_connected_socket_impl(lw_shared_ptr fd) : _fd(std::move(fd)) {} + explicit posix_connected_socket_impl(lw_shared_ptr fd, conntrack::handle&& handle) + : _fd(std::move(fd)), _handle(std::move(handle)) {} public: virtual data_source source() override { return data_source(std::make_unique< posix_data_source_impl>(_fd)); @@ -182,17 +187,16 @@ template future posix_server_socket_impl::accept() { return _lfd.accept().then([this] (pollable_fd fd, socket_address sa) { - static unsigned balance = 0; - auto cpu = balance++ % smp::count; - + auto cth = _conntrack.get_handle(); + auto cpu = cth.cpu(); if (cpu == engine().cpu_id()) { std::unique_ptr csi( - new posix_connected_socket_impl(make_lw_shared(std::move(fd)))); + new posix_connected_socket_impl(make_lw_shared(std::move(fd)), std::move(cth))); return make_ready_future( connected_socket(std::move(csi)), sa); } else { - smp::submit_to(cpu, [this, fd = std::move(fd.get_file_desc()), sa] () mutable { - posix_ap_server_socket_impl::move_connected_socket(_sa, pollable_fd(std::move(fd)), sa); + smp::submit_to(cpu, [this, fd = std::move(fd.get_file_desc()), sa, cth = std::move(cth)] () mutable { + posix_ap_server_socket_impl::move_connected_socket(_sa, pollable_fd(std::move(fd)), sa, std::move(cth)); }); return accept(); } @@ -243,7 +247,7 @@ posix_ap_server_socket_impl::abort_accept() { template future posix_reuseport_server_socket_impl::accept() { - return _lfd.accept().then([this] (pollable_fd fd, socket_address sa) { + return _lfd.accept().then([] (pollable_fd fd, socket_address sa) { std::unique_ptr csi( new posix_connected_socket_impl(make_lw_shared(std::move(fd)))); return make_ready_future( @@ -258,11 +262,12 @@ posix_reuseport_server_socket_impl::abort_accept() { } template -void posix_ap_server_socket_impl::move_connected_socket(socket_address sa, pollable_fd fd, socket_address addr) { +void +posix_ap_server_socket_impl::move_connected_socket(socket_address sa, pollable_fd fd, socket_address addr, conntrack::handle cth) { auto i = sockets.find(sa.as_posix_sockaddr_in()); if (i != sockets.end()) { try { - std::unique_ptr csi(new posix_connected_socket_impl(make_lw_shared(std::move(fd)))); + std::unique_ptr csi(new posix_connected_socket_impl(make_lw_shared(std::move(fd)), std::move(cth))); i->second.set_value(connected_socket(std::move(csi)), std::move(addr)); } catch (...) { i->second.set_exception(std::current_exception()); @@ -490,3 +495,5 @@ posix_udp_channel::receive() { } } + +} diff --git a/net/posix-stack.hh b/net/posix-stack.hh index 059ae791b9c..e33d3a4e661 100644 --- a/net/posix-stack.hh +++ b/net/posix-stack.hh @@ -23,13 +23,78 @@ #define POSIX_STACK_HH_ #include "core/reactor.hh" +#include "core/sharded.hh" #include "stack.hh" #include +namespace seastar { + namespace net { using namespace seastar; +// We can't keep this in any of the socket servers as instance members, because a connection can +// outlive the socket server. To avoid having the whole socket_server tracked as a shared pointer, +// we will have a conntrack structure. +// +// Right now this class is used by the posix_server_socket_impl, but it could be used by any other. +class conntrack { + class load_balancer { + std::vector _cpu_load; + public: + load_balancer() : _cpu_load(size_t(smp::count), 0) {} + void closed_cpu(shard_id cpu) { + _cpu_load[cpu]--; + } + shard_id next_cpu() { + // FIXME: The naive algorithm will just round robin the connections around the shards. + // A more complex version can keep track of the amount of activity in each connection, + // and use that information. + auto min_el = std::min_element(_cpu_load.begin(), _cpu_load.end()); + auto cpu = shard_id(std::distance(_cpu_load.begin(), min_el)); + _cpu_load[cpu]++; + return cpu; + } + }; + + lw_shared_ptr _lb; + void closed_cpu(shard_id cpu) { + _lb->closed_cpu(cpu); + } +public: + class handle { + shard_id _host_cpu; + shard_id _target_cpu; + foreign_ptr> _lb; + public: + handle() : _lb(nullptr) {} + handle(shard_id cpu, lw_shared_ptr lb) + : _host_cpu(engine().cpu_id()) + , _target_cpu(cpu) + , _lb(make_foreign(std::move(lb))) {} + + handle(const handle&) = delete; + handle(handle&&) = default; + ~handle() { + if (!_lb) { + return; + } + smp::submit_to(_host_cpu, [this, cpu = _target_cpu, lb = std::move(_lb)] { + lb->closed_cpu(cpu); + }); + } + shard_id cpu() { + return _target_cpu; + } + }; + friend class handle; + + conntrack() : _lb(make_lw_shared()) {} + handle get_handle() { + return handle(_lb->next_cpu(), _lb); + } +}; + class posix_data_source_impl final : public data_source_impl { lw_shared_ptr _fd; temporary_buffer _buf; @@ -65,7 +130,7 @@ public: explicit posix_ap_server_socket_impl(socket_address sa) : _sa(sa) {} virtual future accept() override; virtual void abort_accept() override; - static void move_connected_socket(socket_address sa, pollable_fd fd, socket_address addr); + static void move_connected_socket(socket_address sa, pollable_fd fd, socket_address addr, conntrack::handle handle); }; using posix_tcp_ap_server_socket_impl = posix_ap_server_socket_impl; using posix_sctp_ap_server_socket_impl = posix_ap_server_socket_impl; @@ -74,6 +139,7 @@ template class posix_server_socket_impl : public server_socket_impl { socket_address _sa; pollable_fd _lfd; + conntrack _conntrack; public: explicit posix_server_socket_impl(socket_address sa, pollable_fd lfd) : _sa(sa), _lfd(std::move(lfd)) {} virtual future accept(); @@ -101,7 +167,7 @@ public: explicit posix_network_stack(boost::program_options::variables_map opts) : _reuseport(engine().posix_reuseport_available()) {} virtual server_socket listen(socket_address sa, listen_options opts) override; virtual ::seastar::socket socket() override; - virtual ::net::udp_channel make_udp_channel(ipv4_addr addr) override; + virtual net::udp_channel make_udp_channel(ipv4_addr addr) override; static future> create(boost::program_options::variables_map opts) { return make_ready_future>(std::unique_ptr(new posix_network_stack(opts))); } @@ -121,4 +187,6 @@ public: } +} + #endif diff --git a/net/proxy.cc b/net/proxy.cc index d719bbb0b11..32ef9c72aa1 100644 --- a/net/proxy.cc +++ b/net/proxy.cc @@ -19,6 +19,8 @@ #include "proxy.hh" #include +namespace seastar { + namespace net { class proxy_net_device : public qp { @@ -73,3 +75,5 @@ std::unique_ptr create_proxy_net_device(unsigned master_cpu, device* dev) { return std::make_unique(master_cpu, dev); } } + +} diff --git a/net/proxy.hh b/net/proxy.hh index 1fdb24186fe..a3fd86f9b2f 100644 --- a/net/proxy.hh +++ b/net/proxy.hh @@ -22,9 +22,14 @@ #include "net.hh" #include "packet.hh" +namespace seastar { + namespace net { std::unique_ptr create_proxy_net_device(unsigned master_cpu, device* dev); } + +} + #endif diff --git a/net/socket_defs.hh b/net/socket_defs.hh index a274a246e35..0822ee08a1a 100644 --- a/net/socket_defs.hh +++ b/net/socket_defs.hh @@ -25,6 +25,8 @@ #include #include "net/byteorder.hh" +namespace seastar { + struct ipv4_addr; class socket_address { @@ -49,8 +51,6 @@ public: std::ostream& operator<<(std::ostream&, const socket_address&); -namespace seastar { - enum class transport { TCP = IPPROTO_TCP, SCTP = IPPROTO_SCTP @@ -61,10 +61,8 @@ namespace net { class inet_address; } -} - struct listen_options { - seastar::transport proto = seastar::transport::TCP; + transport proto = transport::TCP; bool reuse_address = false; listen_options(bool rua = false) : reuse_address(rua) @@ -80,7 +78,7 @@ struct ipv4_addr { ipv4_addr(uint16_t port) : ip(0), port(port) {} ipv4_addr(const std::string &addr); ipv4_addr(const std::string &addr, uint16_t port); - ipv4_addr(const seastar::net::inet_address&, uint16_t); + ipv4_addr(const net::inet_address&, uint16_t); ipv4_addr(const socket_address &sa) { ip = net::ntoh(sa.u.in.sin_addr.s_addr); @@ -89,3 +87,5 @@ struct ipv4_addr { ipv4_addr(socket_address &&sa) : ipv4_addr(sa) {} }; + +} diff --git a/net/stack.cc b/net/stack.cc index 33a2d0a2a4b..290aabf6483 100644 --- a/net/stack.cc +++ b/net/stack.cc @@ -22,6 +22,8 @@ #include "stack.hh" #include "core/reactor.hh" +namespace seastar { + net::udp_channel::udp_channel() {} @@ -105,22 +107,22 @@ void connected_socket::shutdown_input() { _csi->shutdown_input(); } -seastar::socket::~socket() +socket::~socket() {} -seastar::socket::socket( - std::unique_ptr<::net::socket_impl> si) +socket::socket( + std::unique_ptr si) : _si(std::move(si)) { } -seastar::socket::socket(seastar::socket&&) noexcept = default; -seastar::socket& seastar::socket::operator=(seastar::socket&&) noexcept = default; +socket::socket(socket&&) noexcept = default; +socket& socket::operator=(socket&&) noexcept = default; -future seastar::socket::connect(socket_address sa, socket_address local, transport proto) { +future socket::connect(socket_address sa, socket_address local, transport proto) { return _si->connect(sa, local, proto); } -void seastar::socket::shutdown() { +void socket::shutdown() { _si->shutdown(); } @@ -156,4 +158,4 @@ bool socket_address::operator==(const socket_address& a) const { a.u.in.sin_addr.s_addr); } - +} diff --git a/net/stack.hh b/net/stack.hh index d4299a868f9..c5e5158a227 100644 --- a/net/stack.hh +++ b/net/stack.hh @@ -23,6 +23,8 @@ #include #include "api.hh" +namespace seastar { + namespace net { /// \cond internal @@ -44,7 +46,7 @@ public: class socket_impl { public: virtual ~socket_impl() {} - virtual future connect(socket_address sa, socket_address local, seastar::transport proto = seastar::transport::TCP) = 0; + virtual future connect(socket_address sa, socket_address local, transport proto = transport::TCP) = 0; virtual void shutdown() = 0; }; @@ -69,3 +71,4 @@ public: } +} diff --git a/net/tcp-stack.hh b/net/tcp-stack.hh index 441e9179a83..2b193f90601 100644 --- a/net/tcp-stack.hh +++ b/net/tcp-stack.hh @@ -26,6 +26,8 @@ #include "core/future.hh" +namespace seastar { + class listen_options; class server_socket; class connected_socket; @@ -44,4 +46,6 @@ tcpv4_socket(tcp& tcpv4); } +} + #endif diff --git a/net/tcp.cc b/net/tcp.cc index 91471eab1b3..8d0edcb829c 100644 --- a/net/tcp.cc +++ b/net/tcp.cc @@ -26,6 +26,8 @@ #include "core/future.hh" #include "native-stack-impl.hh" +namespace seastar { + namespace net { void tcp_option::parse(uint8_t* beg1, uint8_t* end1) { @@ -164,3 +166,4 @@ tcpv4_socket(tcp& tcpv4) { } +} diff --git a/net/tcp.hh b/net/tcp.hh index bca50b91f65..fddc98c4f04 100644 --- a/net/tcp.hh +++ b/net/tcp.hh @@ -46,6 +46,8 @@ #define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 #include +namespace seastar { + using namespace std::chrono_literals; namespace net { @@ -520,7 +522,7 @@ private: return size; } uint16_t local_mss() { - return _tcp.hw_features().mtu - ::net::tcp_hdr_len_min - InetTraits::ip_hdr_len_min; + return _tcp.hw_features().mtu - net::tcp_hdr_len_min - InetTraits::ip_hdr_len_min; } void queue_packet(packet p) { _packetq.emplace_back(typename InetTraits::l4packet{_foreign_ip, std::move(p)}); @@ -631,7 +633,7 @@ private: // queue for packets that do not belong to any tcb circular_buffer _packetq; semaphore _queue_space = {212992}; - seastar::metrics::metric_groups _metrics; + metrics::metric_groups _metrics; public: class connection { lw_shared_ptr _tcb; @@ -712,7 +714,7 @@ public: bool forward(forward_hash& out_hash_data, packet& p, size_t off); listener listen(uint16_t port, size_t queue_length = 100); connection connect(socket_address sa); - const ::net::hw_features& hw_features() const { return _inet._inet.hw_features(); } + const net::hw_features& hw_features() const { return _inet._inet.hw_features(); } future<> poll_tcb(ipaddr to, lw_shared_ptr tcb); void add_connected_tcb(lw_shared_ptr tcbp, uint16_t local_port) { auto it = _listening.find(local_port); @@ -731,7 +733,7 @@ template tcp::tcp(inet_type& inet) : _inet(inet) , _e(_rd()) { - namespace sm = seastar::metrics; + namespace sm = metrics; _metrics.add_group("tcp", { sm::make_derive("linearizations", [] { return tcp_packet_merger::linearizations(); }, @@ -782,7 +784,7 @@ auto tcp::connect(socket_address sa) -> connection { connid id; auto src_ip = _inet._inet.host_address(); auto dst_ip = ipv4_address(sa); - auto dst_port = ::net::ntoh(sa.u.in.sin_port); + auto dst_port = net::ntoh(sa.u.in.sin_port); do { src_port = _port_dist(_e); @@ -1524,9 +1526,9 @@ packet tcp::tcb::get_transmit_packet() { uint32_t len; if (_tcp.hw_features().tx_tso) { // FIXME: Info tap device the size of the splitted packet - len = _tcp.hw_features().max_packet_len - ::net::tcp_hdr_len_min - InetTraits::ip_hdr_len_min; + len = _tcp.hw_features().max_packet_len - net::tcp_hdr_len_min - InetTraits::ip_hdr_len_min; } else { - len = std::min(uint16_t(_tcp.hw_features().mtu - ::net::tcp_hdr_len_min - InetTraits::ip_hdr_len_min), _snd.mss); + len = std::min(uint16_t(_tcp.hw_features().mtu - net::tcp_hdr_len_min - InetTraits::ip_hdr_len_min), _snd.mss); } can_send = std::min(can_send, len); // easy case: one small packet @@ -2077,6 +2079,6 @@ typename tcp::tcb::isn_secret tcp::tcb::_isn_secret; } - +} #endif /* TCP_HH_ */ diff --git a/net/tls.cc b/net/tls.cc index ffcf676e512..8565c7849b8 100644 --- a/net/tls.cc +++ b/net/tls.cc @@ -33,6 +33,8 @@ #include "tls.hh" #include "stack.hh" +namespace seastar { + class net::get_impl { public: static std::unique_ptr get(connected_socket s) { @@ -42,7 +44,7 @@ class net::get_impl { class blob_wrapper: public gnutls_datum_t { public: - blob_wrapper(const seastar::tls::blob& in) + blob_wrapper(const tls::blob& in) : gnutls_datum_t { reinterpret_cast(const_cast(in.data())), unsigned(in.size()) } { @@ -115,7 +117,7 @@ static void gtls_chk(int res) { } } -class seastar::tls::dh_params::impl : gnutlsobj { +class tls::dh_params::impl : gnutlsobj { static gnutls_sec_param_t to_gnutls_level(level l) { switch (l) { case level::LEGACY: return GNUTLS_SEC_PARAM_LEGACY; @@ -170,27 +172,27 @@ class seastar::tls::dh_params::impl : gnutlsobj { gnutls_dh_params_t _params; }; -seastar::tls::dh_params::dh_params(level lvl) : _impl(std::make_unique(lvl)) +tls::dh_params::dh_params(level lvl) : _impl(std::make_unique(lvl)) {} -seastar::tls::dh_params::dh_params(const blob& b, x509_crt_format fmt) +tls::dh_params::dh_params(const blob& b, x509_crt_format fmt) : _impl(std::make_unique(b, fmt)) { } -seastar::tls::dh_params::~dh_params() { +tls::dh_params::~dh_params() { } -seastar::tls::dh_params::dh_params(dh_params&&) noexcept = default; -seastar::tls::dh_params& seastar::tls::dh_params::operator=(dh_params&&) noexcept = default; +tls::dh_params::dh_params(dh_params&&) noexcept = default; +tls::dh_params& tls::dh_params::operator=(dh_params&&) noexcept = default; -future seastar::tls::dh_params::from_file( +future tls::dh_params::from_file( const sstring& filename, x509_crt_format fmt) { return read_fully(filename, "dh parameters").then([fmt](temporary_buffer buf) { return make_ready_future(dh_params(blob(buf.get()), fmt)); }); } -class seastar::tls::x509_cert::impl : gnutlsobj { +class tls::x509_cert::impl : gnutlsobj { public: impl() : _cert([] { @@ -218,22 +220,22 @@ class seastar::tls::x509_cert::impl : gnutlsobj { gnutls_x509_crt_t _cert; }; -seastar::tls::x509_cert::x509_cert(::shared_ptr impl) +tls::x509_cert::x509_cert(shared_ptr impl) : _impl(std::move(impl)) { } -seastar::tls::x509_cert::x509_cert(const blob& b, x509_crt_format fmt) - : x509_cert(::make_shared(b, fmt)) { +tls::x509_cert::x509_cert(const blob& b, x509_crt_format fmt) + : x509_cert(::seastar::make_shared(b, fmt)) { } -future seastar::tls::x509_cert::from_file( +future tls::x509_cert::from_file( const sstring& filename, x509_crt_format fmt) { return read_fully(filename, "x509 certificate").then([fmt](temporary_buffer buf) { return make_ready_future(x509_cert(blob(buf.get()), fmt)); }); } -class seastar::tls::certificate_credentials::impl: public gnutlsobj { +class tls::certificate_credentials::impl: public gnutlsobj { public: impl() : _creds([] { @@ -287,7 +289,7 @@ class seastar::tls::certificate_credentials::impl: public gnutlsobj { _dh_params = std::move(cpy); } future<> set_system_trust() { - return seastar::async([this] { + return async([this] { gtls_chk(gnutls_certificate_set_x509_system_trust(_creds)); _load_system_trust = false; // should only do once, for whatever reason }); @@ -335,53 +337,53 @@ class seastar::tls::certificate_credentials::impl: public gnutlsobj { semaphore _system_trust_sem {1}; }; -seastar::tls::certificate_credentials::certificate_credentials() +tls::certificate_credentials::certificate_credentials() : _impl(std::make_unique()) { } -seastar::tls::certificate_credentials::~certificate_credentials() { +tls::certificate_credentials::~certificate_credentials() { } -seastar::tls::certificate_credentials::certificate_credentials( +tls::certificate_credentials::certificate_credentials( certificate_credentials&&) noexcept = default; -seastar::tls::certificate_credentials& seastar::tls::certificate_credentials::operator=( +tls::certificate_credentials& tls::certificate_credentials::operator=( certificate_credentials&&) noexcept = default; -void seastar::tls::certificate_credentials::set_x509_trust(const blob& b, +void tls::certificate_credentials::set_x509_trust(const blob& b, x509_crt_format fmt) { _impl->set_x509_trust(b, fmt); } -void seastar::tls::certificate_credentials::set_x509_crl(const blob& b, +void tls::certificate_credentials::set_x509_crl(const blob& b, x509_crt_format fmt) { _impl->set_x509_crl(b, fmt); } -void seastar::tls::certificate_credentials::set_x509_key(const blob& cert, +void tls::certificate_credentials::set_x509_key(const blob& cert, const blob& key, x509_crt_format fmt) { _impl->set_x509_key(cert, key, fmt); } -void seastar::tls::certificate_credentials::set_simple_pkcs12(const blob& b, +void tls::certificate_credentials::set_simple_pkcs12(const blob& b, x509_crt_format fmt, const sstring& password) { _impl->set_simple_pkcs12(b, fmt, password); } -future<> seastar::tls::abstract_credentials::set_x509_trust_file( +future<> tls::abstract_credentials::set_x509_trust_file( const sstring& cafile, x509_crt_format fmt) { return read_fully(cafile, "trust file").then([this, fmt](temporary_buffer buf) { set_x509_trust(blob(buf.get(), buf.size()), fmt); }); } -future<> seastar::tls::abstract_credentials::set_x509_crl_file( +future<> tls::abstract_credentials::set_x509_crl_file( const sstring& crlfile, x509_crt_format fmt) { return read_fully(crlfile, "crl file").then([this, fmt](temporary_buffer buf) { set_x509_crl(blob(buf.get(), buf.size()), fmt); }); } -future<> seastar::tls::abstract_credentials::set_x509_key_file( +future<> tls::abstract_credentials::set_x509_key_file( const sstring& cf, const sstring& kf, x509_crt_format fmt) { return read_fully(cf, "certificate file").then([this, fmt, kf](temporary_buffer buf) { return read_fully(kf, "key file").then([this, fmt, buf = std::move(buf)](temporary_buffer buf2) { @@ -390,7 +392,7 @@ future<> seastar::tls::abstract_credentials::set_x509_key_file( }); } -future<> seastar::tls::abstract_credentials::set_simple_pkcs12_file( +future<> tls::abstract_credentials::set_simple_pkcs12_file( const sstring& pkcs12file, x509_crt_format fmt, const sstring& password) { return read_fully(pkcs12file, "pkcs12 file").then([this, fmt, password](temporary_buffer buf) { @@ -398,27 +400,27 @@ future<> seastar::tls::abstract_credentials::set_simple_pkcs12_file( }); } -future<> seastar::tls::certificate_credentials::set_system_trust() { +future<> tls::certificate_credentials::set_system_trust() { return _impl->set_system_trust(); } -void seastar::tls::certificate_credentials::set_priority_string(const sstring& prio) { +void tls::certificate_credentials::set_priority_string(const sstring& prio) { _impl->set_priority_string(prio); } -seastar::tls::server_credentials::server_credentials(::shared_ptr dh) +tls::server_credentials::server_credentials(shared_ptr dh) : server_credentials(*dh) {} -seastar::tls::server_credentials::server_credentials(const dh_params& dh) { +tls::server_credentials::server_credentials(const dh_params& dh) { _impl->dh_params(dh); } -seastar::tls::server_credentials::server_credentials(server_credentials&&) noexcept = default; -seastar::tls::server_credentials& seastar::tls::server_credentials::operator=( +tls::server_credentials::server_credentials(server_credentials&&) noexcept = default; +tls::server_credentials& tls::server_credentials::operator=( server_credentials&&) noexcept = default; -void seastar::tls::server_credentials::set_client_auth(client_auth ca) { +void tls::server_credentials::set_client_auth(client_auth ca) { _impl->set_client_auth(ca); } @@ -429,29 +431,29 @@ static const sstring x509_key_key = "x509_key"; static const sstring pkcs12_key = "pkcs12"; static const sstring system_trust = "system_trust"; -typedef std::basic_string> buffer_type; +typedef std::basic_string> buffer_type; -void seastar::tls::credentials_builder::set_dh_level(dh_params::level level) { +void tls::credentials_builder::set_dh_level(dh_params::level level) { _blobs.emplace(dh_level_key, level); } -void seastar::tls::credentials_builder::set_x509_trust(const blob& b, x509_crt_format fmt) { +void tls::credentials_builder::set_x509_trust(const blob& b, x509_crt_format fmt) { _blobs.emplace(x509_trust_key, std::make_pair(b.to_string(), fmt)); } -void seastar::tls::credentials_builder::set_x509_crl(const blob& b, x509_crt_format fmt) { +void tls::credentials_builder::set_x509_crl(const blob& b, x509_crt_format fmt) { _blobs.emplace(x509_crl_key, std::make_pair(b.to_string(), fmt)); } -void seastar::tls::credentials_builder::set_x509_key(const blob& cert, const blob& key, x509_crt_format fmt) { +void tls::credentials_builder::set_x509_key(const blob& cert, const blob& key, x509_crt_format fmt) { _blobs.emplace(x509_key_key, std::make_tuple(cert.to_string(), key.to_string(), fmt)); } -void seastar::tls::credentials_builder::set_simple_pkcs12(const blob& b, x509_crt_format fmt, const sstring& password) { +void tls::credentials_builder::set_simple_pkcs12(const blob& b, x509_crt_format fmt, const sstring& password) { _blobs.emplace(pkcs12_key, std::make_tuple(b.to_string(), fmt, password)); } -future<> seastar::tls::credentials_builder::set_system_trust() { +future<> tls::credentials_builder::set_system_trust() { // TODO / Caveat: // We cannot actually issue a loading of system trust here, // because we have no actual tls context. @@ -465,15 +467,15 @@ future<> seastar::tls::credentials_builder::set_system_trust() { return make_ready_future(); } -void seastar::tls::credentials_builder::set_client_auth(client_auth auth) { +void tls::credentials_builder::set_client_auth(client_auth auth) { _client_auth = auth; } -void seastar::tls::credentials_builder::set_priority_string(const sstring& prio) { +void tls::credentials_builder::set_priority_string(const sstring& prio) { _priority = prio; } -void seastar::tls::credentials_builder::apply_to(certificate_credentials& creds) const { +void tls::credentials_builder::apply_to(certificate_credentials& creds) const { // Could potentially be templated down, but why bother... { auto tr = _blobs.equal_range(x509_trust_key); @@ -520,23 +522,22 @@ void seastar::tls::credentials_builder::apply_to(certificate_credentials& creds) creds._impl->set_client_auth(_client_auth); } -::shared_ptr seastar::tls::credentials_builder::build_certificate_credentials() const { - auto creds = ::make_shared(); +shared_ptr tls::credentials_builder::build_certificate_credentials() const { + auto creds = make_shared(); apply_to(*creds); return creds; } -::shared_ptr seastar::tls::credentials_builder::build_server_credentials() const { +shared_ptr tls::credentials_builder::build_server_credentials() const { auto i = _blobs.find(dh_level_key); if (i == _blobs.end()) { throw std::invalid_argument("No DH level set"); } - auto creds = ::make_shared(dh_params(boost::any_cast(i->second))); + auto creds = make_shared(dh_params(boost::any_cast(i->second))); apply_to(*creds); return creds; } -namespace seastar { namespace tls { /** @@ -555,8 +556,8 @@ class session : public enable_lw_shared_from_this { CLIENT = GNUTLS_CLIENT, SERVER = GNUTLS_SERVER, }; - session(type t, ::shared_ptr creds, - std::unique_ptr<::net::connected_socket_impl> sock, sstring name = { }) + session(type t, shared_ptr creds, + std::unique_ptr sock, sstring name = { }) : _type(t), _sock(std::move(sock)), _creds(std::move(creds)), _hostname( std::move(name)), _in(_sock->source()), _out(_sock->sink()), _in_sem(1), _out_sem(1), _output_pending( @@ -601,9 +602,9 @@ class session : public enable_lw_shared_from_this { } #endif } - session(type t, ::shared_ptr creds, - ::connected_socket sock, sstring name = { }) - : session(t, std::move(creds), ::net::get_impl::get(std::move(sock)), + session(type t, shared_ptr creds, + connected_socket sock, sstring name = { }) + : session(t, std::move(creds), net::get_impl::get(std::move(sock)), std::move(name)) { } @@ -863,11 +864,11 @@ class session : public enable_lw_shared_from_this { // Could not send/recv data immediately. // Ask gnutls which direction we are waiting for. if (gnutls_record_get_direction(*this) == 0) { - return wait_for_input(async).then([this, f = std::forward(f)] { + return wait_for_input(async).then([f = std::forward(f)] { return f(); }); } else { - return wait_for_output(async).then([this, f = std::forward(f)] { + return wait_for_output(async).then([f = std::forward(f)] { return f(); }); } @@ -911,7 +912,7 @@ class session : public enable_lw_shared_from_this { return _out.flush(); } - ::net::connected_socket_impl & socket() const { + net::connected_socket_impl & socket() const { return *_sock; } @@ -919,8 +920,8 @@ class session : public enable_lw_shared_from_this { private: type _type; - std::unique_ptr<::net::connected_socket_impl> _sock; - ::shared_ptr _creds; + std::unique_ptr _sock; + shared_ptr _creds; const sstring _hostname; data_source _in; data_sink _out; @@ -974,7 +975,7 @@ void session::shutdown_with_timer(gnutls_close_request_t how, Func && func) { } } -class tls_connected_socket_impl : public ::net::connected_socket_impl, public session::session_ref { +class tls_connected_socket_impl : public net::connected_socket_impl, public session::session_ref { public: using session_ref::session_ref; @@ -1002,16 +1003,16 @@ class tls_connected_socket_impl : public ::net::connected_socket_impl, public se bool get_keepalive() const override { return _session->socket().get_keepalive(); } - void set_keepalive_parameters(const ::net::keepalive_params& p) override { + void set_keepalive_parameters(const net::keepalive_params& p) override { _session->socket().set_keepalive_parameters(p); } - ::net::keepalive_params get_keepalive_parameters() const override { + net::keepalive_params get_keepalive_parameters() const override { return _session->socket().get_keepalive_parameters(); } }; -class tls_connected_socket_impl::source_impl: public ::data_source_impl, public session::session_ref { +class tls_connected_socket_impl::source_impl: public data_source_impl, public session::session_ref { public: using session_ref::session_ref; private: @@ -1064,13 +1065,13 @@ class tls_connected_socket_impl::source_impl: public ::data_source_impl, public // produced, cannot exist outside the direct life span of // the connected_socket itself. This is consistent with // other sockets in seastar, though I am than less fond of it... -class tls_connected_socket_impl::sink_impl: public ::data_sink_impl, public session::session_ref { +class tls_connected_socket_impl::sink_impl: public data_sink_impl, public session::session_ref { public: using session_ref::session_ref; private: - typedef ::net::fragment* frag_iter; + typedef net::fragment* frag_iter; - future<> put(::net::packet p, frag_iter i, frag_iter e, size_t off = 0) { + future<> put(net::packet p, frag_iter i, frag_iter e, size_t off = 0) { while (i != e) { auto ptr = i->base; auto size = i->size; @@ -1085,7 +1086,7 @@ class tls_connected_socket_impl::sink_impl: public ::data_sink_impl, public sess // If underlying says EAGAIN, we've actually issued // a send, but must wait for completion. return _session->wait_for_output().then( - [this, p = std::move(p), size, i, e, off]() mutable { + [this, p = std::move(p), i, e, off]() mutable { // re-send same buffers (gnutls internal) auto check = gnutls_record_send(*_session, nullptr, 0); return this->put(std::move(p), i, e, off + check); @@ -1105,7 +1106,7 @@ class tls_connected_socket_impl::sink_impl: public ::data_sink_impl, public sess future<> flush() override { return _session->flush(); } - future<> put(::net::packet p) override { + future<> put(net::packet p) override { auto i = p.fragments().begin(); auto e = p.fragments().end(); return put(std::move(p), i, e); @@ -1117,17 +1118,17 @@ class tls_connected_socket_impl::sink_impl: public ::data_sink_impl, public sess } }; -class server_session : public ::net::server_socket_impl { +class server_session : public net::server_socket_impl { public: - server_session(::shared_ptr creds, ::server_socket sock) + server_session(shared_ptr creds, server_socket sock) : _creds(std::move(creds)), _sock(std::move(sock)) { } future accept() override { // We're not actually doing anything very SSL until we get // an actual connection. Then we create a "server" session // and wrap it up after handshaking. - return _sock.accept().then([this](::connected_socket s, ::socket_address addr) { - return wrap_server(_creds, std::move(s)).then([addr](::connected_socket s) { + return _sock.accept().then([this](connected_socket s, socket_address addr) { + return wrap_server(_creds, std::move(s)).then([addr](connected_socket s) { return make_ready_future(std::move(s), addr); }); }); @@ -1136,20 +1137,20 @@ class server_session : public ::net::server_socket_impl { _sock.abort_accept(); } private: - ::shared_ptr _creds; - ::server_socket _sock; + shared_ptr _creds; + server_socket _sock; }; -class tls_socket_impl : public ::net::socket_impl { - ::shared_ptr _cred; +class tls_socket_impl : public net::socket_impl { + shared_ptr _cred; sstring _name; - seastar::socket _socket; + ::seastar::socket _socket; public: - tls_socket_impl(::shared_ptr cred, sstring name) + tls_socket_impl(shared_ptr cred, sstring name) : _cred(cred), _name(std::move(name)), _socket(engine().net().socket()) { } virtual future connect(socket_address sa, socket_address local, transport proto = transport::TCP) override { - return _socket.connect(sa, local, proto).then([cred = std::move(_cred), name = std::move(_name)](::connected_socket s) mutable { + return _socket.connect(sa, local, proto).then([cred = std::move(_cred), name = std::move(_name)](connected_socket s) mutable { return wrap_client(cred, std::move(s), std::move(name)); }); } @@ -1158,58 +1159,58 @@ class tls_socket_impl : public ::net::socket_impl { } }; -} } -data_source seastar::tls::tls_connected_socket_impl::source() { +data_source tls::tls_connected_socket_impl::source() { return data_source(std::make_unique(_session)); } -data_sink seastar::tls::tls_connected_socket_impl::sink() { +data_sink tls::tls_connected_socket_impl::sink() { return data_sink(std::make_unique(_session)); } -future<::connected_socket> seastar::tls::connect(::shared_ptr cred, socket_address sa, sstring name) { - return engine().connect(sa).then([cred = std::move(cred), name = std::move(name)](::connected_socket s) mutable { +future tls::connect(shared_ptr cred, socket_address sa, sstring name) { + return engine().connect(sa).then([cred = std::move(cred), name = std::move(name)](connected_socket s) mutable { return wrap_client(cred, std::move(s), std::move(name)); }); } -future<::connected_socket> seastar::tls::connect(::shared_ptr cred, socket_address sa, socket_address local, sstring name) { - return engine().connect(sa, local).then([cred = std::move(cred), name = std::move(name)](::connected_socket s) mutable { +future tls::connect(shared_ptr cred, socket_address sa, socket_address local, sstring name) { + return engine().connect(sa, local).then([cred = std::move(cred), name = std::move(name)](connected_socket s) mutable { return wrap_client(cred, std::move(s), std::move(name)); }); } -seastar::socket seastar::tls::socket(::shared_ptr cred, sstring name) { - return seastar::socket(std::make_unique(std::move(cred), std::move(name))); +socket tls::socket(shared_ptr cred, sstring name) { + return ::seastar::socket(std::make_unique(std::move(cred), std::move(name))); } -future<::connected_socket> seastar::tls::wrap_client(::shared_ptr cred, ::connected_socket&& s, sstring name) { +future tls::wrap_client(shared_ptr cred, connected_socket&& s, sstring name) { auto sess = make_lw_shared(session::type::CLIENT, std::move(cred), std::move(s), std::move(name)); auto f = sess->handshake(); return f.then([sess = std::move(sess)]() mutable { - ::connected_socket ssls(std::make_unique(std::move(sess))); - return make_ready_future<::connected_socket>(std::move(ssls)); + connected_socket ssls(std::make_unique(std::move(sess))); + return make_ready_future(std::move(ssls)); }); } -future<::connected_socket> seastar::tls::wrap_server(::shared_ptr cred, ::connected_socket&& s) { +future tls::wrap_server(shared_ptr cred, connected_socket&& s) { auto sess = make_lw_shared(session::type::SERVER, std::move(cred), std::move(s)); auto f = sess->handshake(); return f.then([sess = std::move(sess)]() mutable { - ::connected_socket ssls(std::make_unique(std::move(sess))); - return make_ready_future<::connected_socket>(std::move(ssls)); + connected_socket ssls(std::make_unique(std::move(sess))); + return make_ready_future(std::move(ssls)); }); } -::server_socket seastar::tls::listen(::shared_ptr creds, ::socket_address sa, ::listen_options opts) { +server_socket tls::listen(shared_ptr creds, socket_address sa, listen_options opts) { return listen(std::move(creds), engine().listen(sa, opts)); } -::server_socket seastar::tls::listen(::shared_ptr creds, ::server_socket ss) { - ::server_socket ssls(std::make_unique(creds, std::move(ss))); - return ::server_socket(std::move(ssls)); +server_socket tls::listen(shared_ptr creds, server_socket ss) { + server_socket ssls(std::make_unique(creds, std::move(ss))); + return server_socket(std::move(ssls)); } +} diff --git a/net/tls.hh b/net/tls.hh index 4a5e614e565..43d7214acb5 100644 --- a/net/tls.hh +++ b/net/tls.hh @@ -26,6 +26,8 @@ #include "core/future.hh" #include "core/sstring.hh" +namespace seastar { + class connected_socket; /** @@ -38,7 +40,6 @@ class connected_socket; * with OpenSSL or similar. * */ -namespace seastar { namespace tls { enum class x509_crt_format { DER, @@ -92,8 +93,8 @@ namespace tls { static future from_file(const sstring&, x509_crt_format); private: class impl; - x509_cert(::shared_ptr); - ::shared_ptr _impl; + x509_cert(shared_ptr); + shared_ptr _impl; }; class abstract_credentials { @@ -178,7 +179,7 @@ namespace tls { */ class server_credentials : public certificate_credentials { public: - server_credentials(::shared_ptr); + server_credentials(shared_ptr); server_credentials(const dh_params&); server_credentials(server_credentials&&) noexcept; @@ -215,8 +216,8 @@ namespace tls { void apply_to(certificate_credentials&) const; - ::shared_ptr build_certificate_credentials() const; - ::shared_ptr build_server_credentials() const; + shared_ptr build_certificate_credentials() const; + shared_ptr build_server_credentials() const; private: std::multimap _blobs; @@ -233,8 +234,8 @@ namespace tls { * \param name An optional expected server name for the remote end point */ /// @{ - future<::connected_socket> connect(::shared_ptr, ::socket_address, sstring name = {}); - future<::connected_socket> connect(::shared_ptr, ::socket_address, ::socket_address local, sstring name = {}); + future connect(shared_ptr, socket_address, sstring name = {}); + future connect(shared_ptr, socket_address, socket_address local, sstring name = {}); /// @} /** @@ -246,13 +247,13 @@ namespace tls { * \param name An optional expected server name for the remote end point */ /// @{ - ::seastar::socket socket(::shared_ptr, sstring name = {}); + ::seastar::socket socket(shared_ptr, sstring name = {}); /// @} /** Wraps an existing connection in SSL/TLS. */ /// @{ - future<::connected_socket> wrap_client(::shared_ptr, ::connected_socket&&, sstring name = {}); - future<::connected_socket> wrap_server(::shared_ptr, ::connected_socket&&); + future wrap_client(shared_ptr, connected_socket&&, sstring name = {}); + future wrap_server(shared_ptr, connected_socket&&); /// @} /** @@ -262,9 +263,10 @@ namespace tls { * for the server and optionally trust/crl data. */ /// @{ - ::server_socket listen(::shared_ptr, ::socket_address sa, ::listen_options opts = listen_options()); + server_socket listen(shared_ptr, socket_address sa, listen_options opts = listen_options()); // Wraps an existing server socket in SSL - ::server_socket listen(::shared_ptr, ::server_socket); + server_socket listen(shared_ptr, server_socket); /// @} } } + diff --git a/net/toeplitz.hh b/net/toeplitz.hh index 2c0670a3a2d..a162426d67e 100644 --- a/net/toeplitz.hh +++ b/net/toeplitz.hh @@ -46,6 +46,8 @@ #include +namespace seastar { + using rss_key_type = std::vector; // Mellanox Linux's driver key @@ -90,4 +92,7 @@ toeplitz_hash(const rss_key_type& key, const T& data) } return (hash); } + +} + #endif diff --git a/net/udp.cc b/net/udp.cc index 9cfaf0f1d37..06f4092676e 100644 --- a/net/udp.cc +++ b/net/udp.cc @@ -22,6 +22,8 @@ #include "ip.hh" #include "stack.hh" +namespace seastar { + using namespace net; namespace net { @@ -221,3 +223,6 @@ ipv4_udp::make_channel(ipv4_addr addr) { } } /* namespace net */ + +} + diff --git a/net/udp.hh b/net/udp.hh index 608cf56cc8c..aad8d90ce8a 100644 --- a/net/udp.hh +++ b/net/udp.hh @@ -31,6 +31,8 @@ #include "const.hh" #include "net.hh" +namespace seastar { + namespace net { struct udp_hdr { @@ -56,4 +58,6 @@ struct udp_channel_state { } +} + #endif diff --git a/net/virtio.cc b/net/virtio.cc index 8580e2170fa..b1d6264e079 100644 --- a/net/virtio.cc +++ b/net/virtio.cc @@ -45,6 +45,8 @@ #include #endif +namespace seastar { + using namespace net; namespace virtio { @@ -665,7 +667,7 @@ qp::rxq::prepare_buffers() { if (available.try_wait(opportunistic)) { count += opportunistic; } - auto make_buffer_chain = [this] { + auto make_buffer_chain = [] { single_buffer bc; std::unique_ptr buf(reinterpret_cast(malloc(4096))); buffer_and_virt& b = bc[0]; @@ -1017,6 +1019,8 @@ std::unique_ptr create_virtio_net_device(boost::program_options::va return std::make_unique(opts); } +} + // Locks the shared object in memory and forces on-load function resolution. // Needed if the function passed to enable_interrupt() is run at interrupt // time. diff --git a/net/virtio.hh b/net/virtio.hh index 7ee661e12ff..f074b0905e9 100644 --- a/net/virtio.hh +++ b/net/virtio.hh @@ -26,7 +26,11 @@ #include "net.hh" #include "core/sstring.hh" +namespace seastar { + std::unique_ptr create_virtio_net_device(boost::program_options::variables_map opts = boost::program_options::variables_map()); boost::program_options::options_description get_virtio_net_options_description(); +} + #endif /* VIRTIO_HH_ */ diff --git a/net/xenfront.cc b/net/xenfront.cc index 8b4cb3100b9..c40d0af62b3 100644 --- a/net/xenfront.cc +++ b/net/xenfront.cc @@ -49,6 +49,8 @@ #include "xenfront.hh" #include +namespace seastar { + using namespace net; namespace xen { @@ -456,3 +458,5 @@ std::unique_ptr create_xenfront_net_device(boost::program_options:: } } + +} diff --git a/net/xenfront.hh b/net/xenfront.hh index 31ae845db4f..bbd65231bcd 100644 --- a/net/xenfront.hh +++ b/net/xenfront.hh @@ -28,6 +28,8 @@ #include "core/xen/gntalloc.hh" #include "core/queue.hh" +namespace seastar { + namespace xen { std::unique_ptr create_xenfront_net_device(boost::program_options::variables_map opts, bool userspace); @@ -152,4 +154,6 @@ private: } +} + #endif /* XENFRONT_HH_ */ diff --git a/rpc/lz4_compressor.cc b/rpc/lz4_compressor.cc index b6b83b229e0..a647b2d16cc 100644 --- a/rpc/lz4_compressor.cc +++ b/rpc/lz4_compressor.cc @@ -22,6 +22,8 @@ #include "lz4_compressor.hh" #include "core/byteorder.hh" +namespace seastar { + namespace rpc { const sstring lz4_compressor::factory::_name = "LZ4"; @@ -87,3 +89,5 @@ rcv_buf lz4_compressor::decompress(rcv_buf data) { } } + +} diff --git a/rpc/lz4_compressor.hh b/rpc/lz4_compressor.hh index a7314c0e31e..d3fdd525cfc 100644 --- a/rpc/lz4_compressor.hh +++ b/rpc/lz4_compressor.hh @@ -25,6 +25,8 @@ #include "rpc/rpc_types.hh" #include +namespace seastar { + namespace rpc { class lz4_compressor : public compressor { public: @@ -46,3 +48,5 @@ namespace rpc { rcv_buf decompress(rcv_buf data) override; }; } + +} diff --git a/rpc/multi_algo_compressor_factory.hh b/rpc/multi_algo_compressor_factory.hh index 93b39947f57..bc95ffde10f 100644 --- a/rpc/multi_algo_compressor_factory.hh +++ b/rpc/multi_algo_compressor_factory.hh @@ -26,6 +26,8 @@ #include "core/sstring.hh" #include "rpc_types.hh" +namespace seastar { + namespace rpc { // This is meta compressor factory. It gets an array of regular factories that @@ -74,3 +76,5 @@ public: }; } + +} diff --git a/rpc/rpc.cc b/rpc/rpc.cc index 6e5a46fc41b..895c0dca867 100644 --- a/rpc/rpc.cc +++ b/rpc/rpc.cc @@ -1,5 +1,7 @@ #include "rpc.hh" +namespace seastar { + namespace rpc { no_wait_type no_wait; @@ -28,3 +30,5 @@ namespace rpc { } } } + +} diff --git a/rpc/rpc.hh b/rpc/rpc.hh index 9ff77ee5f4a..31cfcfc65dc 100644 --- a/rpc/rpc.hh +++ b/rpc/rpc.hh @@ -34,6 +34,8 @@ #include "rpc/rpc_types.hh" #include "core/byteorder.hh" +namespace seastar { + namespace rpc { using id_type = int64_t; @@ -345,7 +347,7 @@ public: rpc_semaphore _resources_available; std::unordered_set> _conns; promise<> _ss_stopped; - seastar::gate _reply_gate; + gate _reply_gate; server_options _options; public: server(protocol& proto, ipv4_addr addr, resource_limits memory_limit = resource_limits()); @@ -370,14 +372,14 @@ public: f(*c); } } - seastar::gate& reply_gate() { + gate& reply_gate() { return _reply_gate; } friend connection; }; class client : public protocol::connection { - ::seastar::socket _socket; + socket _socket; id_type _message_id = 1; struct reply_handler_base { timer t; @@ -443,8 +445,8 @@ public: * @param local the local address of this client * @param socket the socket object use to connect to the remote address */ - client(protocol& proto, seastar::socket socket, ipv4_addr addr, ipv4_addr local = ipv4_addr()); - client(protocol& proto, client_options options, seastar::socket socket, ipv4_addr addr, ipv4_addr local = ipv4_addr()); + client(protocol& proto, socket socket, ipv4_addr addr, ipv4_addr local = ipv4_addr()); + client(protocol& proto, client_options options, socket socket, ipv4_addr addr, ipv4_addr local = ipv4_addr()); stats get_stats() const { stats res = this->_stats; @@ -547,4 +549,6 @@ private: }; } +} + #include "rpc_impl.hh" diff --git a/rpc/rpc_impl.hh b/rpc/rpc_impl.hh index 0d95a1a270a..56dfd22b25d 100644 --- a/rpc/rpc_impl.hh +++ b/rpc/rpc_impl.hh @@ -32,6 +32,8 @@ #include #include "net/packet-data-source.hh" +namespace seastar { + namespace rpc { enum class exception_type : uint32_t { @@ -203,19 +205,19 @@ inline void do_marshall(Serializer& serializer, Output& out, const T&... args) { (void)std::initializer_list{(marshall_one(serializer, out, args), 1)...}; } -static inline seastar::memory_output_stream make_serializer_stream(snd_buf& output) { +static inline memory_output_stream make_serializer_stream(snd_buf& output) { auto* b = boost::get>(&output.bufs); if (b) { - return seastar::memory_output_stream(seastar::memory_output_stream::simple(b->get_write(), b->size())); + return memory_output_stream(memory_output_stream::simple(b->get_write(), b->size())); } else { auto& ar = boost::get>>(output.bufs); - return seastar::memory_output_stream(seastar::memory_output_stream::fragmented(ar.begin(), output.size)); + return memory_output_stream(memory_output_stream::fragmented(ar.begin(), output.size)); } } template inline snd_buf marshall(Serializer& serializer, size_t head_space, const T&... args) { - seastar::measuring_output_stream measure; + measuring_output_stream measure; do_marshall(serializer, measure, args...); snd_buf ret(measure.size() + head_space); auto out = make_serializer_stream(ret); @@ -428,7 +430,7 @@ inline future<> reply(wait_type, future&& ret, int64_t msg_id, lw_s if (!client->error()) { snd_buf data; try { - data = ::apply(marshall, + data = apply(marshall, std::tuple_cat(std::make_tuple(std::ref(client->serializer()), 12), std::move(ret.get()))); } catch (std::exception& ex) { uint32_t len = std::strlen(ex.what()); @@ -496,13 +498,13 @@ auto recv_helper(signature sig, Func&& func, WantClientInfo wci // note: apply is executed asynchronously with regards to networking so we cannot chain futures here by doing "return apply()" auto f = client->wait_for_resources(memory_consumed, timeout).then([client, timeout, msg_id, data = std::move(data), &func] (auto permit) mutable { try { - seastar::with_gate(client->get_server().reply_gate(), [client, timeout, msg_id, data = std::move(data), permit = std::move(permit), &func] () mutable { + with_gate(client->get_server().reply_gate(), [client, timeout, msg_id, data = std::move(data), permit = std::move(permit), &func] () mutable { auto args = unmarshall(client->serializer(), std::move(data)); return apply(func, client->info(), timeout, WantClientInfo(), WantTimePoint(), signature(), std::move(args)).then_wrapped([client, timeout, msg_id, permit = std::move(permit)] (futurize_t ret) mutable { return reply(wait_style(), std::move(ret), msg_id, client, timeout).then([permit = std::move(permit)] {}); }); }); - } catch (seastar::gate_closed_exception&) {/* ignore */ } + } catch (gate_closed_exception&) {/* ignore */ } }); if (timeout) { @@ -968,10 +970,10 @@ future<> protocol::server::connection::process() { write_le(p + 4, uint32_t(8)); write_le(p + 8, uint64_t(type)); try { - seastar::with_gate(this->_server._reply_gate, [this, timeout, msg_id, data = std::move(data), permit = std::move(permit)] () mutable { + with_gate(this->_server._reply_gate, [this, timeout, msg_id, data = std::move(data), permit = std::move(permit)] () mutable { return this->respond(-msg_id, std::move(data), timeout).then([c = this->shared_from_this(), permit = std::move(permit)] {}); }); - } catch(seastar::gate_closed_exception&) {/* ignore */} + } catch(gate_closed_exception&) {/* ignore */} }); } } @@ -1042,7 +1044,7 @@ protocol::client::read_response_frame_compressed(input_stre } template -protocol::client::client(protocol& proto, client_options ops, seastar::socket socket, ipv4_addr addr, ipv4_addr local) +protocol::client::client(protocol& proto, client_options ops, socket socket, ipv4_addr addr, ipv4_addr local) : protocol::connection(proto), _socket(std::move(socket)), _server_addr(addr), _options(ops) { _socket.connect(addr, local).then([this, ops = std::move(ops)] (connected_socket fd) { fd.set_nodelay(true); @@ -1115,8 +1117,10 @@ protocol::client::client(protocol& pro {} template -protocol::client::client(protocol& proto, seastar::socket socket, ipv4_addr addr, ipv4_addr local) +protocol::client::client(protocol& proto, socket socket, ipv4_addr addr, ipv4_addr local) : client(proto, client_options{}, std::move(socket), addr, local) {} } + +} diff --git a/rpc/rpc_types.hh b/rpc/rpc_types.hh index 476da957dfb..df5605ca0df 100644 --- a/rpc/rpc_types.hh +++ b/rpc/rpc_types.hh @@ -32,6 +32,8 @@ #include "core/simple-stream.hh" #include "core/lowres_clock.hh" +namespace seastar { + namespace rpc { using rpc_clock_type = lowres_clock; @@ -180,13 +182,13 @@ struct snd_buf { temporary_buffer& front(); }; -static inline seastar::memory_input_stream make_deserializer_stream(rcv_buf& input) { +static inline memory_input_stream make_deserializer_stream(rcv_buf& input) { auto* b = boost::get>(&input.bufs); if (b) { - return seastar::memory_input_stream(seastar::memory_input_stream::simple(b->begin(), b->size())); + return memory_input_stream(memory_input_stream::simple(b->begin(), b->size())); } else { auto& ar = boost::get>>(input.bufs); - return seastar::memory_input_stream(seastar::memory_input_stream::fragmented(ar.begin(), input.size)); + return memory_input_stream(memory_input_stream::fragmented(ar.begin(), input.size)); } } @@ -210,3 +212,6 @@ public: }; } // namespace rpc + +} + diff --git a/scripts/perftune.py b/scripts/perftune.py index 6748086be9f..c25c2a92959 100755 --- a/scripts/perftune.py +++ b/scripts/perftune.py @@ -14,18 +14,15 @@ import subprocess import sys -_pid_dir = re.compile(r"\d+") -def _is_pid_dir(candidate): - return _pid_dir.match(candidate) and os.path.isdir(os.path.join("/proc", candidate)) +def run_one_command(prog_args, my_stderr=None, check=True): + proc = subprocess.Popen(prog_args, stdout = subprocess.PIPE, stderr = my_stderr) + outs, errs = proc.communicate() + outs = str(outs, 'utf-8') -def pids(): - return [ int(x) for x in os.listdir("/proc") if _is_pid_dir(x) ] + if check and proc.returncode != 0: + raise subprocess.CalledProcessError(returncode=proc.returncode, cmd=" ".join(prog_args), output=outs, stderr=errs) -def pid_name(pidno): - return open(os.path.join("/proc", str(pidno), "comm"), "r").read().strip() - -def run_one_command(prog_args, my_stderr=None): - return str(subprocess.check_output(prog_args, stderr=my_stderr), 'utf-8') + return outs def run_hwloc_distrib(prog_args): """ @@ -60,7 +57,7 @@ def distribute_irqs(irqs, cpu_mask): set_one_mask("/proc/irq/{}/smp_affinity".format(irqs[i]), mask) def is_process_running(name): - return any([pid_name(pid) == name for pid in pids()]) + return len(list(filter(lambda ps_line : not re.search('', ps_line), run_one_command(['ps', '--no-headers', '-C', name], check=False).splitlines()))) > 0 def restart_irqbalance(banned_irqs): """ @@ -73,7 +70,7 @@ def restart_irqbalance(banned_irqs): banned_irqs_list = list(banned_irqs) # If there is nothing to ban - quit - if len(banned_irqs_list) == 0: + if not banned_irqs_list: return # return early if irqbalance is not running @@ -86,6 +83,11 @@ def restart_irqbalance(banned_irqs): config_file = '/etc/sysconfig/irqbalance' options_key = 'IRQBALANCE_ARGS' systemd = True + elif os.path.exists('/etc/conf.d/irqbalance'): + config_file = '/etc/conf.d/irqbalance' + options_key = 'IRQBALANCE_OPTS' + with open('/proc/1/comm', 'r') as comm: + systemd = 'systemd' in comm.read() else: print("Unknown system configuration - not restarting irqbalance!") print("You have to prevent it from moving IRQs {} manually!".format(banned_irqs_list)) @@ -108,7 +110,7 @@ def restart_irqbalance(banned_irqs): # Search for the original options line opt_lines = list(filter(lambda line : re.search("^\s*{}".format(options_key), line), cfile_lines)) - if len(opt_lines) == 0: + if not opt_lines: new_options = "{}=\"".format(options_key) elif len(opt_lines) == 1: # cut the last " @@ -476,13 +478,14 @@ def __learn_irqs_one(self, iface): Otherwise, we will use only IRQs which names fit one of the patterns above. """ irqs2procline = get_irqs2procline_map() - all_irqs = learn_all_irqs_one("/sys/class/net/{}/device".format(iface), irqs2procline, iface) + # filter 'all_irqs' to only reference valid keys from 'irqs2procline' and avoid an IndexError on the 'irqs' search below + all_irqs = set(learn_all_irqs_one("/sys/class/net/{}/device".format(iface), irqs2procline, iface)).intersection(irqs2procline.keys()) fp_irqs_re = re.compile("\-TxRx\-|\-fp\-|\-Tx\-Rx\-") irqs = list(filter(lambda irq : fp_irqs_re.search(irqs2procline[irq]), all_irqs)) - if len(irqs) > 0: + if irqs: return irqs else: - return all_irqs + return list(all_irqs) def __learn_irqs(self): """ @@ -548,6 +551,9 @@ class SupportedDiskTypes(enum.IntEnum): def __init__(self, args): super().__init__(args) + if not (self.args.dirs or self.args.devs): + raise Exception("'disks' tuning was requested but neither directories nor storage devices were given") + self.__pyudev_ctx = pyudev.Context() self.__dir2disks = self.__learn_directories() self.__disk2irqs = self.__learn_irqs() @@ -568,7 +574,7 @@ def tune(self): mode_cpu_mask = PerfTunerBase.irqs_cpu_mask_for_mode(self.mode, self.args.cpu_mask) non_nvme_disks, non_nvme_irqs = self.__disks_info_by_type(DiskPerfTuner.SupportedDiskTypes.non_nvme) - if len(non_nvme_disks) > 0: + if non_nvme_disks: print("Setting non-NVMe disks: {}...".format(", ".join(non_nvme_disks))) distribute_irqs(non_nvme_irqs, mode_cpu_mask) self.__tune_disks(non_nvme_disks) @@ -576,7 +582,7 @@ def tune(self): print("No non-NVMe disks to tune") nvme_disks, nvme_irqs = self.__disks_info_by_type(DiskPerfTuner.SupportedDiskTypes.nvme) - if len(nvme_disks) > 0: + if nvme_disks: print("Setting NVMe disks: {}...".format(", ".join(nvme_disks))) distribute_irqs(nvme_irqs, self.args.cpu_mask) self.__tune_disks(nvme_disks) @@ -588,6 +594,11 @@ def _get_def_mode(self): """ Return a default configuration mode. """ + # if the only disks we are tuning are NVMe disks - return the MQ mode + non_nvme_disks, non_nvme_irqs = self.__disks_info_by_type(DiskPerfTuner.SupportedDiskTypes.non_nvme) + if not non_nvme_disks: + return PerfTunerBase.SupportedModes.mq + num_cores = int(run_hwloc_calc(['--number-of', 'core', 'machine:0', '--restrict', self.args.cpu_mask])) num_PUs = int(run_hwloc_calc(['--number-of', 'PU', 'machine:0', '--restrict', self.args.cpu_mask])) if num_PUs <= 4: @@ -642,6 +653,9 @@ def __group_disks_info_by_type(self): for irq in irqs: non_nvme_irqs.add(irq) + if not (nvme_disks or non_nvme_disks): + raise Exception("'disks' tuning was requested but no disks were found") + disks_info_by_type[DiskPerfTuner.SupportedDiskTypes.nvme] = ( list(nvme_disks), list(nvme_irqs) ) disks_info_by_type[DiskPerfTuner.SupportedDiskTypes.non_nvme] = ( list(non_nvme_disks), list(non_nvme_irqs) ) @@ -662,16 +676,18 @@ def __learn_directory(self, directory, recur=False): return [] try: - udev_obj = pyudev.Devices().from_device_number(self.__pyudev_ctx, 'block', os.stat(directory).st_dev) + udev_obj = pyudev.Device.from_device_number(self.__pyudev_ctx, 'block', os.stat(directory).st_dev) return self.__get_phys_devices(udev_obj) except: # handle cases like ecryptfs where the directory is mounted to another directory and not to some block device filesystem = run_one_command(['df', '-P', directory]).splitlines()[-1].split()[0].strip() if not re.search(r'^/dev/', filesystem): devs = self.__learn_directory(filesystem, True) + else: + raise Exception("Logic error: failed to create a udev device while 'df -P' {} returns a {}".format(directory, filesystem)) # log error only for the original directory - if not recur and len(devs) == 0: + if not recur and not devs: print("Can't get a block device for {} - skipping".format(directory)) return devs @@ -679,7 +695,7 @@ def __learn_directory(self, directory, recur=False): def __get_phys_devices(self, udev_obj): # if device is a virtual device - the underlying physical devices are going to be its slaves if re.search(r'virtual', udev_obj.sys_path): - return list(itertools.chain.from_iterable([ self.__get_phys_devices(pyudev.Devices.from_device_file(self.__pyudev_ctx, "/dev/{}".format(slave))) for slave in os.listdir(os.path.join(udev_obj.sys_path, 'slaves')) ])) + return list(itertools.chain.from_iterable([ self.__get_phys_devices(pyudev.Device.from_device_file(self.__pyudev_ctx, "/dev/{}".format(slave))) for slave in os.listdir(os.path.join(udev_obj.sys_path, 'slaves')) ])) else: # device node is something like /dev/sda1 - we need only the part without /dev/ return [ re.match(r'/dev/(\S+\d*)', udev_obj.device_node).group(1) ] @@ -695,7 +711,7 @@ def __learn_irqs(self): if device in disk2irqs.keys(): continue - udev_obj = pyudev.Devices.from_device_file(self.__pyudev_ctx, "/dev/{}".format(device)) + udev_obj = pyudev.Device.from_device_file(self.__pyudev_ctx, "/dev/{}".format(device)) dev_sys_path = udev_obj.sys_path split_sys_path = list(pathlib.PurePath(dev_sys_path).parts) @@ -726,7 +742,7 @@ def __tune_one_feature(self, dev_node, path_creator, value, tuned_devs_set): If there isn't such ancestor - return False. """ - udev = pyudev.Devices.from_device_file(pyudev.Context(), dev_node) + udev = pyudev.Device.from_device_file(pyudev.Context(), dev_node) feature_file = path_creator(udev.sys_path) if os.path.exists(feature_file): if not dev_node in tuned_devs_set: @@ -814,7 +830,7 @@ def names(): # if nothing needs to be configured - quit if args.tune is None: - sys.exit(0) + sys.exit("ERROR: At least one tune mode MUST be given.") try: tuners = [] diff --git a/tests/alloc_test.cc b/tests/alloc_test.cc index 7118eec3ed0..3e7bb1a3255 100644 --- a/tests/alloc_test.cc +++ b/tests/alloc_test.cc @@ -24,7 +24,7 @@ #include "core/reactor.hh" #include - +using namespace seastar; SEASTAR_TEST_CASE(alloc_almost_all_and_realloc_it_with_a_smaller_size) { #ifndef DEFAULT_ALLOCATOR diff --git a/tests/allocator_test.cc b/tests/allocator_test.cc index be563b80fa8..7701935a22b 100644 --- a/tests/allocator_test.cc +++ b/tests/allocator_test.cc @@ -31,6 +31,8 @@ #include #include +using namespace seastar; + template void test_aligned_allocator() { using aptr = std::unique_ptr; diff --git a/tests/blkdiscard_test.cc b/tests/blkdiscard_test.cc index e42e7cf3fb6..8d0d17c0558 100644 --- a/tests/blkdiscard_test.cc +++ b/tests/blkdiscard_test.cc @@ -25,6 +25,8 @@ #include "core/file.hh" #include "core/reactor.hh" +using namespace seastar; + namespace bpo = boost::program_options; struct file_test { diff --git a/tests/checked_ptr_test.cc b/tests/checked_ptr_test.cc index 3e78c0fa971..43aa378df2f 100644 --- a/tests/checked_ptr_test.cc +++ b/tests/checked_ptr_test.cc @@ -26,6 +26,8 @@ #include "core/checked_ptr.hh" #include "core/weak_ptr.hh" +using namespace seastar; + struct my_st : public weakly_referencable { my_st(int a_) : a(a_) {} int a; diff --git a/tests/chunked_fifo_test.cc b/tests/chunked_fifo_test.cc index ef349e46b19..8acac062a8d 100644 --- a/tests/chunked_fifo_test.cc +++ b/tests/chunked_fifo_test.cc @@ -29,6 +29,8 @@ #include #include "core/circular_buffer.hh" +using namespace seastar; + BOOST_AUTO_TEST_CASE(chunked_fifo_small) { // Check all the methods of chunked_fifo but with a trivial type (int) and // only a few elements - and in particular a single chunk is enough. diff --git a/tests/circular_buffer_test.cc b/tests/circular_buffer_test.cc index a52bee8f3d5..d8fe6ab40ec 100644 --- a/tests/circular_buffer_test.cc +++ b/tests/circular_buffer_test.cc @@ -28,6 +28,8 @@ #include #include "core/circular_buffer.hh" +using namespace seastar; + BOOST_AUTO_TEST_CASE(test_erasing) { circular_buffer buf; diff --git a/tests/connect_test.cc b/tests/connect_test.cc index d533f0319d0..d1672fb52c6 100644 --- a/tests/connect_test.cc +++ b/tests/connect_test.cc @@ -2,6 +2,9 @@ #include "net/ip.hh" +#include + +using namespace seastar; using namespace net; SEASTAR_TEST_CASE(test_connection_attempt_is_shutdown) { @@ -20,7 +23,11 @@ SEASTAR_TEST_CASE(test_connection_attempt_is_shutdown) { } SEASTAR_TEST_CASE(test_unconnected_socket_shutsdown_established_connection) { - auto sa = make_ipv4_address({"127.0.0.1", 10001}); + // Use a random port to reduce chance of conflict. + // TODO: retry a few times on failure. + std::random_device rnd; + auto distr = std::uniform_int_distribution(12000, 65000); + auto sa = make_ipv4_address({"127.0.0.1", distr(rnd)}); return do_with(engine().net().listen(sa, listen_options()), [sa] (auto& listener) { listener.accept(); auto unconn = engine().net().socket(); diff --git a/tests/defer_test.cc b/tests/defer_test.cc index dac5a14594e..1f21c2d917d 100644 --- a/tests/defer_test.cc +++ b/tests/defer_test.cc @@ -24,6 +24,8 @@ #include #include "util/defer.hh" +using namespace seastar; + BOOST_AUTO_TEST_CASE(test_defer_does_not_run_when_canceled) { bool ran = false; { diff --git a/tests/directory_test.cc b/tests/directory_test.cc index e031acbef34..cc6d71d20d3 100644 --- a/tests/directory_test.cc +++ b/tests/directory_test.cc @@ -25,6 +25,8 @@ #include "core/print.hh" #include "core/shared_ptr.hh" +using namespace seastar; + int main(int ac, char** av) { class lister { file _f; diff --git a/tests/distributed_test.cc b/tests/distributed_test.cc index e18eb39387a..b9ad65f57dd 100644 --- a/tests/distributed_test.cc +++ b/tests/distributed_test.cc @@ -25,6 +25,8 @@ #include "core/future-util.hh" #include "core/sleep.hh" +using namespace seastar; + struct async : public seastar::async_sharded_service { thread_local static bool deleted; ~async() { diff --git a/tests/echotest.cc b/tests/echotest.cc index 5232985663f..47b97de7cec 100644 --- a/tests/echotest.cc +++ b/tests/echotest.cc @@ -28,6 +28,7 @@ #include #include +using namespace seastar; using namespace net; void dump_packet(const packet& p) { diff --git a/tests/execution_stage_test.cc b/tests/execution_stage_test.cc index 3ae81ad42ee..fd4aad39a50 100644 --- a/tests/execution_stage_test.cc +++ b/tests/execution_stage_test.cc @@ -27,6 +27,8 @@ #include "test-utils.hh" #include "core/execution_stage.hh" +using namespace seastar; + static std::random_device rd; SEASTAR_TEST_CASE(test_create_stage_from_lvalue_function_object) { diff --git a/tests/fair_queue_test.cc b/tests/fair_queue_test.cc index fff687438e9..da759965653 100644 --- a/tests/fair_queue_test.cc +++ b/tests/fair_queue_test.cc @@ -33,6 +33,7 @@ #include #include +using namespace seastar; using namespace std::chrono_literals; struct test_env { diff --git a/tests/fileiotest.cc b/tests/fileiotest.cc index 64638867f0a..c87030a9848 100644 --- a/tests/fileiotest.cc +++ b/tests/fileiotest.cc @@ -25,6 +25,8 @@ #include "core/file.hh" #include "core/reactor.hh" +using namespace seastar; + struct file_test { file_test(file&& f) : f(std::move(f)) {} file f; diff --git a/tests/foreign_ptr_test.cc b/tests/foreign_ptr_test.cc index 429e6c4994f..661e5f2565e 100644 --- a/tests/foreign_ptr_test.cc +++ b/tests/foreign_ptr_test.cc @@ -23,6 +23,9 @@ #include "core/distributed.hh" #include "core/shared_ptr.hh" +#include "core/thread.hh" + +using namespace seastar; SEASTAR_TEST_CASE(make_foreign_ptr_from_lw_shared_ptr) { auto p = make_foreign(make_lw_shared("foo")); @@ -35,3 +38,13 @@ SEASTAR_TEST_CASE(make_foreign_ptr_from_shared_ptr) { BOOST_REQUIRE(p->size() == 3); return make_ready_future<>(); } + + +SEASTAR_TEST_CASE(foreign_ptr_copy_test) { + return seastar::async([] { + auto ptr = make_foreign(make_shared("foo")); + BOOST_REQUIRE(ptr->size() == 3); + auto ptr2 = ptr.copy().get0(); + BOOST_REQUIRE(ptr2->size() == 3); + }); +} diff --git a/tests/fstream_test.cc b/tests/fstream_test.cc index 8782ea3c772..042900e6cfd 100644 --- a/tests/fstream_test.cc +++ b/tests/fstream_test.cc @@ -36,6 +36,8 @@ #include #include "mock_file.hh" +using namespace seastar; + struct writer { output_stream out; writer(file f) : out(make_file_output_stream(std::move(f))) {} diff --git a/tests/futures_test.cc b/tests/futures_test.cc index db0b19198b1..dbec85c6d37 100644 --- a/tests/futures_test.cc +++ b/tests/futures_test.cc @@ -29,6 +29,7 @@ #include "core/thread.hh" #include +using namespace seastar; using namespace std::chrono_literals; class expected_exception : std::runtime_error { diff --git a/tests/httpd.cc b/tests/httpd.cc index 911361983ef..b3e9db81938 100644 --- a/tests/httpd.cc +++ b/tests/httpd.cc @@ -13,6 +13,7 @@ #include "core/future-util.hh" #include "tests/test-utils.hh" +using namespace seastar; using namespace httpd; class handl : public httpd::handler_base { diff --git a/tests/ip_test.cc b/tests/ip_test.cc index 56fe6677e63..b1a7eff245c 100644 --- a/tests/ip_test.cc +++ b/tests/ip_test.cc @@ -25,6 +25,7 @@ #include "core/reactor.hh" #include "net/virtio.hh" +using namespace seastar; using namespace net; int main(int ac, char** av) { diff --git a/tests/l3_test.cc b/tests/l3_test.cc index e7116b6f55e..031c8a43582 100644 --- a/tests/l3_test.cc +++ b/tests/l3_test.cc @@ -23,6 +23,7 @@ #include "core/reactor.hh" #include "net/virtio.hh" +using namespace seastar; using namespace net; void dump_arp_packets(l3_protocol& proto) { diff --git a/tests/linecount.cc b/tests/linecount.cc index 6d7f2d999e5..7bebeee1bb2 100644 --- a/tests/linecount.cc +++ b/tests/linecount.cc @@ -28,6 +28,8 @@ #include "core/reactor.hh" #include +using namespace seastar; + struct reader { public: reader(file f) : is(make_file_input_stream(std::move(f))) {} diff --git a/tests/loopback_socket.hh b/tests/loopback_socket.hh index ae0715a945a..a15e76c6194 100644 --- a/tests/loopback_socket.hh +++ b/tests/loopback_socket.hh @@ -30,6 +30,8 @@ #include "core/do_with.hh" #include "net/stack.hh" +namespace seastar { + class loopback_buffer { bool _aborted = false; queue> _q{1}; @@ -184,3 +186,5 @@ public: _b2->shutdown(); } }; + +} diff --git a/tests/memcached/test_ascii_parser.cc b/tests/memcached/test_ascii_parser.cc index 770ab590d73..4c27291fdf9 100644 --- a/tests/memcached/test_ascii_parser.cc +++ b/tests/memcached/test_ascii_parser.cc @@ -27,6 +27,7 @@ #include "apps/memcached/ascii.hh" #include "core/future-util.hh" +using namespace seastar; using namespace net; using namespace memcache; diff --git a/tests/mock_file.hh b/tests/mock_file.hh index 59f56de281d..248e4b43c32 100644 --- a/tests/mock_file.hh +++ b/tests/mock_file.hh @@ -26,6 +26,8 @@ #include "test-utils.hh" #include "core/file.hh" +namespace seastar { + class mock_read_only_file final : public file_impl { bool _closed = false; uint64_t _total_file_size; @@ -107,3 +109,5 @@ public: return make_ready_future>(temporary_buffer(length)); } }; + +} diff --git a/tests/output_stream_test.cc b/tests/output_stream_test.cc index cdfb9e5f172..e2e439501a8 100644 --- a/tests/output_stream_test.cc +++ b/tests/output_stream_test.cc @@ -29,6 +29,7 @@ #include "test-utils.hh" #include +using namespace seastar; using namespace net; static sstring to_sstring(const packet& p) { diff --git a/tests/packet_test.cc b/tests/packet_test.cc index 20adcfadf21..30961542102 100644 --- a/tests/packet_test.cc +++ b/tests/packet_test.cc @@ -26,6 +26,7 @@ #include "net/packet.hh" #include +using namespace seastar; using namespace net; BOOST_AUTO_TEST_CASE(test_headers_are_contiguous) { diff --git a/tests/perf/perf_fstream.cc b/tests/perf/perf_fstream.cc index 4d545ce3b15..19fc83187e7 100644 --- a/tests/perf/perf_fstream.cc +++ b/tests/perf/perf_fstream.cc @@ -24,6 +24,7 @@ #include "../../core/file.hh" #include "../../core/app-template.hh" +using namespace seastar; using namespace std::chrono_literals; int main(int ac, char** av) { diff --git a/tests/rpc.cc b/tests/rpc.cc index 5ca51b296f2..3bbf1a03262 100644 --- a/tests/rpc.cc +++ b/tests/rpc.cc @@ -25,6 +25,8 @@ #include "core/sleep.hh" #include "rpc/lz4_compressor.hh" +using namespace seastar; + struct serializer { }; diff --git a/tests/scollectd_test.cc b/tests/scollectd_test.cc deleted file mode 100644 index ba865110643..00000000000 --- a/tests/scollectd_test.cc +++ /dev/null @@ -1,136 +0,0 @@ -/* - * This file is open source software, licensed to you under the terms - * of the Apache License, Version 2.0 (the "License"). See the NOTICE file - * distributed with this work for additional information regarding copyright - * ownership. You may not use this file except in compliance with the License. - * - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * Copyright (C) 2016 ScyllaDB. - */ -#include - -#include "core/do_with.hh" -#include "test-utils.hh" -#include "core/sstring.hh" -#include "core/reactor.hh" -#include "core/do_with.hh" -#include "core/future-util.hh" -#include "core/sharded.hh" -#include "core/gate.hh" -#include "core/scollectd.hh" -#include "core/scollectd_api.hh" - -using namespace seastar; -using namespace scollectd; - -static const plugin_id plugin = "the_piglet"; -static const plugin_instance_id my_id = "wulf"; - -template -static void test_bind_counters(_Args&& ... args) { - std::vector bound; - - { - plugin_instance_metrics pm(plugin, my_id, std::forward<_Args>(args)...); - - bound = pm.bound_ids(); - BOOST_CHECK_EQUAL(bound.size(), sizeof...(args)); - - for (auto& id : bound) { - auto vals = get_collectd_value(id); - BOOST_CHECK_GE(vals.size(), 1); - } - } - - // check unregistration - for (auto& id : bound) { - auto vals = get_collectd_value(id); - BOOST_CHECK_EQUAL(vals.size(), 0); - } -} - -SEASTAR_TEST_CASE(test_simple_plugin_instance_metrics) { - uint64_t counter = 12; - test_bind_counters(total_bytes("test_bytes", counter)); - - double cow; - test_bind_counters(total_bytes("test_bytes", counter), typed_value_impl("sleeping", cow)); - - int64_t rx, tx; - test_bind_counters(typed_value_impl("christmas_presents", rx, tx)); - - // comment/description - int64_t woff; - test_bind_counters(typed_value_impl("woffs", description("this is how many woffs where barked"), woff)); - - // typed - test_bind_counters(typed_value_impl("waffs", make_typed(data_type::ABSOLUTE, woff))); - - return make_ready_future(); -} - -SEASTAR_TEST_CASE(test_simple_description) { - uint64_t counter = 12; - sstring desc = "I am hungry like the wolf"; - - plugin_instance_metrics pm(plugin, my_id, total_bytes("test_bytes", description(desc), counter)); - - auto s = get_collectd_description_str(pm.bound_ids().front()); - BOOST_CHECK_EQUAL(s, desc); - - return make_ready_future(); -} - -SEASTAR_TEST_CASE(test_bind_callable) { - uint64_t val; - - auto callable = [&val] { return val; }; - - plugin_instance_metrics pm(plugin, my_id, total_bytes("test_bytes", callable)); - - auto bound = pm.bound_ids(); - - for (auto& id : bound) { - for (uint64_t i : { 1, 4, 45542, 2323, 12, 0 }) { - val = i; - auto vals = get_collectd_value(id); - for (auto v : vals) { - BOOST_CHECK_EQUAL(v.ui(), i); - } - } - } - - return make_ready_future(); -} - -SEASTAR_TEST_CASE(test_bind_callable_raw) { - uint64_t val; - - auto callable = [&val] { return val; }; - - scollectd::type_instance_id id("apa", per_cpu_plugin_instance, "total_bytes", "ko"); - scollectd::registration r = add_polled_metric(id, make_typed(data_type::DERIVE, callable)); - - for (uint64_t i : { 1, 4, 45542, 2323, 12, 0 }) { - val = i; - auto vals = get_collectd_value(id); - for (auto v : vals) { - BOOST_CHECK_EQUAL(v.ui(), i); - } - } - - return make_ready_future(); -} - diff --git a/tests/shared_ptr_test.cc b/tests/shared_ptr_test.cc index bf0450aaf63..98e447c388b 100644 --- a/tests/shared_ptr_test.cc +++ b/tests/shared_ptr_test.cc @@ -28,6 +28,8 @@ #include "core/sstring.hh" #include "core/shared_ptr.hh" +using namespace seastar; + struct expected_exception : public std::exception {}; struct A { diff --git a/tests/slab_test.cc b/tests/slab_test.cc index 94b4dd4e9ac..fc44efc3d30 100644 --- a/tests/slab_test.cc +++ b/tests/slab_test.cc @@ -25,6 +25,10 @@ #include #include "core/slab.hh" +using namespace seastar; + +namespace bi = boost::intrusive; + static constexpr size_t max_object_size = 1024*1024; class item : public slab_item_base { diff --git a/tests/smp_test.cc b/tests/smp_test.cc index b922496c876..7a374e59941 100644 --- a/tests/smp_test.cc +++ b/tests/smp_test.cc @@ -23,6 +23,8 @@ #include "core/app-template.hh" #include "core/print.hh" +using namespace seastar; + future test_smp_call() { return smp::submit_to(1, [] { return make_ready_future(3); diff --git a/tests/sstring_test.cc b/tests/sstring_test.cc index 9fdd59f6365..0dac2f28512 100644 --- a/tests/sstring_test.cc +++ b/tests/sstring_test.cc @@ -25,6 +25,8 @@ #include "core/sstring.hh" #include +using namespace seastar; + BOOST_AUTO_TEST_CASE(test_equality) { BOOST_REQUIRE_EQUAL(sstring("aaa"), sstring("aaa")); } diff --git a/tests/tcp_sctp_client.cc b/tests/tcp_sctp_client.cc index a06d54ce6f1..6c675cc791c 100644 --- a/tests/tcp_sctp_client.cc +++ b/tests/tcp_sctp_client.cc @@ -23,8 +23,8 @@ #include "core/future-util.hh" #include "core/distributed.hh" -using namespace net; using namespace seastar; +using namespace net; using namespace std::chrono_literals; static int rx_msg_size = 4 * 1024; diff --git a/tests/tcp_test.cc b/tests/tcp_test.cc index e31dd58d6e3..8c7df5d7d5c 100644 --- a/tests/tcp_test.cc +++ b/tests/tcp_test.cc @@ -23,6 +23,7 @@ #include "net/virtio.hh" #include "net/tcp.hh" +using namespace seastar; using namespace net; struct tcp_test { diff --git a/tests/test-utils.cc b/tests/test-utils.cc index 9f118ee4e73..958b86ed056 100644 --- a/tests/test-utils.cc +++ b/tests/test-utils.cc @@ -33,6 +33,8 @@ #include "core/app-template.hh" #include +namespace seastar { + void seastar_test::run() { // HACK: please see https://github.com/cloudius-systems/seastar/issues/10 BOOST_REQUIRE(true); @@ -76,6 +78,8 @@ bool init_unit_test_suite() { return true; } +} + int main(int ac, char** av) { - return ::boost::unit_test::unit_test_main(&init_unit_test_suite, ac, av); + return ::boost::unit_test::unit_test_main(&seastar::init_unit_test_suite, ac, av); } diff --git a/tests/test-utils.hh b/tests/test-utils.hh index 22c6bf582bb..92b12a61a0d 100644 --- a/tests/test-utils.hh +++ b/tests/test-utils.hh @@ -28,6 +28,8 @@ #include "core/future.hh" #include "test_runner.hh" +namespace seastar { + class seastar_test { public: seastar_test(); @@ -47,3 +49,5 @@ public: static name name ## _instance; \ future<> name::run_test_case() + +} diff --git a/tests/test_runner.cc b/tests/test_runner.cc index 319d1f507e1..6b9b8bcf154 100644 --- a/tests/test_runner.cc +++ b/tests/test_runner.cc @@ -26,6 +26,8 @@ #include "core/reactor.hh" #include "test_runner.hh" +namespace seastar { + static test_runner instance; struct stop_execution : public std::exception {}; @@ -93,3 +95,5 @@ test_runner::run_sync(std::function()> task) { test_runner& global_test_runner() { return instance; } + +} diff --git a/tests/test_runner.hh b/tests/test_runner.hh index 818e817f427..55f6c4391af 100644 --- a/tests/test_runner.hh +++ b/tests/test_runner.hh @@ -28,6 +28,8 @@ #include "core/posix.hh" #include "exchanger.hh" +namespace seastar { + class posix_thread; class test_runner { @@ -43,3 +45,5 @@ public: }; test_runner& global_test_runner(); + +} diff --git a/tests/timertest.cc b/tests/timertest.cc index 0bf0041bc57..676a77004e8 100644 --- a/tests/timertest.cc +++ b/tests/timertest.cc @@ -24,6 +24,7 @@ #include "core/print.hh" #include +using namespace seastar; using namespace std::chrono_literals; #define BUG() do { \ diff --git a/tests/udp_client.cc b/tests/udp_client.cc index 57c6b5a5dd5..5f01d0cf7dc 100644 --- a/tests/udp_client.cc +++ b/tests/udp_client.cc @@ -24,6 +24,7 @@ #include "core/reactor.hh" #include "net/api.hh" +using namespace seastar; using namespace net; using namespace std::chrono_literals; diff --git a/tests/udp_server.cc b/tests/udp_server.cc index 527816eea5f..6586577f064 100644 --- a/tests/udp_server.cc +++ b/tests/udp_server.cc @@ -23,6 +23,7 @@ #include "core/app-template.hh" #include "core/future-util.hh" +using namespace seastar; using namespace net; using namespace std::chrono_literals; diff --git a/tests/udp_zero_copy.cc b/tests/udp_zero_copy.cc index 016df8b276e..f4ed0a66621 100644 --- a/tests/udp_zero_copy.cc +++ b/tests/udp_zero_copy.cc @@ -28,6 +28,7 @@ #include #include +using namespace seastar; using namespace net; using namespace std::chrono_literals; namespace bpo = boost::program_options; diff --git a/tests/unwind_test.cc b/tests/unwind_test.cc index 0f6eccd37eb..b79a3e3b477 100644 --- a/tests/unwind_test.cc +++ b/tests/unwind_test.cc @@ -27,6 +27,8 @@ #include "core/posix.hh" #include "util/backtrace.hh" +using namespace seastar; + void foo() { throw std::runtime_error("foo"); } diff --git a/tests/weak_ptr_test.cc b/tests/weak_ptr_test.cc index 7a6c224c4eb..04e4a26da69 100644 --- a/tests/weak_ptr_test.cc +++ b/tests/weak_ptr_test.cc @@ -25,6 +25,8 @@ #include #include "core/weak_ptr.hh" +using namespace seastar; + class myclass : public weakly_referencable {}; BOOST_AUTO_TEST_CASE(test_weak_ptr_is_empty_when_default_initialized) { diff --git a/util/backtrace.hh b/util/backtrace.hh index a04a4ef0cf9..6c9838ea58d 100644 --- a/util/backtrace.hh +++ b/util/backtrace.hh @@ -26,6 +26,8 @@ #include #include +namespace seastar { + // Invokes func for each frame passing return address as argument. template void backtrace(Func&& func) noexcept(noexcept(func(0))) { @@ -69,15 +71,21 @@ public: } }; +} + namespace std { template<> -struct hash<::saved_backtrace> { - size_t operator()(const ::saved_backtrace& b) const { +struct hash { + size_t operator()(const seastar::saved_backtrace& b) const { return b.hash(); } }; } +namespace seastar { + saved_backtrace current_backtrace(); + +} diff --git a/util/bool_class.hh b/util/bool_class.hh index c1000ab0fac..6acadb0fd54 100644 --- a/util/bool_class.hh +++ b/util/bool_class.hh @@ -23,6 +23,8 @@ #include +namespace seastar { + /// \addtogroup utilities /// @{ @@ -104,3 +106,5 @@ template const bool_class bool_class::no { false }; /// @} + +} diff --git a/util/conversions.cc b/util/conversions.cc index 11042d836bf..a41aa949e64 100644 --- a/util/conversions.cc +++ b/util/conversions.cc @@ -26,6 +26,8 @@ #include "core/print.hh" #include +namespace seastar { + size_t parse_memory_size(std::string s) { size_t factor = 1; if (s.size()) { @@ -41,5 +43,6 @@ size_t parse_memory_size(std::string s) { return boost::lexical_cast(s) * factor; } +} #endif /* CONVERSIONS_CC_ */ diff --git a/util/conversions.hh b/util/conversions.hh index 960085d5577..d3989e260fd 100644 --- a/util/conversions.hh +++ b/util/conversions.hh @@ -26,6 +26,8 @@ #include #include +namespace seastar { + // Convert a string to a memory size, allowing binary SI // suffixes (intentionally, even though SI suffixes are // decimal, to follow existing usage). @@ -44,4 +46,6 @@ static inline std::vector string2vector(std::string str) { return v; } +} + #endif /* CONVERSIONS_HH_ */ diff --git a/util/defer.hh b/util/defer.hh index 60b9b87a862..758b4be8278 100644 --- a/util/defer.hh +++ b/util/defer.hh @@ -22,6 +22,8 @@ #ifndef UTIL_DEFER_HH_ #define UTIL_DEFER_HH_ +namespace seastar { + template class deferred_action { Func _func; @@ -51,4 +53,6 @@ defer(Func&& func) { return deferred_action(std::forward(func)); } +} + #endif /* UTIL_DEFER_HH_ */ diff --git a/util/function_input_iterator.hh b/util/function_input_iterator.hh index 4ffbebca2b8..bdf38e0237b 100644 --- a/util/function_input_iterator.hh +++ b/util/function_input_iterator.hh @@ -22,6 +22,8 @@ #ifndef UTIL_FUNCTION_INPUT_ITERATOR_HH_ #define UTIL_FUNCTION_INPUT_ITERATOR_HH_ +namespace seastar { + template struct function_input_iterator { Function _func; @@ -68,4 +70,6 @@ make_function_input_iterator(Function&& func) { return function_input_iterator(func, State{}); } +} + #endif /* UTIL_FUNCTION_INPUT_ITERATOR_HH_ */ diff --git a/util/indirect.hh b/util/indirect.hh index ac317ada5eb..e544a5f1a1b 100644 --- a/util/indirect.hh +++ b/util/indirect.hh @@ -23,6 +23,8 @@ #include +namespace seastar { + // This header defines functors for comparing and hashing pointers by pointed-to values instead of pointer addresses. // // Examples: @@ -68,3 +70,5 @@ struct indirect_hash { return 0; } }; + +} diff --git a/util/is_smart_ptr.hh b/util/is_smart_ptr.hh index 9a45bc15731..68fb9b9611d 100644 --- a/util/is_smart_ptr.hh +++ b/util/is_smart_ptr.hh @@ -23,8 +23,12 @@ #include // for std::unique_ptr +namespace seastar { + template struct is_smart_ptr : std::false_type {}; template struct is_smart_ptr> : std::true_type {}; + +} diff --git a/util/log.cc b/util/log.cc index 69fcffe1caa..4aa553c8066 100644 --- a/util/log.cc +++ b/util/log.cc @@ -174,6 +174,10 @@ logger::set_syslog_enabled(bool enabled) { _syslog.store(enabled, std::memory_order_relaxed); } +bool logger::is_shard_zero() { + return engine().cpu_id() == 0; +} + void log_registry::set_all_loggers_level(log_level level) { std::lock_guard g(_mutex); diff --git a/util/log.hh b/util/log.hh index 637b59b5ca8..10f66aa9815 100644 --- a/util/log.hh +++ b/util/log.hh @@ -103,6 +103,8 @@ public: logger(logger&& x); ~logger(); + bool is_shard_zero(); + /// Test if desired log level is enabled /// /// \param level - enum level value (info|error...) @@ -157,6 +159,18 @@ public: void info(const char* fmt, Args&&... args) { log(log_level::info, fmt, std::forward(args)...); } + /// Log with info tag on shard zero only: + /// INFO %Y-%m-%d %T,%03d [shard 0] - "your msg" \n + /// + /// \param fmt - printf style format + /// \param args - args to print string + /// + template + void info0(const char* fmt, Args&&... args) { + if (is_shard_zero()) { + log(log_level::info, fmt, std::forward(args)...); + } + } /// Log with info tag: /// DEBUG %Y-%m-%d %T,%03d [shard 0] - "your msg" \n /// diff --git a/util/print_safe.hh b/util/print_safe.hh index 129997997c5..488180c81c2 100644 --- a/util/print_safe.hh +++ b/util/print_safe.hh @@ -23,6 +23,8 @@ #include +namespace seastar { + // // Collection of async-signal safe printing functions. // @@ -107,3 +109,5 @@ void print_decimal_safe(Integral n) noexcept { auto len = convert_decimal_safe(buf, i, n); print_safe(buf, len); } + +} diff --git a/util/spinlock.hh b/util/spinlock.hh index 2c504b5476c..715bab4a932 100644 --- a/util/spinlock.hh +++ b/util/spinlock.hh @@ -24,6 +24,8 @@ #include #include +namespace seastar { + namespace util { // Spin lock implementation. @@ -47,3 +49,5 @@ public: }; } + +} diff --git a/util/transform_iterator.hh b/util/transform_iterator.hh index e726c1c29ed..e7abfabb811 100644 --- a/util/transform_iterator.hh +++ b/util/transform_iterator.hh @@ -22,6 +22,8 @@ #ifndef UTIL_TRANSFORM_ITERATOR_HH_ #define UTIL_TRANSFORM_ITERATOR_HH_ +namespace seastar { + template class transform_iterator { Iterator _i; @@ -53,4 +55,6 @@ make_transform_iterator(Iterator i, Func f) { return transform_iterator(i, f); } +} + #endif /* UTIL_TRANSFORM_ITERATOR_HH_ */