| @ -0,0 +1,85 @@ | |||
| #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 { | |||
| /** | |||
| * @brief | |||
| */ | |||
| class channel { | |||
| allocator& self_allocator; //< This is from whatever created the channel and MUST outlive the channel | |||
| gp::unique_ptr<allocator> local_allocator_impl; //< The allocator used here | |||
| gp::vector<gp::vector<char>> 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 | |||
| */ | |||
| channel(allocator& memory_source, size_t memory_available = gp_config::limits::channel_default_size) | |||
| : self_allocator(memory_source) | |||
| , local_allocator_impl( | |||
| gp::unique_ptr<gp::buddy<gp_config::limits::channel_max_size/16,16>> | |||
| ::make(self_allocator, memory_available, self_allocator) | |||
| .cast<allocator>() | |||
| ) | |||
| , 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<char> message) { | |||
| if(message.size() == 0) return false; | |||
| auto guard = lock_guard(lock); | |||
| auto v = gp::vector<char>(*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<gp::vector<char>> if the receive failed, this will be empty, else it will contain the message | |||
| */ | |||
| template<typename predicate> | |||
| gp::optional<gp::vector<char>> try_receive( | |||
| allocator allocate, | |||
| predicate pred = [](gp::buffer<char>){return true;} | |||
| ) { | |||
| for(auto it = data.begin(); it != data.end(); ++it) { | |||
| auto& elem = *it; | |||
| if(predicate(elem.as_buffer())) { | |||
| auto v = gp::vector<char>(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; | |||
| } | |||
| }; | |||
| } | |||