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

  1. #pragma once
  2. #include "gp/containers/vector.hpp"
  3. #include "gp/functional/optional.hpp"
  4. #include "gp/ipc/bottleneck.hpp"
  5. #include "gp/utils/allocators/allocator.hpp"
  6. #include "gp/utils/allocators/buddy.hpp"
  7. #include "gp/utils/pointers.hpp"
  8. namespace gp {
  9. /**
  10. * @brief
  11. */
  12. class channel {
  13. allocator& self_allocator; //< This is from whatever created the channel and MUST outlive the channel
  14. gp::unique_ptr<allocator> local_allocator_impl; //< The allocator used here
  15. gp::vector<gp::vector<char>> data;
  16. gp::fast_bottleneck lock;
  17. public:
  18. /**
  19. * @brief Construct a new channel object
  20. *
  21. * @param memory_source Where do we allocate from for this channel
  22. * @param memory_available The amount of memory to allocate, default specified in the configuration
  23. */
  24. channel(allocator& memory_source, size_t memory_available = gp_config::limits::channel_default_size)
  25. : self_allocator(memory_source)
  26. , local_allocator_impl(
  27. gp::unique_ptr<gp::buddy<gp_config::limits::channel_max_size/16,16>>
  28. ::make(self_allocator, memory_available, self_allocator)
  29. .cast<allocator>()
  30. )
  31. , data(*local_allocator_impl)
  32. {}
  33. /**
  34. * @brief Sends a message on the channel
  35. *
  36. * @param message The message to transmit through the channel, this implies two copies. Empty messages are invalid and hence not transmitted.
  37. * @return true if the message was successfully sent
  38. * @return false if the message was not sent
  39. */
  40. bool try_send(const gp::buffer<char> message) {
  41. if(message.size() == 0) return false;
  42. auto guard = lock_guard(lock);
  43. auto v = gp::vector<char>(*local_allocator_impl);
  44. if(not v.reserve(message.size())) return false;
  45. for(char c : message) {
  46. v.push_back(c);
  47. }
  48. if(not data.push_back(v)) return false;
  49. if((*data.rbegin()).size() == 0) return false;
  50. return true;
  51. }
  52. /**
  53. * @brief Tries to receive a message that matches a certain predicate
  54. *
  55. * @tparam predicate represents the predicate type, you will probably never specify that by hand
  56. * @param allocate The allocator to allocate the response from
  57. * @param pred The predicate
  58. * @return gp::optional<gp::vector<char>> if the receive failed, this will be empty, else it will contain the message
  59. */
  60. template<typename predicate>
  61. gp::optional<gp::vector<char>> try_receive(
  62. allocator allocate,
  63. predicate pred = [](gp::buffer<char>){return true;}
  64. ) {
  65. for(auto it = data.begin(); it != data.end(); ++it) {
  66. auto& elem = *it;
  67. if(predicate(elem.as_buffer())) {
  68. auto v = gp::vector<char>(allocate);
  69. if(not v.reserve(elem.size())) return nullopt;
  70. for(char c : elem) {
  71. v.push_back(c);
  72. }
  73. data.remove(it);
  74. return gp::move(v);
  75. }
  76. }
  77. return nullopt;
  78. }
  79. };
  80. }