Flow 1.0.0
Flow project: Public API.
Classes | Public Types | Public Member Functions | Related Functions | List of all members
flow::net_flow::Event_set Class Reference

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>

Inheritance diagram for flow::net_flow::Event_set:
[legend]
Collaboration diagram for flow::net_flow::Event_set:
[legend]

Classes

class  Socket_as_any_equals
 Equality predicate class used in storing various sockets of types wrapped as boost::anys in the Sockets type. More...
 
class  Socket_as_any_hash
 Hasher class used in storing various sockets of types wrapped as boost::anys 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...
 
Nodenode () 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_contextoperator= (const Log_context &src)
 Assignment operator that behaves similarly to the copy constructor. More...
 
Log_contextoperator= (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...
 
Loggerget_logger () const
 Returns the stored Logger pointer, particularly as many FLOW_LOG_*() macros expect. More...
 
const Componentget_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...
 

Detailed Description

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.

Note
Important: With the somewhat more recent availability of the asio::Node hierarchy, which slickly adds boost.asio-event-loop-integration to the Node hierarchy, public use of this class Event_set should be avoided for those already using a boost.asio event loop. It is both significantly simpler – and a bit faster – to use Peer_socket::async_send(), Server_socket::async_accept(), etc. – as well as a bit faster. IN PARTICULAR, the complex discussion in Event_set doc header regarding async_wait() with a glue socket is something one should skip over if one could just integrate directly into a boost.asio loop.
Advice: Even if you don't already use a boost.asio event loop, consider doing so before giving up and going for an Event_set glue pattern as described in this class doc header. Your application may be sigificantly simpler and more maintainable as a result.
Advice: In general, please read the bird's eye view of the entire set of I/O ops in 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.
All that said, Event_set is a very important facility and is, at the very least, built-upon internally for all but the basic non-blocking ops. Advanced users should understand it (even if they don't use it directly).

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.

// NOTE: ASCII art arrows |--------> (etc.) indicate thread execution. "U" is the user thread.
// "K" is unspecified kernel "thread." "W" is unspecified `net_flow` working thread.
// T (cont.) just means thread T was already running up to the point we are illustrating.
U (cont.)
| Set up local socket (e.g., Unix domain socket or localhost TCP socket) comm_sock.
| Set up select() native socket set sel_read_set, for native "readable" events.
| Set up net_flow::Event_set flow_set.
| FD_SET(comm_sock, sel_read_set);
| Set up native TCP peer socket tcp_sock.
| Set up Flow peer socket flow_sock.
| FD_SET(tcp_sock, sel_read_set);
| flow_set.add_wanted_socket(flow_sock, Event_type::S_PEER_SOCKET_READABLE);
| Event loop begins here. <--------------------------------------+ K (cont.) W (cont.)
| flow_set.async_wait(handle_flow_ev); | | ... | ...
| Inform W. -> | | ... | flow_set -> WAITING.
| select(2, sel_read_set, NULL, NULL, ...positive-timeout...); | | ... | ...
| ...select() blocking... | | ... | ...
| ... | | ... | ...
| ... | | tcp_sock readable! | ...
| ... | | <- Tell select(). |
| K says tcp_sock readable! Get ready to return! | | ... | flow_sock readable!
| | | | handle_flow_ev():
| | | | Write 1 -> comm_sock.
| | | comm_sock readable! |
| | | <- Tell select(). |
| K says comm_sock readable, and that's it. Return! | | ... | ...
| flow_set.async_wait_finish(); | v | ...
| Inform W. -> | | ...
| if (FD_ISSET(tcp_sock, sel_read_set)) | | flow_set -> INACTIVE.
| Non-blocking recv(tcp_sock) until exhausted! | v
| if (FD_ISSET(comm_sock, sel_read_set)) |
| In this example we've only 1 Flow socket and event, so: \ |
| flow_sock.async_receive() until exhausted! |
| Event loop ends here. -----------------------------------------+
v
A user-set collection of sockets and desired conditions on those sockets (such as: "socket has data t...
Definition: event_set.hpp:254
Event_type
Type of event or condition of interest supported by class Event_set.
Definition: event_set.hpp:307
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(...
Definition: event_set.cpp:215
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,...
Definition: event_set.cpp:72
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 give...
Definition: event_set.hpp:1015

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)

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.

Relationship between 2 different Event_set objects

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.

Signals and interruption

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.

Thread safety

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.

Member Typedef Documentation

◆ Sockets

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::anys 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.

Member Enumeration Documentation

◆ Event_type

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 Peer_socket sock is such that calling sock->receive() would yield either non-zero ("successfully dequeued received data") or zero and an error (but not zero and NO error).

In other words, specifies the condition where a Peer_socket is Readable.

In Event_set::Sockets structures associated with this Event_set::Event_type, boost::any elements wrap the type: Peer_socket::Ptr. boost::any_cast<> to that type to obtain the socket.

S_PEER_SOCKET_WRITABLE 

Event type specifying the condition of interest wherein a target Peer_socket sock is such that calling sock->send() with a non-empty buffer would yield either non-zero ("successfully enqueued data to be sent") or zero and an error (but not zero and NO error).

In other words, specifies the condition where a Peer_socket is Writable.

In Event_set::Sockets structures associated with this Event_set::Event_type, boost::any elements wrap the type: Peer_socket::Ptr. boost::any_cast<> to that type to obtain the socket.

S_SERVER_SOCKET_ACCEPTABLE 

Event type specifying the condition of interest wherein a target Server_socket serv is such that calling serv->accept() would yield either non-null ("successfully accepted a ready conneection") or null and an error (but not null and NO error).

In other words, specifies the condition where a Server_socket is Acceptable.

In Event_set::Sockets structures associated with this Event_set::Event_type, boost::any elements wrap the type: Server_socket::Ptr. boost::any_cast<> to that type to obtain the socket.

◆ State

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.

Member Function Documentation

◆ add_wanted_socket()

template<typename Socket >
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.

Template Parameters
Sockettype to which ev_type applies. E.g., Peer_socket for PEER_SOCKET_READABLE.
Parameters
sockSocket to add. Must be from the same Node as the one originating this Event_set.
ev_typeThe condition we are interested in sock reaching.
err_codeSee 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.
Returns
true if and only if no error occurred (*err_code is success).

◆ async_wait()

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.

Parameters
on_eventThe 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_codeSee 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.
Returns
true if and only if no error occurred (*err_code is success).

◆ async_wait_finish()

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.

Parameters
err_codeSee flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED.
Returns
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.

◆ clear()

bool flow::net_flow::Event_set::clear ( Error_code err_code = 0)

Forgets all sockets stored in this object in any fashion.

Parameters
err_codeSee flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED, error::Code::S_EVENT_SET_IMMUTABLE_WHEN_WAITING.
Returns
true if and only if no error occurred (*err_code is success).

◆ clear_result_sockets()

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.

Parameters
ev_typeSee emit_result_sockets().
err_codeSame.
Returns
Same.

◆ clear_wanted_sockets()

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.

Parameters
ev_typeSee swap_wanted_sockets().
err_codeSee flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED, error::Code::S_EVENT_SET_IMMUTABLE_WHEN_WAITING.
Returns
true if and only if no error occurred (*err_code is success).

◆ close()

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.)

Parameters
err_codeSee flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED.

◆ emit_result_sockets()

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.

Rationale

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.

How to use

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:

Event_set::Sockets readable_socks; // Note this contains boost::any's as elements, regardless of `ev_type`.
event_set->emit_result_sockets(&readable_socks, Event_set::Event_type::S_PEER_SOCKET_READABLE);
for (const auto& readable_sock_as_any : readable_socks)
{
// Since we asked for S_PEER_SOCKET_READABLE, we know results are all Peer_sockets. Cast to that type:
const Peer_socket::Ptr readable_sock = boost::any_cast<Peer_socket::Ptr>(readable_sock_as_any);
// PEER_SOCKET_READABLE indicates readiness to receive() to yield data or an error. We can now do so!
readable_sock->receive(...); // Details of this call left to reader.
}
@ S_PEER_SOCKET_READABLE
Event type specifying the condition of interest wherein a target Peer_socket sock is such that callin...
An object of this class is a set that combines the lookup speed of an unordered_set<> and ordering an...
Definition: linked_hash_set.hpp:48
boost::shared_ptr< Peer_socket > Ptr
Short-hand for ref-counted pointer to mutable values of type Target_type::element_type (a-la T*).
Definition: shared_ptr_alias_holder.hpp:118
Parameters
target_setPointer 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_typeThe condition we are interested in which sockets have reached during the last wait.
err_codeSee flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED, error::Code::S_EVENT_SET_IMMUTABLE_WHEN_WAITING.
Returns
true if and only if no error occurred (*err_code is success).
Todo:
Event_set::emit_result_sockets() sets a Sockets structure which stores boost:anys each of which stores either a Peer_socket::Ptr or a Server_socket::Ptr; Sockets should be changed to store C++17 std::variants. Performance, both internally and externally, would improve by using this type-safe compile-time mechanism (which is akin to unions but much more pleasant to use). At the time this feature was written, Flow was in C++11, so variants were not available, and the author wanted to play around with anys instead of haxoring old-school unions. variant is much nicer, however, and the dynamic nature of any is entirely unnecessary here.

◆ events_detected()

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.

Note
Conceptually, this is a bit like when 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).
Parameters
err_codeSee 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.
Returns
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).

◆ events_wanted()

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.).

Parameters
err_codeSee flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED.
Returns
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()

Node * flow::net_flow::Event_set::node ( ) const

Node that produced this Event_set.

Note that this may change the moment the method returns (but only to null).

Returns
Pointer to (guaranteed valid) Node; null if state() is S_CLOSED.

◆ poll()

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.

Parameters
err_codeSee 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.
Returns
true if and only if no error occurred (*err_code is success).

◆ remove_wanted_socket()

template<typename Socket >
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().

Template Parameters
Seeadd_wanted_socket().
Parameters
sockSocket to remove.
ev_typeSee add_wanted_socket().
err_codeSee 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.
Returns
true if and only if no error occurred (*err_code is success).

◆ state()

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.

Returns
Ditto.

◆ swap_wanted_sockets()

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:

// Exchange of our empty `socks` with what's in `es`.
es->swap_wanted_sockets(&socks, Event_type::S_PEER_SOCKET_READBLE);
// ...Remove every 3rd socket from `socks`, and log `socks.size()`....
// Now put the modified socket set back into `es`.
es->swap_wanted_sockets(&socks, Event_type::S_PEER_SOCKET_READBLE);

Rationale

The swap paradigm (precursor to the "move" paradigm added in C++11) allows arbitrarily complex operations without sacrificing performance or thread safety.

Parameters
target_setPointer 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_typeSee add_wanted_socket().
err_codeSee flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_EVENT_SET_CLOSED, error::Code::S_EVENT_SET_IMMUTABLE_WHEN_WAITING.
Returns
true if and only if no error occurred (*err_code is success).

◆ sync_wait() [1/2]

template<typename Rep , typename Period >
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.

Template Parameters
RepSee boost::chrono::duration documentation (and see above tip).
PeriodSee boost::chrono::duration documentation (and see above tip).
Parameters
max_waitThe 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_codeSee flow::Error_code docs for error reporting semantics. Generated codes: Same as the other sync_wait() plus: error::Code::S_WAIT_USER_TIMEOUT.
Returns
true if and only if no error occurred (*err_code is success). Timeout expiring IS an error, in particular.

◆ sync_wait() [2/2]

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).

Parameters
err_codeSee 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.
Returns
true if and only if no error occurred (*err_code is success).

Friends And Related Function Documentation

◆ operator<<() [1/2]

std::ostream & operator<< ( std::ostream &  os,
Event_set::Event_type  ev_type 
)
related

Prints string representation of given event type to given standard ostream and returns the latter.

Parameters
osStream to print to.
ev_typeValue to serialize.
Returns
os.

◆ operator<<() [2/2]

std::ostream & operator<< ( std::ostream &  os,
Event_set::State  state 
)
related

Prints string representation of given Event_set state to given standard ostream and returns the latter.

Parameters
osStream to print to.
stateValue to serialize.
Returns
os.

The documentation for this class was generated from the following files: