|
@ -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; |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
} |