Flow 1.0.0
Flow project: Public API.
|
A user-set collection of sockets and desired conditions on those sockets (such as: "socket has data to read"), with the ability to wait for those conditions to become true and signal the user when so. More...
#include <event_set.hpp>
Classes | |
class | Socket_as_any_equals |
Equality predicate class used in storing various sockets of types wrapped as boost::any s in the Sockets type. More... | |
class | Socket_as_any_hash |
Hasher class used in storing various sockets of types wrapped as boost::any s in the Sockets type. More... | |
Public Types | |
enum class | State { S_INACTIVE , S_WAITING , S_CLOSED } |
A state of an Event_set. More... | |
enum class | Event_type { S_PEER_SOCKET_READABLE , S_PEER_SOCKET_WRITABLE , S_SERVER_SOCKET_ACCEPTABLE } |
Type of event or condition of interest supported by class Event_set. More... | |
using | Sockets = util::Linked_hash_set< boost::any, Socket_as_any_hash, Socket_as_any_equals > |
A set of sockets of one type, used to communicate sets of desired and resulting events in various Event_set APIs. More... | |
using | Event_handler = Function< void(bool)> |
The type for custom handler passed to async_wait(), which is executed when one or more active events detected, or interrupted as if by signal. | |
Public Types inherited from flow::util::Shared_ptr_alias_holder< boost::shared_ptr< Event_set > > | |
using | Ptr = boost::shared_ptr< Event_set > |
Short-hand for ref-counted pointer to mutable values of type Target_type::element_type (a-la T* ). | |
using | Const_ptr = Const_target_ptr |
Short-hand for ref-counted pointer to immutable values of type Target_type::element_type (a-la T const * ). | |
Public Member Functions | |
~Event_set () | |
Boring destructor. Note that deletion is to be handled exclusively via shared_ptr , never explicitly. | |
State | state () const |
Current State of the Event_set. More... | |
Node * | node () const |
Node that produced this Event_set. More... | |
void | close (Error_code *err_code=0) |
Clears all stored resources (any desired events, result events, and any handler saved by async_wait()) and moves state to State::S_CLOSED. More... | |
template<typename Socket > | |
bool | add_wanted_socket (typename Socket::Ptr sock, Event_type ev_type, Error_code *err_code=0) |
Adds the given socket to the set of sockets we want to know are "ready" by the definition of the given event type. More... | |
template<typename Socket > | |
bool | remove_wanted_socket (typename Socket::Ptr sock, Event_type ev_type, Error_code *err_code=0) |
Opposite of add_wanted_socket(). More... | |
bool | swap_wanted_sockets (Sockets *target_set, Event_type ev_type, Error_code *err_code) |
Efficiently exchanges the current set of sockets we want to know are "ready" by the definiton of the given event type. More... | |
bool | clear_wanted_sockets (Event_type ev_type, Error_code *err_code=0) |
Identical to swap_wanted_sockets(&sockets, ev_type, err_code) , where originally sockets is empty and is afterwards cleared; but more efficient. More... | |
bool | events_wanted (Error_code *err_code=0) const |
Returns true if and only if at least one wanted event for at least one socket is registered (via add_wanted_socket(), swap_wanted_sockets(), etc.). More... | |
bool | poll (Error_code *err_code=0) |
Checks for all previously described events that currently hold, saves them for retrieval via emit_result_sockets(), etc., and returns. More... | |
bool | sync_wait (Error_code *err_code=0) |
Blocks indefinitely until one or more of the previously described events hold – or the wait is interrupted; saves them for retrieval via emit_result_sockets(), etc. More... | |
template<typename Rep , typename Period > | |
bool | sync_wait (const boost::chrono::duration< Rep, Period > &max_wait, Error_code *err_code=0) |
Same as the other sync_wait() but will stop waiting if the timeout given as argument expires. More... | |
bool | async_wait (const Event_handler &on_event, Error_code *err_code=0) |
Moves object to State::S_WAITING state, saves the given handler to be executed later (in a different, unspecified thread), when one or more of the previously described events hold; and immediately returns. More... | |
bool | async_wait_finish (Error_code *err_code=0) |
Moves object from State::S_WAITING to State::S_INACTIVE, and forgets any handler saved by async_wait(), or does nothing if state is already INACTIVE. More... | |
bool | events_detected (Error_code *err_code=0) const |
Returns true if and only if the last wait, if any, detected at least one event. More... | |
bool | emit_result_sockets (Sockets *target_set, Event_type ev_type, Error_code *err_code=0) |
Gets the sockets that satisfy the condition of the given Event_type detected during the last wait. More... | |
bool | clear_result_sockets (Event_type ev_type, Error_code *err_code=0) |
Identical to emit_result_sockets(&sockets, ev_type, err_code) , where originally sockets is empty and is afterwards cleared; but more efficient. More... | |
bool | clear (Error_code *err_code=0) |
Forgets all sockets stored in this object in any fashion. More... | |
Public Member Functions inherited from flow::log::Log_context | |
Log_context (Logger *logger=0) | |
Constructs Log_context by storing the given pointer to a Logger and a null Component. More... | |
template<typename Component_payload > | |
Log_context (Logger *logger, Component_payload component_payload) | |
Constructs Log_context by storing the given pointer to a Logger and a new Component storing the specified generically typed payload (an enum value). More... | |
Log_context (const Log_context &src) | |
Copy constructor that stores equal Logger* and Component values as the source. More... | |
Log_context (Log_context &&src) | |
Move constructor that makes this equal to src , while the latter becomes as-if default-constructed. More... | |
Log_context & | operator= (const Log_context &src) |
Assignment operator that behaves similarly to the copy constructor. More... | |
Log_context & | operator= (Log_context &&src) |
Move assignment operator that behaves similarly to the move constructor. More... | |
void | swap (Log_context &other) |
Swaps Logger pointers and Component objects held by *this and other . More... | |
Logger * | get_logger () const |
Returns the stored Logger pointer, particularly as many FLOW_LOG_*() macros expect. More... | |
const Component & | get_log_component () const |
Returns reference to the stored Component object, particularly as many FLOW_LOG_*() macros expect. More... | |
Related Functions | |
(Note that these are not member functions.) | |
std::ostream & | operator<< (std::ostream &os, Event_set::State state) |
Prints string representation of given Event_set state to given standard ostream and returns the latter. More... | |
std::ostream & | operator<< (std::ostream &os, Event_set::Event_type ev_type) |
Prints string representation of given event type to given standard ostream and returns the latter. More... | |
Additional Inherited Members | |
Static Public Member Functions inherited from flow::util::Shared_ptr_alias_holder< boost::shared_ptr< Event_set > > | |
static Ptr | ptr_cast (const From_ptr &ptr_to_cast) |
Provides syntactic-sugary way to perform a static_pointer_cast<> from a compatible smart pointer type From_ptr , typically From_ptr::element_type being in the same class hierarchy as Target_ptr::element_type . More... | |
static Const_ptr | const_ptr_cast (const From_ptr &ptr_to_cast) |
Identical to ptr_cast() but adds const -ness (immutability) to the pointed-to type. More... | |
static Ptr | dynamic_ptr_cast (const From_ptr &ptr_to_cast) |
Equivalent to ptr_cast() but a dynamic_pointer_cast instead of static. More... | |
static Const_ptr | dynamic_const_ptr_cast (const From_ptr &ptr_to_cast) |
Identical to const_ptr_cast() but a dynamic_pointer_cast instead of static. More... | |
A user-set collection of sockets and desired conditions on those sockets (such as: "socket has data to read"), with the ability to wait for those conditions to become true and signal the user when so.
net_flow
; this is found in asio::Node doc header. This should help hugely in choosing the right type of operating mode in the context of a non-trivial application, possibly featuring other types of I/O in addition to the Flow protocol. This fulfills the same role as BSD sockets' select()
(and its more powerful non-portable equivalents like epoll, kqueue, etc.). In addition to the feature set provided by functions such as select()
, Event_set provides a way to work together with the OS's built-in select()
equivalent; that is to say it provides a way to add Flow-protocol sockets into an event loop written in terms of select()
or spritiually similar facilities (epoll, kqueue, etc.).
The simplest way to use Event_set is to not use it directly at all. Instead, use the blocking methods such as Peer_socket::sync_receive(), Peer_socket::sync_send(), Node::sync_connect(), and Server_socket::sync_accept(). Such methods will internally construct an Event_set with the target socket and perform work (namely some Event_set::sync_wait() calls) on it, hidden from the caller. This mode of operation is analogous to BSD sockets' blocking functions (e.g., recv()
in blocking mode – POSIX O_NONBLOCK
bit set / WinSock FIONBIO
mode is enabled).
This is often insufficient, as in event loops of any complexity. The next simplest way to use Event_set is to use a synchronous wait. To do so, construct an Event_set, add the desired (socket, desired event on that socket) pairs to it (see the swap_wanted_sockets(), add_wanted_socket(), remove_wanted_socket() methods), then perform sync_wait() (with an optional timeout). Once that method returns (which happens once either 1 or more events are active, there is a timeout, or wait is interrupted via Node::interrupt_all_waits()), examine which events (if any) were found to be active, then perform the appropriate operation (e.g., serv->accept()
if Acceptable, sock->send()
if Writable) on each active socket/event pair. This mode of operation is analogous to FD_SET(); select(); FD_ISSET()
or epoll_ctl(); epoll_wait()
. The key feature is that the sync_wait() (like select()
or epoll_wait()
) call blocks the calling thread until there are events or a timeout or a global interrupt (the latter similar to POSIX EINTR
caused by a signal). Additionally, the Event_set::poll() method is analogous to a select()
call with a 0 timeout.
The above is sufficient when writing an event loop that works only with Flow-protocol sockets. However, many practical applications will use an event loop that works with Flow-protocol sockets and other resources (e.g., TCP sockets, pipes) simultaneously. Then what?
We now discuss Event_set::async_wait(). The basic deficiency of sync_wait() is simply that it will only wake up if there's a Flow-protocol event and knows nothing about anything else. Similarly, your select()
-or-equivalent-of-choice will know nothing of Flow-protocol events, since it can only work with standard file descriptors (or whatever), not Flow sockets. For this case Event_set provides asynchronous waiting. As before, you'll construct an Event_set and add some desired sockets/events. Then, call async_wait(F)
, where F()
is a function. async_wait() will IMMEDIATELY return. However, another, unspecified, thread will wait for the desired condition(s) to become true. Once that occurs, that unspecified thread will call F()
. F()
can do whatever you want (but see restrictions documented on async_wait()); however in the use case described in this paragraph it will probably want to do something that will wake up the select()
(or epoll_wait()
, or ...) of your main event loop. For example, you can set up a Unix domain socket or pipe and add it into your select()
(or equivalent's) event set; and then have F()
write 1 byte to it. Once this occurs, that select()
(etc.) will wake up; you can call async_wait_finish()
and then check the active Flow-protocol events using the same techniques as shown in the preceding paragraph, after having called sync_wait().
The following illustrates the preceding paragraph. In this example, we are interested in a single TCP socket from which to read; and a single Flow socket from which to read. This can be extended to more native and Flow socket types, more sockets, and more sophisticated native mechanisms than select()
; but basically this is it. (However, in case of select()
and its peculiar FD_SET()
semantics, please note that this examples combines sel_read_set
to handle both the special local "linking" socket comm_sock
and the TCP-readable socket tcp_sock
. If you want to check for another type of event than being "readable" – e.g., "writable" – then with select()
you'd need to make a separate event set, perhaps named sel_write_set
, and it would not be handling comm_sock
, in whose case we only care about readability. Note also that the example event loop iteration shown below models situation where tcp_sock
and flow_sock
happen to both become readable just about at the same time, causing select()
to return with 2 sockets in readable state simultaneously for illustration purposes. Realistically probably one would become readable; then in another loop iteration the other would... but this would be annoyingly lengthy to show; and though this is less probable, it's still possible.
The above are informal suggestions for use. Here is the more formal description of operation of an Event_set. Event_set is a simple state machine with three states: INACTIVE, WAITING, CLOSED. After construction (which is done by Node in a factory fashion), Event_set is in INACTIVE state. CLOSED means the Event_set has been Event_set::close()d, or the originating Node has been destroyed. It is not possible to perform any operations on a CLOSED Event_set, nor is it possible to exit the CLOSED state. A CLOSED Event_set stores no resources and will be deleted via shared_ptr<>
mechanics once all user references to it are gone.
The rest of the time, Event_set switches back and forth between INACTIVE and WAITING. In INACTIVE, you may set and examine the desired sockets/events. The following are supported (see also Event_set::Event_type enum
values)
sock->receive()
with unlimited target buffer space would return either a non-zero number of bytes or indicate an error. (Therefore "not Readable" means it would return 0 but no error.)sock->send()
. Note, however, that typically this is more likely to be immediately true on a given socket; if there's space in the Send buffer for a certain small amount of data, then the socket owning it is Writable; whereas data have to actually have arrived from the other side for it to be Readable. Nevertheless, if network conditions are such that the Send buffer cannot be purged via sending its contents to the other side, then eventually it won't be Writable either (so don't count on writability without checking).serv->accept()
would return either a non-null Server_socket::Ptr or indicate an error. (Therefore "not Acceptable" means it would return null but no error.)The desired events can be specified with swap_wanted_sockets(), add_wanted_socket(), and remove_wanted_socket(). Note that all of these methods are efficient (no copying of socket sets involved). Also note that all the socket objects passed to an Event_set must come from the same Node as that Event_set; else behavior is undefined.
Also in INACTIVE, you may examine the results of the last wait, if any. Do this using emit_result_sockets(), etc. Note that these methods are also efficient (no copying) and can only be used once for each wait (all subsequent uses yield empty socket sets).
In WAITING state, a wait for the specified events has started. async_wait(), sync_wait(), and poll() change state from INACTIVE to WAITING. In WAITING state, all of the above methods return errors. The following pieces of code change state back to INACTIVE: async_wait_finish(), sync_wait() (once an event is true, or timeout), poll() (immediately), and an unspecified non-user thread if async_wait() was used, and an event has been asynchronously detected.
In particular, if Event_set e
is used by 1 thread, and that thread performs only sync_wait()s for waiting, user code will never be able to observe the WAITING state, as sync_wait() will go INACTIVE->WAITING->INACTIVE internally.
They are entirely independent. In particular, you may put the same socket+event combo into 2 different Event_set objects and wait on both Event_sets simultaneously. If that socket/event holds for both Event_set objects at the same time, both will be signalled. I informally recommend against using this. For example, if the event is "sock is
Readable," and you will receive()
as a result, which of the two receive()
calls gets what data? However there may be a reasonable use case for the Acceptable event.
Any POSIX blocking function, once it has started waiting, will exit with errno == EINTR
if a signal is delivered. (Sometimes this is just annoying, but sometimes it's useful: if SIGTERM handler sets some global terminate flag, then the signal delivery will immediately break out of the blocking call, so the flag can be immediately checked by the main code (and then the program can cleanly exit, for example).) Event_set::sync_wait() and Event_set::async_wait() support similar functionality. Call Node::interrupt_all_waits() to interrupt any Event_set object(s) within that Node currently in WAITING state. They enter INACTIVE state, and it is indicated to the user of each sync_wait() or async_wait() that the reason for the wait's finish was an interruption (see those functions for details on how this is communicated). Conceptually this is similar to POSIX blocking select()
or blocking recv()
returning -1/EINTR
.
To actually cause signals to trigger Node::interrupt_all_waits() (as occurs by default, conceptually, in POSIX programs w/r/t EINTR
), the user has two basic options. They can either register signal handlers that'll explicitly invoke that method; or they can let Node do so automatically for SIGINT and SIGTERM. This is controlled by the Node option Node_options::m_st_capture_interrupt_signals_internally.
Same as for Peer_socket. (Briefly: all operations safe for simultaneous execution on separate or the same object.) An informal recommendation, however, is to only use a given Event_set in one thread. Otherwise things will get confusing, quickly, with no practical benefit of which I, at least, am aware.
using flow::net_flow::Event_set::Sockets = util::Linked_hash_set<boost::any, Socket_as_any_hash, Socket_as_any_equals> |
A set of sockets of one type, used to communicate sets of desired and resulting events in various Event_set APIs.
As a rule, a given Sockets object will store sockets of one underlying type; meaning the boost::any
s stored inside one such set can ALL be boost::any_cast<>
to the same type. Which type that is is usually determined by the associated Event_type value, typically supplied alongside a Sockets set in another argument. For example, if ev_type == Event_type::S_PEER_SOCKET_READABLE
, then every boost::any
in the set can be decoded as follows: Peer_socket::Ptr sock = any_cast<Peer_socket::Ptr>(sock_as_any)
, where sock_as_any
is an element in a Sockets.
As of this writing, the type is chronologically ordered; meaning sockets will be stored in order from latest to oldest to be inserted into the structure. E.g., emit_result_sockets() will produce a set containing of readable sockets, in reverse chronological order in which they were detected as being readable. This may or may not be useful in debugging.
|
strong |
Type of event or condition of interest supported by class Event_set.
When specifying interest in some socket reaching a certain condition, or when requesting the results of that interest, the user essentially specifies a pair of data: an enumeration value of this type and a socket of a certain type (that type is specified in the very name of the Event_type). For example, I may be interested in Event_type::S_PEER_SOCKET_READABLE becoming true for Peer_socket::Ptr sock
. The precise meaning is documented for each enumeration value.
Enumerator | |
---|---|
S_PEER_SOCKET_READABLE | Event type specifying the condition of interest wherein a target In other words, specifies the condition where a Peer_socket is Readable. In Event_set::Sockets structures associated with this Event_set::Event_type, |
S_PEER_SOCKET_WRITABLE | Event type specifying the condition of interest wherein a target In other words, specifies the condition where a Peer_socket is Writable. In Event_set::Sockets structures associated with this Event_set::Event_type, |
S_SERVER_SOCKET_ACCEPTABLE | Event type specifying the condition of interest wherein a target In other words, specifies the condition where a Server_socket is Acceptable. In Event_set::Sockets structures associated with this Event_set::Event_type, |
|
strong |
A state of an Event_set.
Enumerator | |
---|---|
S_INACTIVE | Default state; valid Event_set that is not currently waiting on events. All user operations are valid in this state. |
S_WAITING | Waiting state: valid Event_set that is currently waiting on previously described events. In this state only async_wait_finish() may be called without resulting in an error. |
S_CLOSED | Node has disowned the Peer_socket; all further operations will result in error. |
bool flow::net_flow::Event_set::add_wanted_socket | ( | typename Socket::Ptr | sock, |
Event_type | ev_type, | ||
Error_code * | err_code = 0 |
||
) |
Adds the given socket to the set of sockets we want to know are "ready" by the definition of the given event type.
See individual Event_type enumeration members' doc comments for exact definition of readiness for each Event_type. For example, Event_type::S_PEER_SOCKET_READABLE means we want to know when sock->receive()
would yield either some data or an error, but not no data and no error.
Socket | type to which ev_type applies. E.g., Peer_socket for PEER_SOCKET_READABLE. |
sock | Socket to add. Must be from the same Node as the one originating this Event_set. |
ev_type | The condition we are interested in sock reaching. |
err_code | See flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_ALREADY_EXISTS, error::Code::S_EVENT_SET_CLOSED, error::Code::S_EVENT_SET_IMMUTABLE_WHEN_WAITING. |
true
if and only if no error occurred (*err_code
is success). bool flow::net_flow::Event_set::async_wait | ( | const Event_handler & | on_event, |
Error_code * | err_code = 0 |
||
) |
Moves object to State::S_WAITING state, saves the given handler to be executed later (in a different, unspecified thread), when one or more of the previously described events hold; and immediately returns.
State will go back to State::S_INACTIVE when the handler fires; or when async_wait_finish() is called by the user (whichever happens first). State may also change to State::S_CLOSED if this->close()
is called, or the Node is destroyed. The saved handler will be forgotten at that time. Once INACTIVE is entered, emit_result_sockets(), etc., are to be used to access the detected events. on_event() must take one bool argument. If this argument is false
, use emit_result_sockets(), etc., to access the detected events. If this argument is true
, then Node::interrupt_all_waits() was invoked and has interrupted this wait (conceptually similar to EINTR
in POSIX). In the latter case, do no use emit_result_sockets(), etc., as no events are active due to the interruption.
In a non-error invocation, state() will be INACTIVE before the call and WAITING after it. In an error invocation, state() will not change, and the method will return immediately. Additionally, on_event() will NEVER be called, if another thread calls this->close()
before async_wait_finish() is called, and before the Node is destroyed. So don't do that.
Restrictions on what on_event()
is allowed to do: It is allowed to do anything except make any net_flow
call related to the net_flow::Node originating the current Event_set; doing so results in undefined behavior. Informally, it also must not block; spending significant time in on_event()
will disrupt the functionality of the Node. Even more informally, the goal of on_event()
should be to quickly signal the user's thread(s) that the events hold (using the technique of the user's choice) and then return – beyond that, the handling of the ready events should be in the user's thread(s).
Tip: Call async_wait_finish() before checking the saved results, using emit_result_sockets(). Doing so BEFORE any events are detected will finish this asynchronous wait. Doing so AFTER any events are detected will be a harmless NOOP.
Tip: Use lambdas (or bind()
) to make async_wait() asynchronously call any arbitrary function or method with any arbitrary arguments – NOT just a free void
function with 1 argument. Outside the scope of discussion here, but if this doesn't ring a bell, please look into lambdas (bind()
as a backup).
Rationale for no timeout argument: Since the caller of async_wait() retains flow control without blocking, the user code can enforce its own timeout logic, if necessary, and simply call async_wait_finish() when desired. In fact that is just what sync_wait() (with timeout argument) does internally.
on_event | The function to call as soon as as one or more events previously described hold, AND this->state() is still State::S_WAITING. (Also see above tip.) on_event(bool was_interrupted) will be called. |
err_code | See flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_DOUBLE_WAIT_OR_POLL, error::Code::S_EVENT_SET_CLOSED. |
true
if and only if no error occurred (*err_code
is success). bool flow::net_flow::Event_set::async_wait_finish | ( | Error_code * | err_code = 0 | ) |
Moves object from State::S_WAITING to State::S_INACTIVE, and forgets any handler saved by async_wait(), or does nothing if state is already INACTIVE.
Use this to cut short an asynchronous wait started by async_wait(). After return, emit_result_sockets(), etc., can be used to check the active events (if any) detected during the last wait.
In a non-error invocation, state() will be State::S_INACTIVE or State::S_WAITING before the call and State::S_INACTIVE after it. In an error invocation, state() will not change.
You might call async_wait_finish() while another thread is executing a timeout-less sync_wait() (which is also invoked by blocking methods like Peer_socket::sync_receive()). However that will cause that sync_wait() to NEVER return. So don't do that.
err_code | See flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED. |
true
if and only if no error occurred (*err_code
is success). In particular, state() being State::S_INACTIVE when the method starts is not an error. bool flow::net_flow::Event_set::clear | ( | Error_code * | err_code = 0 | ) |
Forgets all sockets stored in this object in any fashion.
err_code | See flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED, error::Code::S_EVENT_SET_IMMUTABLE_WHEN_WAITING. |
bool flow::net_flow::Event_set::clear_result_sockets | ( | Event_type | ev_type, |
Error_code * | err_code = 0 |
||
) |
Identical to emit_result_sockets(&sockets, ev_type, err_code)
, where originally sockets
is empty and is afterwards cleared; but more efficient.
ev_type | See emit_result_sockets(). |
err_code | Same. |
bool flow::net_flow::Event_set::clear_wanted_sockets | ( | Event_type | ev_type, |
Error_code * | err_code = 0 |
||
) |
Identical to swap_wanted_sockets(&sockets, ev_type, err_code)
, where originally sockets
is empty and is afterwards cleared; but more efficient.
ev_type | See swap_wanted_sockets(). |
err_code | See flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED, error::Code::S_EVENT_SET_IMMUTABLE_WHEN_WAITING. |
true
if and only if no error occurred (*err_code
is success). void flow::net_flow::Event_set::close | ( | Error_code * | err_code = 0 | ) |
Clears all stored resources (any desired events, result events, and any handler saved by async_wait()) and moves state to State::S_CLOSED.
In particular Node will have disowned this object by the time close() returns.
You might call close() while state is State::S_WAITING, but if a timeout-less sync_wait() is executing in another thread, it will NEVER return. Similarly, if state is currently WAITING due to async_wait(), the handler saved by async_wait() will NEVER be called. So don't do that. However, first closing one or more sockets being waited on by those calls and THEN calling this->close()
is perfectly safe, in that sync_wait() will exit, or the handler will be called. (In fact when Node shuts down it does just that.)
err_code | See flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED. |
bool flow::net_flow::Event_set::emit_result_sockets | ( | Sockets * | target_set, |
Event_type | ev_type, | ||
Error_code * | err_code = 0 |
||
) |
Gets the sockets that satisfy the condition of the given Event_type detected during the last wait.
More precisely, moves all sockets satisfying that condition detected during the last wait (if any) into the set provided by the user. Because it is a move (for efficiency among other reasons), the subsequent calls to the same method will yield empty sets (until the next wait operation). Calling before any wait will also yield an empty set.
Note that the accumulated sockets are NOT preserved across waits. That is, if you start a wait, the preceding wait's results are wiped out.
Making the method a one-off that returns nothing after the first invocation (per wait) allows for thread safety without sacrificing efficiency by adding set copy.
First, prepare a (usually empty) target socket set structure. Second, call emit_result_sockets() to transfer the results to it. Third, for each socket of interest, any_cast<>
it from boost::any
to the socket pointer of the appropriate type. (Recall that boost::any
stores an object of any type inside it, so to get access to that you must know to what to cast it; but this is easy, since by specifying ev_type
you are implying a certain socket type.) Example:
target_set | Pointer to set of sockets to which to load the current internal set of result sockets of type ev_type found during the last wait. Any elements here at call time will be removed. |
ev_type | The condition we are interested in which sockets have reached during the last wait. |
err_code | See flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED, error::Code::S_EVENT_SET_IMMUTABLE_WHEN_WAITING. |
true
if and only if no error occurred (*err_code
is success).boost:any
s each of which stores either a Peer_socket::Ptr
or a Server_socket::Ptr
; Sockets should be changed to store C++17 std::variant
s. Performance, both internally and externally, would improve by using this type-safe compile-time mechanism (which is akin to union
s but much more pleasant to use). At the time this feature was written, Flow was in C++11, so variant
s were not available, and the author wanted to play around with any
s instead of haxoring old-school union
s. variant
is much nicer, however, and the dynamic nature of any
is entirely unnecessary here. bool flow::net_flow::Event_set::events_detected | ( | Error_code * | err_code = 0 | ) | const |
Returns true
if and only if the last wait, if any, detected at least one event.
In other words, returns true
if and only if emit_result_sockets() would currently emit at least one socket, if tried with all possible Event_type.
One can still use emit_result_sockets()
to get the specific events after calling this.
select()
returns 0 or higher; and one uses the check of whether its return value is 0 or non-zero. Non-zero is actually some complex index thing, but often that detail is not necessary (much like emit_result_sockets()
is unnecessary, analogously), as the mere presence or absence of 1+ events is enough information. For example, if only one event for one socket is being waited on, one can check this and confidently perform the appropriate I/O operation for that one socket, if and only if this returns true
– or select()
would return non-zero. Slightly more wastefully, but still not bad at all, is when (say) 2 event types are being waited on, but for only 1 socket. In that case true
return => just perform both I/O operations; one OR both of them should yield something (and the one that doesn't hardly uses any resources). Similarly, even if you're waiting on a few sockets, if it's a limited number (like, say, 2-3), then indiscriminately trying all possible I/O on all 2-3 sockets is only slightly wasteful: and the code is quite a bit shorter than carefully checking emit_result_sockets()
(or doing FD_ISSET()
, etc., in the analogous select()
code). err_code | See flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED, error::Code::S_EVENT_SET_RESULT_CHECK_WHEN_WAITING. |
true
if there are active events; false
if there are no active events (then *err_code
is success) or there was an error (*err_code
is failure; i.e., bool(*err_code) == true
). bool flow::net_flow::Event_set::events_wanted | ( | Error_code * | err_code = 0 | ) | const |
Returns true
if and only if at least one wanted event for at least one socket is registered (via add_wanted_socket(), swap_wanted_sockets(), etc.).
err_code | See flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED. |
true
if there are wanted events; false
if there are no wanted events (then *err_code
is success) or there was an error (*err_code
is failure; i.e., bool(*err_code) == true
). Node * flow::net_flow::Event_set::node | ( | ) | const |
bool flow::net_flow::Event_set::poll | ( | Error_code * | err_code = 0 | ) |
Checks for all previously described events that currently hold, saves them for retrieval via emit_result_sockets(), etc., and returns.
This is akin to a non-blocking sync_wait() (which does not exist; poll() does), a/k/a a select()
with a timeout of zero.
In a non-error invocation, state() will be State::S_INACTIVE before and after the call. In an error invocation, state() will not change.
err_code | See flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED, error::Code::S_EVENT_SET_DOUBLE_WAIT_OR_POLL. |
true
if and only if no error occurred (*err_code
is success). bool flow::net_flow::Event_set::remove_wanted_socket | ( | typename Socket::Ptr | sock, |
Event_type | ev_type, | ||
Error_code * | err_code = 0 |
||
) |
Opposite of add_wanted_socket().
See | add_wanted_socket(). |
sock | Socket to remove. |
ev_type | See add_wanted_socket(). |
err_code | See flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_EVENT_DOES_NOT_EXIST, error::Code::S_EVENT_SET_CLOSED, error::Code::S_EVENT_SET_IMMUTABLE_WHEN_WAITING. |
true
if and only if no error occurred (*err_code
is success). Event_set::State flow::net_flow::Event_set::state | ( | ) | const |
Current State of the Event_set.
Note that this may change the moment the method returns.
bool flow::net_flow::Event_set::swap_wanted_sockets | ( | Sockets * | target_set, |
Event_type | ev_type, | ||
Error_code * | err_code | ||
) |
Efficiently exchanges the current set of sockets we want to know are "ready" by the definiton of the given event type.
See individual Event_type enumeration members' doc comments for exact definition of readiness for each Event_type. For example, Event_type::S_PEER_SOCKET_READABLE means we want to know when sock->receive()
would yield either some data or an error, but not no data and no error. Use this to perform arbitrarily complex operations on the internal set storing sockets of interest for the given event type ev_type
, when the add_wanted_socket() and remove_wanted_socket() methods are insufficient. For example:
The swap paradigm (precursor to the "move" paradigm added in C++11) allows arbitrarily complex operations without sacrificing performance or thread safety.
target_set | Pointer to set of sockets to which to load the current internal set. Currently contained sockets must be from the same Node that originated this Event_set. |
ev_type | See add_wanted_socket(). |
err_code | See flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED, error::Code::S_EVENT_SET_IMMUTABLE_WHEN_WAITING. |
true
if and only if no error occurred (*err_code
is success). bool flow::net_flow::Event_set::sync_wait | ( | const boost::chrono::duration< Rep, Period > & | max_wait, |
Error_code * | err_code = 0 |
||
) |
Same as the other sync_wait() but will stop waiting if the timeout given as argument expires.
If the timeout expires, it is the error code error::Code::S_WAIT_USER_TIMEOUT. This is akin to a select()
call with a finite timeout.
An additional error situation is possible in addition to that described in the 2nd/3rd paragraphs of the other sync_wait()'s doc header: if this is close()
d during the wait, and the wait times out, error::Code::S_EVENT_SET_CLOSED will be emitted when this timeout is detected. On the positive side, that means sync_wait(timeout)
will eventually exit no matter what.
Tip: Typical types you might use for max_wait
: boost::chrono::milliseconds
, boost::chrono::seconds
, boost::chrono::high_resolution_clock::duration
.
No guarantees are made as to the accuracy of the timeout timer, although you can optimistically provide arbitrarily precise values for max_wait
.
Rep | See boost::chrono::duration documentation (and see above tip). |
Period | See boost::chrono::duration documentation (and see above tip). |
max_wait | The maximum amount of time from now to wait before giving up on the wait and returning. "duration<Rep, Period>::max()" will eliminate the time limit and cause indefinite wait. |
err_code | See flow::Error_code docs for error reporting semantics. Generated codes: Same as the other sync_wait() plus: error::Code::S_WAIT_USER_TIMEOUT. |
true
if and only if no error occurred (*err_code
is success). Timeout expiring IS an error, in particular. bool flow::net_flow::Event_set::sync_wait | ( | Error_code * | err_code = 0 | ) |
Blocks indefinitely until one or more of the previously described events hold – or the wait is interrupted; saves them for retrieval via emit_result_sockets(), etc.
; and returns. This is akin to a select()
call with no (i.e., infinite) timeout.
The special case of Node::interrupt_all_waits() interrupting this wait – which is conceptually similar to EINTR
in POSIX – manifests itself as error::Code::S_WAIT_INTERRUPTED. In this case, emit_result_sockets() (etc.) should not be used, as one should assume no events are active due to the interruption.
In a non-error invocation, state() will be State::S_INACTIVE before and after the call, unless underlying Node is destroyed, in which case the final state may be State::S_CLOSED. In an error invocation, state() will not change, and the method will return immediately. Additionally, the method will NEVER return, if another thread calls this->close()
or this->async_wait_finish()
during this sync_wait() (so don't do that).
err_code | See flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_WAIT_INTERRUPTED, error::Code::S_EVENT_SET_NO_EVENTS, error::Code::S_EVENT_SET_DOUBLE_WAIT_OR_POLL, error::Code::S_EVENT_SET_CLOSED. |
true
if and only if no error occurred (*err_code
is success).
|
related |
Prints string representation of given event type to given standard ostream
and returns the latter.
os | Stream to print to. |
ev_type | Value to serialize. |
os
.
|
related |
Prints string representation of given Event_set state to given standard ostream
and returns the latter.
os | Stream to print to. |
state | Value to serialize. |
os
.