General Purpose library for Freestanding C++ and POSIX systems
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

85 lines
3.2 KiB

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