Commit 48cb551e authored by Dominik Charousset's avatar Dominik Charousset

WIP

parent 498c837b
...@@ -36,6 +36,7 @@ Convenience options: ...@@ -36,6 +36,7 @@ Convenience options:
--build-type=Debug --build-type=Debug
--sanitizers=address,undefined --sanitizers=address,undefined
--enable-utility-targets --enable-utility-targets
--enable-export-compile-commands
Flags (use --enable-<name> to activate and --disable-<name> to deactivate): Flags (use --enable-<name> to activate and --disable-<name> to deactivate):
...@@ -143,6 +144,7 @@ while [ $# -ne 0 ]; do ...@@ -143,6 +144,7 @@ while [ $# -ne 0 ]; do
CMakeBuildType='Debug' CMakeBuildType='Debug'
append_cache_entry CAF_INC_SANITIZERS STRING 'address,undefined' append_cache_entry CAF_INC_SANITIZERS STRING 'address,undefined'
set_build_flag utility-targets ON set_build_flag utility-targets ON
set_build_flag export-compile-commands ON
;; ;;
--enable-*) --enable-*)
set_build_flag $optarg ON set_build_flag $optarg ON
......
...@@ -123,30 +123,31 @@ target_include_directories(caf-net-test PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/tes ...@@ -123,30 +123,31 @@ target_include_directories(caf-net-test PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/tes
target_link_libraries(caf-net-test PRIVATE CAF::test) target_link_libraries(caf-net-test PRIVATE CAF::test)
caf_incubator_add_test_suites(caf-net-test caf_incubator_add_test_suites(caf-net-test
accept_socket
application
convert_ip_endpoint
datagram_socket
datagram_transport
doorman
endpoint_manager
header
ip
multiplexer
net.backend.tcp
net.basp.message_queue net.basp.message_queue
net.basp.ping_pong net.basp.ping_pong
net.basp.worker net.basp.worker
accept_socket net.length_prefix_framing
network_socket
pipe_socket pipe_socket
application
socket socket
convert_ip_endpoint
socket_guard socket_guard
datagram_socket
stream_application stream_application
datagram_transport
stream_socket stream_socket
doorman
stream_transport stream_transport
endpoint_manager
string_application string_application
header
tcp_sockets tcp_sockets
ip
transport_worker transport_worker
multiplexer
transport_worker_dispatcher transport_worker_dispatcher
udp_datagram_socket udp_datagram_socket
network_socket
net.backend.tcp
) )
...@@ -48,24 +48,52 @@ stack *up*. Outgoing data always travels the protocol stack *down*. ...@@ -48,24 +48,52 @@ stack *up*. Outgoing data always travels the protocol stack *down*.
..code-block:: C++ ..code-block:: C++
interface base [role: upper layer] {
/// Called whenever the underlying transport is ready to send, allowing the
/// upper layers to produce additional output data or use the event to read
/// from event queues, etc.
/// @returns `true` if the lower layers may proceed, `false` otherwise
/// (aborts execution).
template <class LowerLayer>
bool prepare_send(LowerLayer& down);
/// Called whenever the underlying transport finished writing all buffered
/// data for output to query whether an upper layer still has pending events
/// or may produce output data on the next call to `prepare_send`.
/// @returns `true` if the underlying socket may get removed from the I/O
/// event loop, `false` otherwise.
template <class LowerLayer>
bool done_sending(LowerLayer& down);
}
interface base [role: lower layer] {
/// Returns whether the layer has output slots available.
bool can_send_more() const noexcept;
}
interface stream_oriented [role: upper layer] { interface stream_oriented [role: upper layer] {
/// Called by the lower layer for cleaning up any state in case of an error.
template <class LowerLayer>
void abort(LowerLayer& down, const error& reason);
/// Consumes bytes from the lower layer. /// Consumes bytes from the lower layer.
/// @param down Reference to the lower layer that received the data. /// @param down Reference to the lower layer that received the data.
/// @param buffer Available bytes to read. /// @param buffer Available bytes to read.
/// @param delta Bytes that arrived since last calling this function. /// @param delta Bytes that arrived since last calling this function.
/// @returns The number of consumed bytes (may be zero if waiting for more /// @returns The number of consumed bytes. May be zero if waiting for more
/// input or negative to signal an error) and a policy that /// input or negative to signal an error.
/// configures how many bytes to receive next (as well as /// @note When returning a negative value, clients should also call
/// thresholds for when to call this function again). /// `down.abort_reason(...)` with an appropriate error code.
/// @note When returning a negative value for the number of consumed bytes,
/// clients must also call `down.set_read_error(...)` with an
/// appropriate error code.
template <class LowerLayer> template <class LowerLayer>
pair<ptrdiff_t, receive_policy> consume(LowerLayer& down, byte_span buffer, ptrdiff_t consume(LowerLayer& down, byte_span buffer, byte_span delta);
byte_span delta);
} }
interface stream_oriented [role: lower layer] { interface stream_oriented [role: lower layer] {
/// Configures threshold for the next receive operations. Policies remain
/// active until calling this function again.
/// @warning Calling this function in `consume` invalidates both byte spans.
void configure_read(read_policy policy);
/// Prepares the layer for outgoing traffic, e.g., by allocating an output /// Prepares the layer for outgoing traffic, e.g., by allocating an output
/// buffer as necessary. /// buffer as necessary.
void begin_output(); void begin_output();
...@@ -78,6 +106,11 @@ stack *up*. Outgoing data always travels the protocol stack *down*. ...@@ -78,6 +106,11 @@ stack *up*. Outgoing data always travels the protocol stack *down*.
/// Prepares written data for transfer, e.g., by flushing buffers or /// Prepares written data for transfer, e.g., by flushing buffers or
/// registering sockets for write events. /// registering sockets for write events.
void end_output(); void end_output();
/// Propagates an abort reason to the lower layers. After processing the
/// current read or write event, the lowest layer will call `abort` on its
// upper layer.
void abort_reason(error reason);
} }
interface datagram_oriented [role: upper layer] { interface datagram_oriented [role: upper layer] {
...@@ -137,6 +170,9 @@ stack *up*. Outgoing data always travels the protocol stack *down*. ...@@ -137,6 +170,9 @@ stack *up*. Outgoing data always travels the protocol stack *down*.
byte_buffer& message_buffer(); byte_buffer& message_buffer();
/// Seals and prepares a message for transfer. /// Seals and prepares a message for transfer.
void end_message(); /// @note When returning `false`, clients must also call
/// `down.set_read_error(...)` with an appropriate error code.
template <class LowerLayer>
bool end_message();
} }
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include "caf/net/datagram_transport.hpp" #include "caf/net/datagram_transport.hpp"
#include "caf/net/defaults.hpp" #include "caf/net/defaults.hpp"
#include "caf/net/endpoint_manager.hpp" #include "caf/net/endpoint_manager.hpp"
#include "caf/net/endpoint_manager_impl.hpp"
#include "caf/net/endpoint_manager_queue.hpp" #include "caf/net/endpoint_manager_queue.hpp"
#include "caf/net/fwd.hpp" #include "caf/net/fwd.hpp"
#include "caf/net/host.hpp" #include "caf/net/host.hpp"
......
...@@ -36,4 +36,9 @@ CAF_NET_EXPORT extern const size_t max_header_buffers; ...@@ -36,4 +36,9 @@ CAF_NET_EXPORT extern const size_t max_header_buffers;
/// Port to listen on for tcp. /// Port to listen on for tcp.
CAF_NET_EXPORT extern const uint16_t tcp_port; CAF_NET_EXPORT extern const uint16_t tcp_port;
/// Caps how much Bytes a stream transport pushes to its write buffer before
/// stopping to read from its message queue. Default TCP send buffer is 16kB (at
/// least on Linux).
constexpr auto stream_output_buf_cap = size_t{32768};
} // namespace caf::defaults::middleman } // namespace caf::defaults::middleman
...@@ -52,10 +52,16 @@ public: ...@@ -52,10 +52,16 @@ public:
// -- properties ------------------------------------------------------------- // -- properties -------------------------------------------------------------
actor_system& system() { actor_system& system() noexcept {
return sys_; return sys_;
} }
const actor_system_config& config() const noexcept;
// -- queue access -----------------------------------------------------------
bool at_end_of_message_queue();
endpoint_manager_queue::message_ptr next_message(); endpoint_manager_queue::message_ptr next_message();
// -- event management ------------------------------------------------------- // -- event management -------------------------------------------------------
......
...@@ -124,6 +124,8 @@ private: ...@@ -124,6 +124,8 @@ private:
/// Stores the id for the next timeout. /// Stores the id for the next timeout.
uint64_t next_timeout_id_; uint64_t next_timeout_id_;
error err_;
}; };
} // namespace caf::net } // namespace caf::net
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstdint>
#include <cstring>
#include <memory>
#include "caf/byte.hpp"
#include "caf/detail/network_order.hpp"
#include "caf/sec.hpp"
#include "caf/tag/message_oriented.hpp"
#include "caf/tag/stream_oriented.hpp"
namespace caf::net {
/// Length-prefixed message framing for discretizing a Byte stream into messages
/// of varying size. The framing uses 4 Bytes for the length prefix, but
/// messages (including the 4 Bytes for the length prefix) are limited to a
/// maximum size of INT32_MAX. This limitation comes from the POSIX API (recv)
/// on 32-bit platforms.
template <class UpperLayer>
class length_prefix_framing {
public:
using input_tag = tag::stream_oriented;
using output_tag = tag::message_oriented;
using length_prefix_type = uint32_t;
constexpr size_t max_message_length = INT32_MAX;
// -- interface for the upper layer ------------------------------------------
template <class LowerLayer>
class access {
public:
access(LowerLayer* lower_layer, length_prefix_framing* this_layer)
: lower_layer_(lower_layer), this_layer(this_layer) {
// nop
}
void begin_message() {
lower_layer_->begin_output();
auto& buf = message_buffer();
message_offset_ = buf.size();
buf.insert(buf.end(), 4, byte{0});
}
byte_buffer& message_buffer() {
return lower_layer_->output_buffer.size();
}
void end_message() {
using detail::to_network_order;
auto& buf = message_buffer();
auto msg_begin = buf.begin() + message_offset_;
auto msg_size = std::distance(msg_begin + 4, buf.end());
if (msg_size > 0 && msg_size < max_message_length) {
auto u32_size = to_network_order(static_cast<uint32_t>(msg_size));
memcpy(std::addressof(*msg_begin), &u32_size, 4);
return true;
} else {
abort_reason(make_error(
sec::runtime_error, msg_size == 0 ? "logic error: message of size 0"
: "maximum message size exceeded"));
return false;
}
}
bool can_send_more() const noexcept {
return lower_layer_->can_send_more();
}
void abort_reason(error reason) {
return lower_layer_->abort_reason(std::move(reason));
}
void configure_read(receive_policy policy) {
if (policy.max_size > 0 && transport_->max_read_size_ == 0)
parent_->register_reading();
transport_->min_read_size_ = policy.min_size;
transport_->max_read_size_ = policy.max_size;
transport_->read_buf_.resize(policy.max_size);
}
private:
LowerLayer* lower_layer_;
length_prefix_framing* this_layer_;
size_t message_offset_ = 0;
};
// -- properties -------------------------------------------------------------
auto& upper_layer() noexcept {
return upper_layer_;
}
const auto& upper_layer() const noexcept {
return upper_layer_;
}
// -- role: upper layer ------------------------------------------------------
template <class LowerLayer>
bool prepare_send(LowerLayer& down) {
access<LowerLayer> this_layer{&down, this};
return upper_layer_.prepare_send(this_layer);
}
template <class LowerLayer>
bool done_sending(LowerLayer& down) {
access<LowerLayer> this_layer{&down, this};
return upper_layer_.done_sending(this_layer);
}
template <class LowerLayer>
void abort(LowerLayer& down, const error& reason) {
access<LowerLayer> this_layer{&down, this};
return upper_layer_.abort(this_layer, reason);
}
template <class LowerLayer>
ptrdiff_t consume(LowerLayer& down, byte_span buffer, byte_span delta) {
}
private:
UpperLayer upper_layer_;
};
} // namespace caf::net
...@@ -18,45 +18,28 @@ ...@@ -18,45 +18,28 @@
#pragma once #pragma once
#include <cstddef> #include <cstdint>
#include <string>
#include <utility>
#include "caf/config.hpp"
namespace caf::net { namespace caf::net {
enum class CAF_NET_EXPORT receive_policy_flag : unsigned { struct receive_policy {
at_least, uint32_t min_size;
at_most, uint32_t max_size;
exactly
};
inline std::string to_string(receive_policy_flag x) {
return x == receive_policy_flag::at_least
? "at_least"
: (x == receive_policy_flag::at_most ? "at_most" : "exactly");
}
class CAF_NET_EXPORT receive_policy {
public:
receive_policy() = delete;
using config = std::pair<receive_policy_flag, size_t>;
static config at_least(size_t num_bytes) { /// @pre `min_size > 0`
CAF_ASSERT(num_bytes > 0); /// @pre `min_size <= max_size`
return {receive_policy_flag::at_least, num_bytes}; static constexpr receive_policy between(uint32_t min_size,
uint32_t max_size) {
return {min_size, max_size};
} }
static config at_most(size_t num_bytes) { /// @pre `size > 0`
CAF_ASSERT(num_bytes > 0); static constexpr receive_policy exactly(uint32_t size) {
return {receive_policy_flag::at_most, num_bytes}; return {size, size};
} }
static config exactly(size_t num_bytes) { static constexpr receive_policy stop() {
CAF_ASSERT(num_bytes > 0); return {0, 0};
return {receive_policy_flag::exactly, num_bytes};
} }
}; };
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "caf/detail/net_export.hpp" #include "caf/detail/net_export.hpp"
#include "caf/error.hpp" #include "caf/error.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/make_counted.hpp"
#include "caf/net/fwd.hpp" #include "caf/net/fwd.hpp"
#include "caf/net/operation.hpp" #include "caf/net/operation.hpp"
#include "caf/net/socket.hpp" #include "caf/net/socket.hpp"
...@@ -70,6 +71,21 @@ public: ...@@ -70,6 +71,21 @@ public:
/// @pre `flag != operation::none` /// @pre `flag != operation::none`
bool mask_del(operation flag) noexcept; bool mask_del(operation flag) noexcept;
const error& abort_reason() const noexcept {
return abort_reason_;
}
void abort_reason(error reason) noexcept {
abort_reason_ = std::move(reason);
}
template <class... Ts>
const error& abort_reason_or(Ts&&... xs) {
if (!abort_reason_)
abort_reason_ = make_error(std::forward<Ts>(xs)...);
return abort_reason_;
}
// -- event loop management -------------------------------------------------- // -- event loop management --------------------------------------------------
void register_reading(); void register_reading();
...@@ -96,8 +112,65 @@ protected: ...@@ -96,8 +112,65 @@ protected:
operation mask_; operation mask_;
weak_multiplexer_ptr parent_; weak_multiplexer_ptr parent_;
error abort_reason_;
};
template <class Protocol>
class socket_manager_impl : public socket_manager {
public:
template <class... Ts>
socket_manager_impl(Ts&&... xs) : protocol_(std::forward<Ts>(xs)...) {
// nop
}
bool handle_read_event() override {
return protocol_.handle_read_event(*this);
}
bool handle_write_event() override {
return protocol_.handle_write_event(*this);
}
void handle_error(sec code) override {
abort_reason_ = code;
return protocol_.abort(*this, abort_reason_);
}
auto& protocol() noexcept {
return protocol_;
}
const auto& protocol() const noexcept {
return protocol_;
}
private:
Protocol protocol_;
}; };
/// @relates socket_manager
using socket_manager_ptr = intrusive_ptr<socket_manager>; using socket_manager_ptr = intrusive_ptr<socket_manager>;
template <class B, template <class> class... Layers>
struct make_socket_manager_helper;
template <class B>
struct make_socket_manager_helper<B> {
using type = B;
};
template <class B, template <class> class Layer,
template <class> class... Layers>
struct make_socket_manager_helper<B, Layer, Layers...>
: make_socket_manager_helper<Layer<B>, Layers...> {
// no content
};
template <class App, template <class> class... Layers, class... Ts>
auto make_socket_manager(Ts&&... xs) {
using impl = make_socket_manager_helper<App, Layers..., socket_manager_impl>;
return make_counted<impl>(std::forward<Ts>(xs)...);
}
} // namespace caf::net } // namespace caf::net
This diff is collapsed.
...@@ -173,11 +173,6 @@ public: ...@@ -173,11 +173,6 @@ public:
// -- (pure) virtual functions ----------------------------------------------- // -- (pure) virtual functions -----------------------------------------------
/// Configures this transport for the next read event.
virtual void configure_read(receive_policy::config) {
// nop
}
/// Called by the endpoint manager when the transport can read data from its /// Called by the endpoint manager when the transport can read data from its
/// socket. /// socket.
virtual bool handle_read_event(endpoint_manager&) = 0; virtual bool handle_read_event(endpoint_manager&) = 0;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
namespace caf::tag {
struct message_oriented {};
} // namespace caf::tag
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
namespace caf::tag {
struct stream_oriented {};
} // namespace caf::tag
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
namespace caf::net { namespace caf::net {
// -- constructors, destructors, and assignment operators ----------------------
endpoint_manager::endpoint_manager(socket handle, const multiplexer_ptr& parent, endpoint_manager::endpoint_manager(socket handle, const multiplexer_ptr& parent,
actor_system& sys) actor_system& sys)
: super(handle, parent), sys_(sys), queue_(unit, unit, unit) { : super(handle, parent), sys_(sys), queue_(unit, unit, unit) {
...@@ -35,6 +37,18 @@ endpoint_manager::~endpoint_manager() { ...@@ -35,6 +37,18 @@ endpoint_manager::~endpoint_manager() {
// nop // nop
} }
// -- properties ---------------------------------------------------------------
const actor_system_config& endpoint_manager::config() const noexcept {
return sys_.config();
}
// -- queue access -------------------------------------------------------------
bool endpoint_manager::at_end_of_message_queue() {
return queue_.empty() && queue_.try_block();
}
endpoint_manager_queue::message_ptr endpoint_manager::next_message() { endpoint_manager_queue::message_ptr endpoint_manager::next_message() {
if (queue_.blocked()) if (queue_.blocked())
return nullptr; return nullptr;
...@@ -50,6 +64,8 @@ endpoint_manager_queue::message_ptr endpoint_manager::next_message() { ...@@ -50,6 +64,8 @@ endpoint_manager_queue::message_ptr endpoint_manager::next_message() {
return result; return result;
} }
// -- event management ---------------------------------------------------------
void endpoint_manager::resolve(uri locator, actor listener) { void endpoint_manager::resolve(uri locator, actor listener) {
using intrusive::inbox_result; using intrusive::inbox_result;
using event_type = endpoint_manager_queue::event; using event_type = endpoint_manager_queue::event;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE net.length_prefix_framing
#include "caf/net/length_prefix_framing.hpp"
#include "caf/test/dsl.hpp"
using namespace caf;
namespace {
struct fixture {
};
} // namespace
CAF_TEST_FIXTURE_SCOPE(length_prefix_framing_tests, fixture)
CAF_TEST(todo) {
// implement me
}
CAF_TEST_FIXTURE_SCOPE_END()
...@@ -61,7 +61,7 @@ struct fixture : host_fixture, test_coordinator_fixture<config> { ...@@ -61,7 +61,7 @@ struct fixture : host_fixture, test_coordinator_fixture<config> {
mm.mpx()->set_thread_id(); mm.mpx()->set_thread_id();
auto backend = dynamic_cast<backend::test*>(mm.backend("test")); auto backend = dynamic_cast<backend::test*>(mm.backend("test"));
auto mgr = backend->peer(mars); auto mgr = backend->peer(mars);
auto& dref = dynamic_cast<endpoint_manager_impl<transport_type>&>(*mgr); auto& dref = dynamic_cast<socket_manager_impl<transport_type>&>(*mgr);
app = &dref.transport().application(); app = &dref.transport().application();
sock = backend->socket(mars); sock = backend->socket(mars);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment