22#include <flow/error/error.hpp>
23#include <flow/util/util.hpp>
24#include <boost/chrono/floor.hpp>
25#include <boost/array.hpp>
26#include <boost/asio/connect_pipe.hpp>
27#include <boost/io/ios_state.hpp>
48std::string shared_name_to_mq_name(
const Shared_name& name);
59 m_interrupting_snd(false),
60 m_interrupting_rcv(false),
61 m_interrupter_snd(m_nb_task_engine),
62 m_interrupt_detector_snd(m_nb_task_engine),
63 m_interrupter_rcv(m_nb_task_engine),
64 m_interrupt_detector_rcv(m_nb_task_engine)
69template<
typename Mode_tag>
71 size_t max_n_msg,
size_t max_msg_sz,
73 flow::log::Log_context(logger_ptr,
Log_component::S_TRANSPORT),
74 m_absolute_name(absolute_name_arg),
75 m_interrupting_snd(false),
76 m_interrupting_rcv(false),
77 m_interrupter_snd(m_nb_task_engine),
78 m_interrupt_detector_snd(m_nb_task_engine),
79 m_interrupter_rcv(m_nb_task_engine),
80 m_interrupt_detector_rcv(m_nb_task_engine)
83 using flow::error::Runtime_error;
85 using boost::io::ios_all_saver;
86 using boost::system::system_category;
96 assert(max_n_msg >= 1);
97 assert(max_msg_sz >= 1);
99 static_assert(std::is_same_v<Mode_tag, util::Create_only> || std::is_same_v<Mode_tag, util::Open_or_create>,
100 "Can only delegate to this ctor with Mode_tag = Create_only or Open_or_create.");
101 constexpr bool CREATE_ONLY_ELSE_MAYBE = std::is_same_v<Mode_tag, util::Create_only>;
102 constexpr char const * MODE_STR = CREATE_ONLY_ELSE_MAYBE ?
"create-only" :
"open-or-create";
104 if (logger_ptr && logger_ptr->should_log(Sev::S_TRACE, get_log_component()))
106 ios_all_saver saver{*(logger_ptr->this_thread_ostream())};
108 FLOW_LOG_TRACE_WITHOUT_CHECKING
109 (
"Posix_mq_handle [" << *
this <<
"]: Constructing MQ handle to MQ at name [" <<
absolute_name() <<
"] in "
110 "[" << MODE_STR <<
"] mode; max msg size [" << max_msg_sz <<
"] x [" << max_n_msg <<
"] msgs; "
111 "perms = [" << std::setfill(
'0') << std::setw(4) << std::oct << perms.get_permissions() <<
"].");
114 const auto mq_name = shared_name_to_mq_name(
absolute_name());
115 bool created =
false;
161 const auto do_mq_open_func = [&](
bool create_else_open)
166 if (create_else_open)
169 attr.mq_maxmsg = max_n_msg;
170 attr.mq_msgsize = max_msg_sz;
172 raw = mq_open(mq_name.c_str(),
173 O_RDWR | O_CREAT | O_EXCL,
174 perms.get_permissions(),
179 raw = mq_open(mq_name.c_str(), O_RDWR);
183 sys_err_code.clear();
185 create_else_open && (created =
true);
189 sys_err_code = {errno, system_category()};
193 if (CREATE_ONLY_ELSE_MAYBE)
197 if ((do_mq_open_func(
true), sys_err_code)
208 bool success =
false;
211 if (do_mq_open_func(
true), !sys_err_code)
225 if (sys_err_code != boost::system::errc::file_exists)
231 if (do_mq_open_func(
false), !sys_err_code)
237 if (sys_err_code != boost::system::errc::no_such_file_or_directory)
244 if (logger_ptr && logger_ptr->should_log(Sev::S_INFO, get_log_component()))
246 ios_all_saver saver{*(logger_ptr->this_thread_ostream())};
247 FLOW_LOG_INFO_WITHOUT_CHECKING
248 (
"Posix_mq_handle [" << *
this <<
"]: Create-or-open algorithm encountered the rare concurrency: "
249 "MQ at name [" <<
absolute_name() <<
"] existed during the create-only mq_open() but disappeared "
250 "before we were able to complete open-only mq_open(). Retrying in spin-lock fashion. "
251 "Details: max msg size [" << max_msg_sz <<
"] x [" << max_n_msg <<
"] msgs; "
252 "perms = [" << std::setfill(
'0') << std::setw(4) << std::oct << perms.get_permissions() <<
"].");
265 assert(sys_err_code);
293 mq_unlink(mq_name.c_str());
296 if (logger_ptr && logger_ptr->should_log(Sev::S_WARNING, get_log_component()))
298 ios_all_saver saver{*(logger_ptr->this_thread_ostream())};
300 FLOW_LOG_WARNING_WITHOUT_CHECKING
301 (
"Posix_mq_handle [" << *
this <<
"]: Setup error (additional details may be above) while "
302 "constructing MQ handle to MQ at name [" <<
absolute_name() <<
"] in "
303 "[" << MODE_STR <<
"] mode; max msg size [" << max_msg_sz <<
"] x [" << max_n_msg <<
"] msgs; "
304 "perms = [" << std::setfill(
'0') << std::setw(4) << std::oct << perms.get_permissions() <<
"].");
306 FLOW_ERROR_SYS_ERROR_LOG_WARNING();
310 *err_code = sys_err_code;
314 throw Runtime_error{sys_err_code, FLOW_UTIL_WHERE_AM_I_STR()};
319 *err_code = sys_err_code;
342 flow::log::Log_context(logger_ptr,
Log_component::S_TRANSPORT),
343 m_absolute_name(absolute_name_arg),
344 m_interrupting_snd(false),
345 m_interrupting_rcv(false),
346 m_interrupter_snd(m_nb_task_engine),
347 m_interrupt_detector_snd(m_nb_task_engine),
348 m_interrupter_rcv(m_nb_task_engine),
349 m_interrupt_detector_rcv(m_nb_task_engine)
351 using flow::log::Sev;
352 using flow::error::Runtime_error;
353 using boost::system::system_category;
360 (
"Posix_mq_handle [" << *
this <<
"]: Constructing MQ handle to MQ at name [" <<
absolute_name() <<
"] in "
377 const auto raw = mq_open(shared_name_to_mq_name(
absolute_name()).c_str(), O_RDWR);
380 sys_err_code = {errno, system_category()};
390 assert(sys_err_code);
411 (
"Posix_mq_handle [" << *
this <<
"]: Setup error (additional details may be above) while "
412 "constructing MQ handle to MQ at name [" <<
absolute_name() <<
"] in open-only mode.");
413 FLOW_ERROR_SYS_ERROR_LOG_WARNING();
417 *err_code = sys_err_code;
421 throw Runtime_error{sys_err_code, FLOW_UTIL_WHERE_AM_I_STR()};
426 *err_code = sys_err_code;
433 using boost::asio::connect_pipe;
447 (
"Posix_mq_handle [" << *
this <<
"]: Constructing MQ handle to MQ at name [" <<
absolute_name() <<
"]: "
448 "connect-pipe failed. Details follow.");
456 using boost::system::system_category;
457 using ::epoll_create1;
459 using Epoll_event = ::epoll_event;
471 const auto setup = [&](
Native_handle* epoll_hndl_ptr,
bool snd_else_rcv)
473 auto& epoll_hndl = *epoll_hndl_ptr;
477 if (epoll_hndl.m_native_handle == -1)
481 sys_err_code = {errno, system_category()};
482 FLOW_LOG_WARNING(
"Posix_mq_handle [" << *
this <<
"]: Created MQ handle fine, but epoll_create1() failed; "
489 Epoll_event event_of_interest1;
490 event_of_interest1.events = snd_else_rcv ? EPOLLOUT : EPOLLIN;
492 Epoll_event event_of_interest2;
493 event_of_interest2.events = EPOLLIN;
494 event_of_interest2.data.fd = interrupt_detector.native_handle();
495 if ((epoll_ctl(epoll_hndl.m_native_handle, EPOLL_CTL_ADD, event_of_interest1.data.fd, &event_of_interest1) == -1)
497 (epoll_ctl(epoll_hndl.m_native_handle, EPOLL_CTL_ADD, event_of_interest2.data.fd, &event_of_interest2) == -1))
499 sys_err_code = {errno, system_category()};
500 FLOW_LOG_WARNING(
"Posix_mq_handle [" << *
this <<
"]: Created MQ handle fine, but an epoll_ctl() failed; "
501 "snd_else_rcv = [" << snd_else_rcv <<
"]; details follow.");
527 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: No MQ handle to close in destructor.");
533 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Closing MQ handle (and epoll set).");
585 using flow::log::Log_context;
591 swap(
static_cast<Log_context&
>(val1),
static_cast<Log_context&
>(val2));
603 array<Native_handle, 4> fds1;
604 array<Native_handle, 4> fds2;
648 assert(
false &&
"Horrible exception."); std::abort();
658 &&
"As advertised: max_msg_size() => undefined behavior if not successfully cted or was moved-from.");
669 assert(
false &&
"mq_getattr() failed (details logged); this is too bizarre.");
674 return size_t(attr.mq_msgsize);
684 &&
"As advertised: max_n_msgs() => undefined behavior if not successfully cted or was moved-from.");
690 assert(
false &&
"mq_getattr() failed (details logged); this is too bizarre.");
693 return size_t(attr.mq_maxmsg);
698 using boost::system::system_category;
709 attr.mq_flags = nb ? O_NONBLOCK : 0;
711 err_code,
"Posix_mq_handle::set_non_blocking(): mq_setattr()");
716 using flow::util::buffers_dump_string;
720 FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(
bool,
try_send, blob, _1);
724 &&
"As advertised: try_send() => undefined behavior if not successfully cted or was moved-from.");
727 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Nb-push of blob @[" <<
blob_data <<
"], "
728 "size [" << blob.size() <<
"].");
729 if (blob.size() == 0)
739 FLOW_LOG_DATA(
"Blob contents: [\n" << buffers_dump_string(blob,
" ") <<
"].");
744 blob.size(), 0) == 0)
752 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Nb-push of blob @[" <<
blob_data <<
"], "
753 "size [" << blob.size() <<
"]: would-block.");
766 using flow::util::buffers_dump_string;
770 if (flow::error::exec_void_and_throw_on_error
771 ([&](
Error_code* actual_err_code) {
send(blob, actual_err_code); },
772 err_code,
"Posix_mq_handle::send()"))
779 &&
"As advertised: send() => undefined behavior if not successfully cted or was moved-from.");
782 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Blocking-push of blob @[" <<
blob_data <<
"], "
783 "size [" << blob.size() <<
"]. Trying nb-push first; if it succeeds -- great. "
784 "Else will wait/retry/wait/retry/....");
785 if (blob.size() == 0)
792 FLOW_LOG_DATA(
"Blob contents: [\n" << buffers_dump_string(blob,
" ") <<
"].");
803 blob.size(), 0) == 0)
817 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Nb-push of blob @[" <<
blob_data <<
"], "
818 "size [" << blob.size() <<
"]: would-block. Executing blocking-wait.");
828 FLOW_LOG_TRACE(
"Blocking-wait reported transmissibility. Retrying.");
835 using flow::util::time_since_posix_epoch;
836 using flow::util::buffers_dump_string;
837 using flow::Fine_clock;
838 using boost::chrono::floor;
839 using boost::chrono::round;
840 using boost::chrono::seconds;
841 using boost::chrono::microseconds;
842 using boost::chrono::nanoseconds;
846 FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(
bool,
timed_send, blob, timeout_from_now, _1);
852 &&
"As advertised: timed_send() => undefined behavior if not successfully cted or was moved-from.");
855 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Blocking-timed-push of blob @[" <<
blob_data <<
"], "
856 "size [" << blob.size() <<
"]; timeout ~[" << round<microseconds>(timeout_from_now) <<
"]. "
857 "Trying nb-push first; if it succeeds -- great. Else will wait/retry/wait/retry/....");
858 if (blob.size() == 0)
865 FLOW_LOG_DATA(
"Blob contents: [\n" << buffers_dump_string(blob,
" ") <<
"].");
868 auto before = Fine_clock::now();
874 blob.size(), 0) == 0)
888 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Nb-push of blob @[" <<
blob_data <<
"], "
889 "size [" << blob.size() <<
"]: would-block. Executing blocking-wait.");
901 FLOW_LOG_TRACE(
"Did not finish before timeout.");
905 FLOW_LOG_TRACE(
"Blocking-wait reported transmissibility. Retrying.");
907 const auto after = Fine_clock::now();
908 timeout_from_now -= (after - before);
918 using flow::util::buffers_dump_string;
922 FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(
bool,
try_receive, blob, _1);
926 &&
"As advertised: try_receive() => undefined behavior if not successfully cted or was moved-from.");
928 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Nb-pop to blob @[" << blob->data() <<
"], "
929 "max-size [" << blob->size() <<
"].");
932 unsigned int pri_ignored;
934 static_cast<char*
>(blob->data()),
935 blob->size(), &pri_ignored)) >= 0)
938 FLOW_LOG_TRACE(
"Received message sized [" << n_rcvd <<
"].");
939 if (blob->size() != 0)
941 FLOW_LOG_DATA(
"Blob contents: [\n" << buffers_dump_string(*blob,
" ") <<
"].");
949 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Nb-pop to blob @[" << blob->data() <<
"], "
950 "max-size [" << blob->size() <<
"]: would-block.");
964 using flow::util::buffers_dump_string;
968 if (flow::error::exec_void_and_throw_on_error
970 err_code,
"Posix_mq_handle::receive()"))
977 &&
"As advertised: receive() => undefined behavior if not successfully cted or was moved-from.");
979 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Blocking-pop to blob @[" << blob->data() <<
"], "
980 "max-size [" << blob->size() <<
"]. Trying nb-pop first; if it succeeds -- great. "
981 "Else will wait/retry/wait/retry/....");
988 unsigned int pri_ignored;
993 static_cast<char*
>(blob->data()),
994 blob->size(), &pri_ignored)) >= 0)
997 FLOW_LOG_TRACE(
"Received message sized [" << n_rcvd <<
"].");
998 if (blob->size() != 0)
1000 FLOW_LOG_DATA(
"Blob contents: [\n" << buffers_dump_string(*blob,
" ") <<
"].");
1006 if (errno != EAGAIN)
1014 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Nb-pop to blob @[" << blob->data() <<
"], "
1015 "max-size [" << blob->size() <<
"]: would-block. Executing blocking-wait.");
1025 FLOW_LOG_TRACE(
"Blocking-wait reported transmissibility. Retrying.");
1033 using flow::Fine_clock;
1034 using flow::util::time_since_posix_epoch;
1035 using flow::util::buffers_dump_string;
1036 using boost::chrono::floor;
1037 using boost::chrono::round;
1038 using boost::chrono::seconds;
1039 using boost::chrono::microseconds;
1040 using boost::chrono::nanoseconds;
1044 FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(
bool,
timed_receive, blob, timeout_from_now, _1);
1050 &&
"As advertised: timed_receive() => undefined behavior if not successfully cted or was moved-from.");
1052 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Blocking-timed-pop to blob @[" << blob->data() <<
"], "
1053 "max-size [" << blob->size() <<
"]; timeout ~[" << round<microseconds>(timeout_from_now) <<
"]. "
1054 "Trying nb-pop first; if it succeeds -- great. Else will wait/retry/wait/retry/....");
1057 unsigned int pri_ignored;
1059 auto before = Fine_clock::now();
1064 static_cast<char*
>(blob->data()),
1065 blob->size(), &pri_ignored)) >= 0)
1068 FLOW_LOG_TRACE(
"Received message sized [" << n_rcvd <<
"].");
1069 if (blob->size() != 0)
1071 FLOW_LOG_DATA(
"Blob contents: [\n" << buffers_dump_string(*blob,
" ") <<
"].");
1077 if (errno != EAGAIN)
1085 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Nb-pop to blob @[" << blob->data() <<
"], "
1086 "max-size [" << blob->size() <<
"]: would-block. Executing blocking-wait.");
1098 FLOW_LOG_TRACE(
"Did not finish before timeout.");
1102 FLOW_LOG_TRACE(
"Blocking-wait reported transmissibility. Retrying.");
1104 const auto after = Fine_clock::now();
1105 timeout_from_now -= (after - before);
1112template<
bool SND_ELSE_RCV>
1118 &&
"As advertised: interrupt_impl() => undefined behavior if not successfully cted or was moved-from.");
1121 bool* interrupting_ptr;
1122 if constexpr(SND_ELSE_RCV)
1130 auto& interrupter = *interrupter_ptr;
1131 auto& interrupting = *interrupting_ptr;
1135 FLOW_LOG_WARNING(
"Posix_mq_handle [" << *
this <<
"]: Interrupt mode already ON for "
1136 "snd_else_rcv [" << SND_ELSE_RCV <<
"]. Ignoring.");
1141 interrupting =
true;
1142 FLOW_LOG_INFO(
"Posix_mq_handle [" << *
this <<
"]: Interrupt mode turning ON for "
1143 "snd_else_rcv [" << SND_ELSE_RCV <<
"].");
1151template<
bool SND_ELSE_RCV>
1157 &&
"As advertised: allow_impl() => undefined behavior if not successfully cted or was moved-from.");
1162 bool* interrupting_ptr;
1163 if constexpr(SND_ELSE_RCV)
1171 auto& interrupt_detector = *interrupt_detector_ptr;
1172 auto& interrupting = *interrupting_ptr;
1176 FLOW_LOG_WARNING(
"Posix_mq_handle [" << *
this <<
"]: Interrupt mode already OFF for "
1177 "snd_else_rcv [" << SND_ELSE_RCV <<
"]. Ignoring.");
1182 interrupting =
false;
1183 FLOW_LOG_INFO(
"Posix_mq_handle [" << *
this <<
"]: Interrupt mode turning OFF for "
1184 "snd_else_rcv [" << SND_ELSE_RCV <<
"].");
1194 return interrupt_impl<true>();
1199 return allow_impl<true>();
1204 return interrupt_impl<false>();
1209 return allow_impl<false>();
1212template<
bool SND_ELSE_RCV>
1217 using flow::util::time_since_posix_epoch;
1218 using boost::chrono::round;
1219 using boost::chrono::milliseconds;
1220 using boost::system::system_category;
1223 using Epoll_event = ::epoll_event;
1225 FLOW_ERROR_EXEC_AND_THROW_ON_ERROR(
bool, wait_impl<SND_ELSE_RCV>, timeout_from_now_or_none, _1);
1229 &&
"As advertised: wait_impl() => undefined behavior if not successfully cted or was moved-from.");
1233 int epoll_timeout_from_now_ms;
1234 milliseconds epoll_timeout_from_now;
1235 if (timeout_from_now_or_none == Fine_duration::max())
1237 epoll_timeout_from_now_ms = -1;
1238 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Infinite-await-unstarved for "
1239 "snd_else_rcv [" << SND_ELSE_RCV <<
"]. Will perform an epoll_wait().");
1243 epoll_timeout_from_now = round<milliseconds>(timeout_from_now_or_none);
1244 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Blocking-await/poll-unstarved for "
1245 "snd_else_rcv [" << SND_ELSE_RCV <<
"]; timeout ~[" << epoll_timeout_from_now <<
"] -- "
1246 "if 0 then poll. Will perform an epoll_wait().");
1247 epoll_timeout_from_now_ms = int(epoll_timeout_from_now.count());
1250 array<Epoll_event, 2> evs;
1254 const auto epoll_result = epoll_wait(epoll_hndl, evs.begin(),
sizeof(evs) /
sizeof(Epoll_event),
1255 epoll_timeout_from_now_ms);
1256 if (epoll_result == -1)
1258 const auto& sys_err_code = *err_code = {errno, system_category()};
1259 FLOW_LOG_WARNING(
"Posix_mq_handle [" << *
this <<
"]: epoll_wait() yielded error. Details follow.");
1260 FLOW_ERROR_SYS_ERROR_LOG_WARNING();
1265 assert(epoll_result <= 2);
1266 if ((epoll_result == 2)
1268 ((epoll_result == 1)
1271 if (timeout_from_now_or_none == Fine_duration::max())
1273 FLOW_LOG_INFO(
"Posix_mq_handle [" << *
this <<
"]: Infinite-await-unstarved for "
1274 "snd_else_rcv [" << SND_ELSE_RCV <<
"]: interrupted.");
1278 FLOW_LOG_INFO(
"Posix_mq_handle [" << *
this <<
"]: Blocking-await/poll-unstarved for "
1279 "snd_else_rcv [" << SND_ELSE_RCV <<
"]; timeout ~[" << epoll_timeout_from_now <<
"] -- "
1280 "if 0 then poll: interrupted.");
1287 const bool success = epoll_result == 1;
1288 if (timeout_from_now_or_none == Fine_duration::max())
1290 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Infinite-await-unstarved for "
1291 "snd_else_rcv [" << SND_ELSE_RCV <<
"]: succeeded? = [" << success <<
"].");
1295 FLOW_LOG_TRACE(
"Posix_mq_handle [" << *
this <<
"]: Blocking-await/poll-unstarved for "
1296 "snd_else_rcv [" << SND_ELSE_RCV <<
"]; timeout ~[" << epoll_timeout_from_now <<
"] -- "
1297 "if 0 then poll: succeeded? = [" << success <<
"].");
1306 return wait_impl<true>(util::Fine_duration::zero(), err_code);
1311 wait_impl<true>(util::Fine_duration::max(), err_code);
1316 return wait_impl<true>(timeout_from_now, err_code);
1321 return wait_impl<false>(util::Fine_duration::zero(), err_code);
1326 wait_impl<false>(util::Fine_duration::max(), err_code);
1331 return wait_impl<false>(timeout_from_now, err_code);
1342 using boost::system::system_category;
1345 if (flow::error::exec_void_and_throw_on_error
1348 err_code,
"Posix_mq_handle::remove_persistent()"))
1354 FLOW_LOG_SET_CONTEXT(logger_ptr, Log_component::S_TRANSPORT);
1356 FLOW_LOG_INFO(
"Posix_mq @ Shared_name[" <<
absolute_name <<
"]: Removing persistent MQ if possible.");
1357 if (mq_unlink(shared_name_to_mq_name(
absolute_name).c_str()) == 0)
1364 const auto& sys_err_code = *err_code = {errno, system_category()};
1365 FLOW_LOG_WARNING(
"Posix_mq @ Shared_name[" <<
absolute_name <<
"]: While removing persistent MQ: "
1366 "mq_unlink() yielded error. Details follow.");
1367 FLOW_ERROR_SYS_ERROR_LOG_WARNING();
1372 using boost::system::system_category;
1381 const auto& sys_err_code = *err_code
1382 = (errno == EMSGSIZE)
1385 FLOW_LOG_WARNING(
"Posix_mq_handle [" << *
this <<
"]: mq_*() yielded error; context = [" << context <<
"]. "
1387 FLOW_ERROR_SYS_ERROR_LOG_WARNING();
1399 os <<
'@' << &val <<
": sh_name[" << val.
absolute_name() <<
"] native_handle[";
1401 if (native_handle.null())
1403 return os <<
"null]";
1406 return os << native_handle <<
']';
1412std::string shared_name_to_mq_name(
const Shared_name& name)
1415 std::string mq_name(
"/");
1416 return mq_name += name.
str();
Implements the Persistent_mq_handle concept by wrapping the POSIX message queue API (see man mq_overv...
bool allow_sends()
Implements Persistent_mq_handle API: Turn off preemptive/concurrent interruption of blocking-sends an...
bool interrupt_receives()
Implements Persistent_mq_handle API: Turn on preemptive/concurrent interruption of blocking-receives ...
bool timed_wait_sendable(util::Fine_duration timeout_from_now, Error_code *err_code=nullptr)
Implements Persistent_mq_handle API: Like timed_send() but without the actual pushing of a message.
bool interrupt_sends()
Implements Persistent_mq_handle API: Turn on preemptive/concurrent interruption of blocking-sends and...
util::Pipe_reader Pipe_reader
Short-hand for anonymous pipe read end.
bool timed_wait_receivable(util::Fine_duration timeout_from_now, Error_code *err_code=nullptr)
Implements Persistent_mq_handle API: Like timed_receive() but without the actual popping of a message...
static const Shared_name S_RESOURCE_TYPE_ID
Implements concept API.
bool wait_impl(util::Fine_duration timeout_from_now_or_none, Error_code *err_code)
Impl body for *_sendable() and *_receivable().
void receive(util::Blob_mutable *blob, Error_code *err_code=nullptr)
Implements Persistent_mq_handle API: Blocking receive: pops copy of message from queue into buffer; i...
bool try_send(const util::Blob_const &blob, Error_code *err_code=nullptr)
Implements Persistent_mq_handle API: Non-blocking send: pushes copy of message to queue and returns t...
bool handle_mq_api_result(int result, Error_code *err_code, util::String_view context) const
Helper that handles the result of an mq_*() call by logging WARNING(s) on error; setting *err_code on...
Pipe_reader m_interrupt_detector_snd
A byte is read from this end by allow_sends() to make it not-readable for the poll-wait in wait_impl(...
void wait_receivable(Error_code *err_code=nullptr)
Implements Persistent_mq_handle API: Like receive() but without the actual popping of a message.
Native_handle m_epoll_hndl_snd
epoll_*() handle (.null() if and only if m_mq is null) that is level-triggered to be active (with onl...
bool allow_receives()
Implements Persistent_mq_handle API: Turn off preemptive/concurrent interruption of blocking-receives...
size_t max_msg_size() const
Implements Persistent_mq_handle API: Returns the max message size of the underlying queue.
Pipe_reader m_interrupt_detector_rcv
Other-direction counterpart to m_interrupt_detector_snd.
Error_code epoll_setup()
Ctor helper that sets up m_epoll_hndl_snd and m_epoll_hndl_rcv.
util::Pipe_writer Pipe_writer
Short-hand for anonymous pipe write end.
size_t max_n_msgs() const
Implements Persistent_mq_handle API: Returns the max message count of the underlying queue.
void wait_sendable(Error_code *err_code=nullptr)
Implements Persistent_mq_handle API: Like send() but without the actual pushing of a message.
Pipe_writer m_interrupter_rcv
Other-direction counterpart to m_interrupter_snd.
Native_handle native_handle() const
Implements Persistent_mq_handle API: Returns the stored native MQ handle; null if not open.
friend void swap(Posix_mq_handle &val1, Posix_mq_handle &val2)
Implements Persistent_mq_handle related concept: Swaps two objects.
bool try_receive(util::Blob_mutable *blob, Error_code *err_code=nullptr)
Implements Persistent_mq_handle API: Non-blocking receive: pops copy of message from queue into buffe...
Posix_mq_handle()
Implements Persistent_mq_handle API: Construct null handle.
bool m_interrupting_snd
Starting at false, this is made true via interrupt_sends(), and back by allow_sends(); acts as a guar...
const Shared_name & absolute_name() const
Implements Persistent_mq_handle API: Returns name equal to absolute_name passed to ctor.
void send(const util::Blob_const &blob, Error_code *err_code=nullptr)
Implements Persistent_mq_handle API: Blocking send: pushes copy of message to queue; if queue is full...
Native_handle m_epoll_hndl_rcv
epoll_*() handle (.null() if and only if m_mq is null) that is level-triggered to be active (with onl...
bool allow_impl()
Impl body for allow_*().
bool interrupt_impl()
Impl body for interrupt_*().
bool timed_send(const util::Blob_const &blob, util::Fine_duration timeout_from_now, Error_code *err_code=nullptr)
Implements Persistent_mq_handle API: Blocking timed send: pushes copy of message to queue; if queue i...
~Posix_mq_handle()
Implements Persistent_mq_handle API: Destroys this handle (or no-op if no handle was successfully con...
bool is_sendable(Error_code *err_code=nullptr)
Implements Persistent_mq_handle API: Like try_send() but without the actual pushing of a message.
bool timed_receive(util::Blob_mutable *blob, util::Fine_duration timeout_from_now, Error_code *err_code=nullptr)
Implements Persistent_mq_handle API: Blocking timed receive: pops copy of message from queue into buf...
bool set_non_blocking(bool nb, Error_code *err_code)
Sets m_mq to blocking or non-blocking and returns true on success and clears *err_code; otherwise ret...
Shared_name m_absolute_name
See absolute_name().
static void remove_persistent(flow::log::Logger *logger_ptr, const Shared_name &name, Error_code *err_code=nullptr)
Implements Persistent_mq_handle API: Removes the named persistent MQ.
Error_code pipe_setup()
Ctor helper that sets up m_interrupt* pipe items.
bool m_interrupting_rcv
Other-direction counterpart to m_interrupting_snd.
bool is_receivable(Error_code *err_code=nullptr)
Implements Persistent_mq_handle API: Like try_receive() but without the actual popping of a message.
Pipe_writer m_interrupter_snd
A byte is written to this end by interrupt_sends() to make it readable for the poll-wait in wait_impl...
Native_handle m_mq
Underlying MQ handle.
flow::util::Task_engine m_nb_task_engine
Never used for .run() or .async() – just so we can construct Pipe_reader, Pipe_writer.
Posix_mq_handle & operator=(Posix_mq_handle &&src)
Implements Persistent_mq_handle API: Replaces handle with the source handle while making the latter i...
String-wrapping abstraction representing a name uniquely distinguishing a kernel-persistent entity fr...
static Shared_name ct(const Source &src)
Copy-constructs from a char-sequence container such as string or util::String_view.
void clear()
Makes it so empty() == true.
const std::string & str() const
Returns (sans copying) ref to immutable entire wrapped name string, suitable to pass into sys calls w...
@ S_INTERRUPTED
A blocking operation was intentionally interrupted or preemptively canceled.
@ S_MQ_MESSAGE_SIZE_OVER_OR_UNDERFLOW
Low-level message queue send-op buffer overflow (> max size) or receive-op buffer underflow (< max si...
Flow-IPC module providing transmission of structured messages and/or low-level blobs (and more) betwe...
std::ostream & operator<<(std::ostream &os, const Generic_msg_batch_in< Msg_resource_t, NO_HNDLS > &val)
Prints string representation of the given Generic_msg_batch_in to the given ostream.
util::Shared_name Shared_name
Convenience alias for the commonly used type util::Shared_name.
void swap(Posix_mq_handle &val1, Posix_mq_handle &val2)
Implements Persistent_mq_handle related concept: Swaps two objects.
void swap(Bipc_mq_handle &val1, Bipc_mq_handle &val2)
Implements Persistent_mq_handle related concept: Swaps two objects.
bipc::permissions Permissions
Short-hand for Unix (POSIX) permissions class.
const uint8_t * blob_data(const Blob_const &blob)
Syntactic-sugary helper that returns pointer to first byte in an immutable buffer,...
bipc::open_only_t Open_only
Tag type indicating an ideally-atomic open-if-exists-else-fail operation.
void pipe_produce(flow::log::Logger *logger_ptr, Pipe_writer *pipe)
Writes a byte to the given pipe writer.
const Open_or_create OPEN_OR_CREATE
Tag value indicating an open-if-exists-else-create operation.
boost::asio::mutable_buffer Blob_mutable
Short-hand for a mutable blob somewhere in memory, stored as exactly a void* and a size_t.
void pipe_consume(flow::log::Logger *logger_ptr, Pipe_reader *pipe)
Reads a byte via the given pipe reader.
boost::asio::writable_pipe Pipe_writer
Short-hand for anonymous pipe write end.
bipc::open_or_create_t Open_or_create
Tag type indicating an atomic open-if-exists-else-create operation.
bipc::create_only_t Create_only
Tag type indicating a create-unless-exists-else-fail operation.
flow::Fine_time_pt Fine_time_pt
Short-hand for Flow's Fine_time_pt.
boost::asio::readable_pipe Pipe_reader
Short-hand for anonymous pipe read end.
void set_resource_permissions(flow::log::Logger *logger_ptr, const fs::path &path, const Permissions &perms, Error_code *err_code)
Utility that sets the permissions of the given resource (at the supplied file system path) to specifi...
flow::Fine_duration Fine_duration
Short-hand for Flow's Fine_duration.
const Create_only CREATE_ONLY
Tag value indicating an atomic create-unless-exists-else-fail operation.
boost::asio::const_buffer Blob_const
Short-hand for an immutable blob somewhere in memory, stored as exactly a void const * and a size_t.
flow::util::String_view String_view
Short-hand for Flow's String_view.
Log_component
The flow::log::Component payload enumeration containing various log components used by Flow-IPC inter...
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
A monolayer-thin wrapper around a native handle, a/k/a descriptor a/k/a FD.
constexpr bool null() const noexcept
Returns true if and only if m_native_handle equals S_NULL_HANDLE.
int handle_t
The native handle type. Much logic relies on this type being light-weight (fast to copy).
handle_t m_native_handle
The native handle (possibly equal to S_NULL_HANDLE), the exact payload of this Native_handle.