29 return start_ops<Op::S_RCV>(std::move(ev_wait_func));
40 flow::async::Task_asio_err_sz&& on_done_func)
48 assert(target_hndl &&
"Native_socket_stream::async_receive_native_handle() must take non-null Native_handle ptr.");
50 return async_receive_native_handle_impl(target_hndl, target_meta_blob, sync_err_code, sync_sz,
51 std::move(on_done_func));
56 flow::async::Task_asio_err_sz&& on_done_func)
58 return async_receive_native_handle_impl(
nullptr, target_blob, sync_err_code, sync_sz, std::move(on_done_func));
63 Error_code* sync_err_code_ptr,
size_t* sync_sz,
64 flow::async::Task_asio_err_sz&& on_done_func)
68 if ((!op_started<Op::S_RCV>(
"async_receive_native_handle()")) || (!state_peer(
"async_receive_native_handle()")))
91 if (m_rcv_user_request)
93 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: Async-receive requested, but the preceding such request "
94 "is still in progress; the message has not arrived yet. "
95 "Likely a user error, but who are we to judge? Ignoring.");
102 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: User async-receive request for "
103 "possible native handle and meta-blob (located @ [" << target_meta_blob.data() <<
"] of "
104 "max size [" << target_meta_blob.size() <<
"]).");
106 if (m_rcv_pending_err_code)
108 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User async-receive request for "
109 "possible native handle and meta-blob (located @ [" << target_meta_blob.data() <<
"] of "
110 "max size [" << target_meta_blob.size() <<
"]): Error already encountered earlier. Emitting "
113 sync_err_code = m_rcv_pending_err_code;
120 "Socket streams allow trying to receive into a buffer that could underflow "
121 "with the largest *possible* message -- as long as the actual message "
122 "(if any) happens to be small enough to fit. Otherwise a pipe-hosing "
123 "error shall occur at receipt time.");
125 m_rcv_user_request.emplace();
126 m_rcv_user_request->m_target_hndl_ptr = target_hndl_or_null;
127 m_rcv_user_request->m_target_meta_blob = target_meta_blob;
128 m_rcv_user_request->m_on_done_func = std::move(on_done_func);
130 rcv_read_msg(&sync_err_code, sync_sz);
135 FLOW_LOG_TRACE(
"Async-request completed synchronously (result "
136 "[" << sync_err_code <<
"] [" << sync_err_code.message() <<
"]); emitting synchronously and "
137 "disregarding handler.");
138 m_rcv_user_request.reset();
143 if ((!sync_err_code_ptr) && sync_err_code)
145 throw flow::error::Runtime_error(sync_err_code,
"Native_socket_stream::Impl::async_receive_native_handle_impl()");
148 sync_err_code_ptr && (*sync_err_code_ptr = sync_err_code);
158 using boost::chrono::round;
159 using boost::chrono::milliseconds;
163 assert(timeout.count() > 0);
165 if ((!op_started<Op::S_RCV>(
"idle_timer_run()")) || (!state_peer(
"idle_timer_run()")))
172 if (m_rcv_idle_timeout != Fine_duration::zero())
174 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User wants to start idle timer, but they have already "
175 "started it before. Therefore ignoring.");
180 m_rcv_idle_timeout = timeout;
183 if (m_rcv_pending_err_code)
185 FLOW_LOG_INFO(
"Socket stream [" << *
this <<
"]: User wants to start idle timer, but an error has already "
186 "been found and emitted earlier. It's moot; ignoring.");
191 FLOW_LOG_INFO(
"Socket stream [" << *
this <<
"]: User wants to start idle-timer with timeout "
192 "[" << round<milliseconds>(m_rcv_idle_timeout) <<
"]. Scheduling (will be rescheduled as needed).");
199 m_rcv_ev_wait_func(&m_rcv_ev_wait_hndl_idle_timer_fired_peer,
201 boost::make_shared<Task>
202 ([
this]() { rcv_on_ev_idle_timer_fired(); }));
204 m_rcv_idle_timer.expires_after(m_rcv_idle_timeout);
205 m_timer_worker.timer_async_wait(&m_rcv_idle_timer, m_rcv_idle_timer_fired_peer);
214 m_timer_worker.consume_timer_firing_signal(m_rcv_idle_timer_fired_peer);
216 if (m_rcv_pending_err_code)
218 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Idle timer fired: There's been 0 traffic past idle timeout. "
219 "However an error has already been found and emitted earlier. Therefore ignoring.");
226 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: Idle timer fired: There's been 0 traffic past idle timeout. "
227 "Will not proceed with any further low-level receiving. If a user async-receive request is "
228 "pending (is it? = [" <<
bool(m_rcv_user_request) <<
"]) will emit to completion handler.");
230 if (m_rcv_user_request)
233 const auto on_done_func = std::move(m_rcv_user_request->m_on_done_func);
234 m_rcv_user_request.reset();
235 on_done_func(m_rcv_pending_err_code, 0);
236 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Handler completed.");
248 if (m_rcv_idle_timeout == Fine_duration::zero())
257 const auto n_canceled = m_rcv_idle_timer.expires_after(m_rcv_idle_timeout);
262 FLOW_LOG_INFO(
"Socket stream [" << *
this <<
"]: Finished reading a message, which means "
263 "we just received traffic, which means the idle timer should be rescheduled. However "
264 "when trying to reschedule it, we found we were too late: it was very recently queued to "
265 "be invoked in the near future. An idle timeout error shall be emitted very soon.");
269 assert((n_canceled == 1) &&
"We only issue 1 timer async_wait() at a time.");
275 m_timer_worker.timer_async_wait(&m_rcv_idle_timer, m_rcv_idle_timer_fired_peer);
283 using flow::util::Lock_guard;
285 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Async-receive: Start of payload 1: "
286 "Trying nb-read of payload 1 (handle if any, meta-blob-length) synchronously.");
287 assert(!m_rcv_pending_err_code);
290 const auto n_rcvd_or_zero
291 = rcv_nb_read_low_lvl_payload(&target_hndl,
292 Blob_mutable(&m_rcv_target_meta_length,
sizeof(m_rcv_target_meta_length)),
293 &m_rcv_pending_err_code);
294 if (!m_rcv_pending_err_code)
296 if (n_rcvd_or_zero != 0)
298 FLOW_LOG_TRACE(
"Got some or all of payload 1.");
299 rcv_on_handle_finalized(target_hndl, n_rcvd_or_zero, sync_err_code, sync_sz);
304 FLOW_LOG_TRACE(
"Got nothing but would-block. Awaiting readability.");
313 Lock_guard<
decltype(m_peer_socket_mutex)> peer_socket_lock(m_peer_socket_mutex);
317 m_rcv_ev_wait_func(&m_ev_wait_hndl_peer_socket,
320 boost::make_shared<Task>([
this]()
322 rcv_on_ev_peer_socket_readable_or_error(Rcv_msg_state::S_MSG_START, 0 );
332 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User async-receive request: "
333 "was about to await readability but discovered opposite-direction socket-hosing error; "
334 "emitting error via completion handler (or via sync-args).");
339 assert(m_rcv_pending_err_code);
341 FLOW_LOG_TRACE(
"Nb-read, or async-read following nb-read encountering would-block, detected error (details "
342 "above); will emit via completion handler (or via sync-args).");
344 *sync_err_code = m_rcv_pending_err_code;
353 using flow::util::Lock_guard;
355 assert(m_rcv_user_request);
356 assert(!m_rcv_pending_err_code);
359 const bool proto_negotiating
362 if (proto_negotiating && (!hndl_or_null.
null()))
364 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: Expecting protocol-negotiation (first) in-message "
365 "to contain *only* a meta-blob: but received Native_handle is non-null which is "
366 "unexpected; emitting error via completion handler (or via sync-args).");
371 assert(ok &&
"Protocol_negotiator breaking contract? Bug?");
372 assert(m_rcv_pending_err_code
373 &&
"Protocol_negotiator should have emitted error given intentionally bad version.");
375 else if ((!hndl_or_null.
null()) && (!m_rcv_user_request->m_target_hndl_ptr))
378 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User async-receive request for "
379 "*only* a meta-blob: but received Native_handle is non-null which is "
380 "unexpected; emitting error via completion handler (or via sync-args).");
386 if (m_rcv_user_request->m_target_hndl_ptr
388 && (!proto_negotiating))
390 *m_rcv_user_request->m_target_hndl_ptr = hndl_or_null;
393 if (n_rcvd ==
sizeof(m_rcv_target_meta_length))
396 rcv_on_head_payload(sync_err_code, sync_sz);
405 Lock_guard<
decltype(m_peer_socket_mutex)> peer_socket_lock(m_peer_socket_mutex);
409 m_rcv_ev_wait_func(&m_ev_wait_hndl_peer_socket,
412 boost::make_shared<Task>([
this, n_rcvd]()
414 rcv_on_ev_peer_socket_readable_or_error(Rcv_msg_state::S_HEAD_PAYLOAD,
415 sizeof(m_rcv_target_meta_length) - n_rcvd);
425 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User async-receive request: "
426 "was about to await readability but discovered opposite-direction socket-hosing error; "
427 "emitting error via completion handler (or via sync-args).");
432 assert(m_rcv_pending_err_code);
436 *sync_err_code = m_rcv_pending_err_code;
444 assert(m_rcv_user_request);
445 assert(!m_rcv_pending_err_code);
447 bool proto_negotiating
450 if (proto_negotiating)
457 m_protocol_negotiator.compute_negotiated_proto_ver
459 &m_rcv_pending_err_code);
460 assert(ok &&
"Protocol_negotiator breaking contract? Bug?");
461 proto_negotiating =
false;
463 if (m_rcv_pending_err_code)
466 *sync_err_code = m_rcv_pending_err_code;
472 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Received all of negotiation payload; passed. "
473 "Ignoring other than registering non-idle activity. Proceeding with the next message read.");
476 rcv_read_msg(sync_err_code, sync_sz);
481 if (m_rcv_target_meta_length == S_META_BLOB_LENGTH_PING_SENTINEL)
483 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Received all of payload 1; length prefix "
484 "contains special value indicating a ping. Ignoring other than registering non-idle "
485 "activity. Proceeding with the next message read.");
488 rcv_read_msg(sync_err_code, sync_sz);
493 const auto user_target_size = m_rcv_user_request->m_target_meta_blob.size();
494 if (m_rcv_target_meta_length != 0)
496 if (m_rcv_target_meta_length <= user_target_size)
498 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Received all of payload 1; length prefix "
499 "[" << m_rcv_target_meta_length <<
"] is positive (and not indicative of ping). "
500 "Reading payload 2.");
501 rcv_read_blob(Rcv_msg_state::S_META_BLOB_PAYLOAD,
502 Blob_mutable(m_rcv_user_request->m_target_meta_blob.data(),
503 size_t(m_rcv_target_meta_length)),
504 sync_err_code, sync_sz);
509 FLOW_LOG_WARNING(
"Received all of payload 1; length prefix "
510 "[" << m_rcv_target_meta_length <<
"] is positive (and not indicative of ping); "
511 "however it exceeds user target blob size [" << user_target_size <<
"] and would "
512 "overflow. Treating similarly to a graceful-close but with a bad error code and "
513 "this warning. Will not proceed with any further low-level receiving; will invoke "
514 "handler (failure).");
519 if (m_rcv_user_request->m_target_hndl_ptr && (!m_rcv_user_request->m_target_hndl_ptr->null()))
521 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Received all of payload 1; length 0 + non-null handle => "
522 "handle received with no meta-blob. Will register non-idle activity; "
523 "and invoke handler (success).");
531 FLOW_LOG_INFO(
"Socket stream [" << *
this <<
"]: User message received: Graceful-close-of-incoming-pipe "
532 "message. Will not proceed with any further low-level receiving. "
533 "Will invoke handler (graceful-close error).");
538 *sync_err_code = m_rcv_pending_err_code;
546 if (m_rcv_pending_err_code)
548 FLOW_LOG_WARNING(
"Socket stream [" << *
this <<
"]: User's wait-for-readable finished (readable or error, "
549 "we do not know which yet); would resume processing depending on what we were doing before; "
550 "however an error was detected in the meantime (as of this writing: idle timeout). "
551 "Stopping read chain.");
552 assert((!m_rcv_user_request)
553 &&
"If rcv-error emitted during low-level async-wait, we should have fed it to any pending async-receive.");
558 assert(m_rcv_user_request);
564 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: User-performed wait-for-readable finished (readable or error, "
565 "we do not know which yet). Resuming processing depending on what we were doing before.");
569 case Rcv_msg_state::S_MSG_START:
570 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: In state MSG_START: Reading from byte 0/handle if any.");
571 rcv_read_msg(&sync_err_code, &sync_sz);
574 case Rcv_msg_state::S_HEAD_PAYLOAD:
575 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: In state HEAD_PAYLOAD: "
576 "Reading meta-length/ping/graceful-close specifier: [" << n_left <<
"] bytes left.");
577 rcv_read_blob(Rcv_msg_state::S_HEAD_PAYLOAD,
578 Blob_mutable(
static_cast<uint8_t*
>(
static_cast<void*
>(&m_rcv_target_meta_length))
579 +
sizeof(m_rcv_target_meta_length) - n_left,
581 &sync_err_code, &sync_sz);
584 case Rcv_msg_state::S_META_BLOB_PAYLOAD:
585 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: In state META_BLOB_PAYLOAD: "
586 "Reading meta-blob: [" << n_left <<
"] bytes left.");
587 rcv_read_blob(Rcv_msg_state::S_META_BLOB_PAYLOAD,
588 Blob_mutable(
static_cast<uint8_t*
>(m_rcv_user_request->m_target_meta_blob.data())
589 +
size_t(m_rcv_target_meta_length) - n_left,
591 &sync_err_code, &sync_sz);
601 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Async-op result ready after successful async-wait. "
602 "Executing handler now.");
605 const auto on_done_func = std::move(m_rcv_user_request->m_on_done_func);
606 m_rcv_user_request.reset();
607 on_done_func(sync_err_code, sync_sz);
608 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Handler completed.");
615 using flow::util::Lock_guard;
617 assert(!m_rcv_pending_err_code);
618 assert(m_rcv_user_request);
620 const auto n_rcvd_or_zero = rcv_nb_read_low_lvl_payload(
nullptr, target_blob, &m_rcv_pending_err_code);
621 if (!m_rcv_pending_err_code)
623 if (n_rcvd_or_zero == target_blob.size())
627 case Rcv_msg_state::S_HEAD_PAYLOAD:
628 rcv_on_head_payload(sync_err_code, sync_sz);
630 case Rcv_msg_state::S_META_BLOB_PAYLOAD:
632 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Received all of payload 2 (meta-blob of length "
633 "[" << m_rcv_target_meta_length <<
"]). Will register non-idle activity; "
634 "and invoke handler (or report via sync-args).");
638 assert(!*sync_err_code);
639 *sync_sz = size_t(m_rcv_target_meta_length);
643 case Rcv_msg_state::S_MSG_START:
644 assert(
false &&
"rcv_read_blob() shall be used only for S_*_PAYLOAD phases.");
646 assert(
false &&
"Should not get here.");
650 FLOW_LOG_TRACE(
"Do not have all of requested payload; got would-block. Awaiting readability.");
654 Lock_guard<
decltype(m_peer_socket_mutex)> peer_socket_lock(m_peer_socket_mutex);
658 m_rcv_ev_wait_func(&m_ev_wait_hndl_peer_socket,
661 boost::make_shared<Task>([
this, msg_state,
662 n_left = target_blob.size() - n_rcvd_or_zero]()
664 rcv_on_ev_peer_socket_readable_or_error(msg_state, n_left);
677 assert(m_rcv_pending_err_code);
679 *sync_err_code = m_rcv_pending_err_code;
687 using flow::util::Lock_guard;
698 size_t n_rcvd_or_zero = 0;
702 Lock_guard<
decltype(m_peer_socket_mutex)> peer_socket_lock(m_peer_socket_mutex);
706 if (target_payload_hndl_or_null)
713 target_payload_hndl_or_null, target_payload_blob, err_code);
722 if (!m_peer_socket->non_blocking())
724 FLOW_LOG_TRACE(
"Socket stream [" << *
this <<
"]: Setting boost.asio peer socket non-blocking mode.");
725 m_peer_socket->non_blocking(
true, *err_code);
734 assert(m_peer_socket->non_blocking());
736 FLOW_LOG_TRACE(
"Reading low-level blob directly via boost.asio (blob details logged above hopefully).");
737 n_rcvd_or_zero = m_peer_socket->read_some(target_payload_blob, *err_code);
745 assert(((!*err_code) && (n_rcvd_or_zero != 0))
746 || (*err_code && (n_rcvd_or_zero == 0)));
747 if (*err_code == boost::asio::error::would_block)
757 m_peer_socket.reset();
770 || (n_rcvd_or_zero == 0));
774 FLOW_LOG_TRACE(
"Received nothing due to error [" << *err_code <<
"] [" << err_code->message() <<
"].");
778 FLOW_LOG_TRACE(
"Receive: no error. Was able to receive [" << n_rcvd_or_zero <<
"] of "
779 "[" << target_payload_blob.size() <<
"] bytes.");
780 if (target_payload_hndl_or_null)
782 if (n_rcvd_or_zero != 0)
784 FLOW_LOG_TRACE(
"Interest in native handle; was able to establish its presence or absence; "
785 "present? = [" << (!target_payload_hndl_or_null->
null()) <<
"].");
790 FLOW_LOG_TRACE(
"No interest in native handle.");
794 return n_rcvd_or_zero;
int16_t proto_ver_t
Type sufficient to store a protocol version; positive values identify newer versions of a protocol; w...
static constexpr proto_ver_t S_VER_UNKNOWN
A proto_ver_t value, namely a negative one, which is a reserved value indicating "unknown version"; i...
void rcv_read_blob(Rcv_msg_state msg_state, const util::Blob_mutable &target_blob, Error_code *sync_err_code, size_t *sync_sz)
A somewhat-general utility that continues read chain with the aim to complete the present in-message,...
Rcv_msg_state
Used to organize tje incoming-direction state machine tactically, this indicates what part of payload...
bool start_receive_blob_ops(util::sync_io::Event_wait_func &&ev_wait_func)
See Native_socket_stream counterpart.
size_t rcv_nb_read_low_lvl_payload(Native_handle *target_payload_hndl_or_null, const util::Blob_mutable &target_payload_blob, Error_code *err_code)
Utility that synchronously, non-blockingly attempts to read over m_peer_socket into the target blob a...
void rcv_on_head_payload(Error_code *sync_err_code, size_t *sync_sz)
Reacts to payload 1 having been completely received.
bool start_receive_native_handle_ops(util::sync_io::Event_wait_func &&ev_wait_func)
See Native_socket_stream counterpart.
bool async_receive_blob(const util::Blob_mutable &target_blob, Error_code *sync_err_code, size_t *sync_sz, flow::async::Task_asio_err_sz &&on_done_func)
See Native_socket_stream counterpart.
void rcv_on_ev_idle_timer_fired()
Handler for the async-wait, via util::sync_io::Timer_event_emitter, of the idle timer firing; if stil...
void rcv_on_handle_finalized(Native_handle hndl_or_null, size_t n_rcvd, Error_code *sync_err_code, size_t *sync_sz)
Helper of rcv_read_msg() – it could have been inlined instead of a method but for readability concern...
bool async_receive_native_handle(Native_handle *target_hndl, const util::Blob_mutable &target_meta_blob, Error_code *sync_err_code, size_t *sync_sz, flow::async::Task_asio_err_sz &&on_done_func)
See Native_socket_stream counterpart.
void rcv_read_msg(Error_code *sync_err_code, size_t *sync_sz)
Begins read chain (completing it as synchronously as possible, async-completing the rest) for the nex...
bool idle_timer_run(util::Fine_duration timeout)
See Native_socket_stream counterpart.
size_t receive_blob_max_size() const
See Native_socket_stream counterpart.
void rcv_not_idle()
No-ops if idle_timer_run() is not engaged; otherwise reacts to non-idleness of the in-pipe by resched...
bool async_receive_native_handle_impl(Native_handle *target_hndl_or_null, const util::Blob_mutable &target_meta_blob, Error_code *sync_err_code, size_t *sync_sz, flow::async::Task_asio_err_sz &&on_done_func)
Body of both async_receive_native_handle() and async_receive_blob().
void rcv_on_ev_peer_socket_readable_or_error(Rcv_msg_state msg_state, size_t n_left)
Completion handler, from outside event loop via sync_io pattern, for the async-wait initiated by vari...
size_t receive_meta_blob_max_size() const
See Native_socket_stream counterpart.
static const size_t & S_MAX_META_BLOB_LENGTH
The maximum length of a blob that can be sent by this protocol.
static constexpr bool S_BLOB_UNDERFLOW_ALLOWED
Implements concept API; namely it is true.
flow::log::Logger * get_logger() const
Returns logger (possibly null).
bool start_receive_native_handle_ops(Event_wait_func_t &&ev_wait_func)
Implements Native_handle_receiver API per contract.
size_t receive_meta_blob_max_size() const
Implements Native_handle_receiver API per contract.
size_t nb_read_some_with_native_handle(flow::log::Logger *logger_ptr, Peer_socket *peer_socket_ptr, Native_handle *target_payload_hndl_ptr, const util::Blob_mutable &target_payload_blob, Error_code *err_code, int message_flags)
boost.asio extension similar to peer_socket->non_blocking(true); auto n = peer_socket->read_some(targ...
@ S_LOW_LVL_TRANSPORT_HOSED_CANNOT_RECEIVE
Unable to receive incoming traffic: an earlier-reported, or at least logged, system error had hosed t...
@ S_SYNC_IO_WOULD_BLOCK
A sync_io operation could not immediately complete; it will complete contingent on active async-wait ...
@ S_RECEIVES_FINISHED_CANNOT_RECEIVE
Will not receive message: either opposing user sent graceful-close via API.
@ S_RECEIVER_IDLE_TIMEOUT
No messages (optional auto-pings or otherwise) have been received; optionally configured timeout exce...
@ S_MESSAGE_SIZE_EXCEEDS_USER_STORAGE
User protocol-code mismatch: local user-provided storage cannot fit entire message received from oppo...
@ S_BLOB_RECEIVER_GOT_NON_BLOB
User protocol-code mismatch: local user expected blob only and no native handle; received at least th...
sync_io-pattern counterparts to async-I/O-pattern object types in parent namespace ipc::transport.
Function< void(Asio_waitable_native_handle *hndl_of_interest, bool ev_of_interest_snd_else_rcv, Task_ptr &&on_active_ev_func)> Event_wait_func
In sync_io pattern, concrete type storing user-supplied function invoked by pattern-implementing ipc:...
flow::async::Task Task
Short-hand for polymorphic function (a-la std::function<>) that takes no arguments and returns nothin...
boost::asio::mutable_buffer Blob_mutable
Short-hand for an mutable blob somewhere in memory, stored as exactly a void* and a size_t.
flow::Fine_duration Fine_duration
Short-hand for Flow's Fine_duration.
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.
bool null() const
Returns true if and only if m_native_handle equals S_NULL_HANDLE.