#pragma once #include "gp/containers/vector.hpp" #include "gp/functional/optional.hpp" #include "gp/ipc/bottleneck.hpp" #include "gp/utils/allocators/allocator.hpp" #include "gp/utils/allocators/buddy.hpp" #include "gp/utils/pointers.hpp" namespace gp { /*class abstract_sender_erasor { virtual ~abstract_sender_erasor() = 0; virtual bool try_send(const gp::buffer message) = 0; }; class abstract_sender_erasor_c : abstract_sender_erasor { virtual abstract_sender_erasor_c* clone(gp::allocator&) = 0; virtual ~abstract_sender_erasor_c() = 0; }; template class abstract_receiver_erasor { [[no_unique_address]] predicate v; virtual abstract_receiver_erasor* clone(abstract_receiver_erasor&, gp::allocator&) = 0; virtual ~abstract_receiver_erasor() = 0; virtual gp::optional> try_receive(allocator& allocate) = 0; }; class abstract_receiver_erasor_c : abstract_sender_erasor { virtual abstract_receiver_erasor_c* clone(gp::allocator&) = 0; virtual ~abstract_receiver_erasor_c() = 0; }; template class impl_channel_sender : abstract_sender_erasor_c { T& value; impl_channel_sender(T& cpy) : value(cpy) {}; impl_channel_sender(T&& cpy) requires std::movable : value(gp::forward(cpy)) {}; virtual abstract_sender_erasor_c* clone(gp::allocator& allocator) { } };*/ /** * @brief */ class basic_channel { allocator& self_allocator; //< This is from whatever created the channel and MUST outlive the channel gp::unique_ptr local_allocator_impl; //< The allocator used here gp::vector> data; gp::fast_bottleneck lock; public: /** * @brief Construct a new channel object * * @param memory_source Where do we allocate from for this channel * @param memory_available The amount of memory to allocate, default specified in the configuration */ basic_channel(allocator& memory_source, size_t memory_available = gp_config::limits::channel_default_size) : self_allocator(memory_source) , local_allocator_impl( gp::unique_ptr> ::make(self_allocator, memory_available, self_allocator) .cast() ) , data(*local_allocator_impl) {} /** * @brief Sends a message on the channel * * @param message The message to transmit through the channel, this implies two copies. Empty messages are invalid and hence not transmitted. * @return true if the message was successfully sent * @return false if the message was not sent */ bool try_send(const gp::buffer message) { if(message.size() == 0) return false; auto guard = lock_guard(lock); auto v = gp::vector(*local_allocator_impl); if(not v.reserve(message.size())) return false; for(char c : message) { v.push_back(c); } if(not data.push_back(v)) return false; if((*data.rbegin()).size() == 0) return false; return true; } /** * @brief Tries to receive a message that matches a certain predicate * * @tparam predicate represents the predicate type, you will probably never specify that by hand * @param allocate The allocator to allocate the response from * @param pred The predicate * @return gp::optional> if the receive failed, this will be empty, else it will contain the message */ template gp::optional> try_receive( allocator& allocate, predicate pred = [](gp::buffer){return true;} ) { for(auto it = data.begin(); it != data.end(); ++it) { auto& elem = *it; if(predicate(elem.as_buffer())) { auto v = gp::vector(allocate); if(not v.reserve(elem.size())) return nullopt; for(char c : elem) { v.push_back(c); } data.remove(it); return gp::move(v); } } return nullopt; } }; }