Flow-IPC 1.0.2
Flow-IPC project: Full implementation reference.
|
Internal class template comprising API/logic common to every Session_server variant, meant to be private
ly sub-classed and largely forwarded.
More...
#include <session_server_impl.hpp>
Classes | |
struct | State |
All internal mutable state of Session_server_impl. More... | |
Public Types | |
using | Session_server_obj = Session_server_t |
See this_session_srv(). More... | |
using | Server_session_obj = Server_session_t |
Useful short-hand for the concrete Server_session type emitted by async_accept(). More... | |
using | Mdt_reader_ptr = typename Server_session_obj::Mdt_reader_ptr |
Short-hand for Session_mv::Mdt_reader_ptr. More... | |
using | Channels = typename Server_session_obj::Channels |
Short-hand for Session_mv::Channels. More... | |
Public Member Functions | |
template<typename Per_app_setup_func > | |
Session_server_impl (flow::log::Logger *logger_ptr, Session_server_obj *this_session_srv_arg, const Server_app &srv_app_ref, const Client_app::Master_set &cli_app_master_set_ref, Error_code *err_code, Per_app_setup_func &&per_app_setup_func) | |
See Session_server ctor; it does that. More... | |
~Session_server_impl () | |
See Session_server dtor. More... | |
template<typename Task_err , typename N_init_channels_by_srv_req_func , typename Mdt_load_func > | |
void | async_accept (Server_session_obj *target_session, Channels *init_channels_by_srv_req, Mdt_reader_ptr *mdt_from_cli_or_null, Channels *init_channels_by_cli_req, N_init_channels_by_srv_req_func &&n_init_channels_by_srv_req_func, Mdt_load_func &&mdt_load_func, Task_err &&on_done_func) |
See Session_server method. More... | |
void | to_ostream (std::ostream *os) const |
See Server_session method. More... | |
Session_server_obj * | this_session_srv () |
Returns pointer to the object that is private ly sub-classing us. More... | |
Public Attributes | |
const Server_app & | m_srv_app_ref |
See Session_server public data member. More... | |
Protected Member Functions | |
template<typename Task > | |
void | sub_class_set_deinit_func (Task &&task) |
Utility for sub-classes: ensures that task() is invoked near the end of *this dtor's execution, after all other (mutable) state has been destroyed, including stopping/joining any threads performing async async_accept() ops. More... | |
Private Types | |
using | Server_session_dtl_obj = Server_session_dtl< Server_session_obj > |
Short-hand for concrete Server_session_dtl type, which each async_accept() creates internally, completes the log-in process upon, and then up-casts to Server_session_obj to emit to user via move-assignment. More... | |
using | Incomplete_session = boost::shared_ptr< Server_session_dtl_obj > |
Internally used ref-counted handle to a Server_session_dtl_obj, suitable for capturing and passing around lambdas. More... | |
using | Incomplete_session_observer = boost::weak_ptr< Server_session_dtl_obj > |
weak_ptr observer of an Incomplete_session. More... | |
using | Incomplete_sessions = boost::unordered_set< Incomplete_session > |
Short-hand for set of Incomplete_session, with fast insertion and removal by key Incomplete_session itself. More... | |
using | Mutex = flow::util::Mutex_non_recursive |
Short-hand for State::m_mutex type. More... | |
using | Lock_guard = flow::util::Lock_guard< Mutex > |
Short-hand for Mutex lock. More... | |
Private Attributes | |
Session_server_obj *const | m_this_session_srv |
See this_session_srv(). More... | |
const Client_app::Master_set & | m_cli_app_master_set_ref |
See ctor. More... | |
const Function< Error_code(const Client_app &client_app)> | m_per_app_setup_func |
See ctor. More... | |
std::optional< State > | m_state |
See State. More... | |
Function< void()> | m_deinit_func_or_empty |
See sub_class_set_deinit_func(). .empty() unless that was called at least once. More... | |
Related Functions | |
(Note that these are not member functions.) | |
template<typename Session_server_t , typename Server_session_t > | |
std::ostream & | operator<< (std::ostream &os, const Session_server_impl< Session_server_t, Server_session_t > &val) |
Prints string representation of the given Session_server_impl to the given ostream . More... | |
Internal class template comprising API/logic common to every Session_server variant, meant to be private
ly sub-classed and largely forwarded.
In particular the vanilla Session_server (see its short Implementation doc header section) sub-classes us and makes no use of the available customization points. Contrast with, e.g., shm::classic::Session_server which uses customization points for shm::classic SHM-arena setup.
The available customization points are as follows.
*this
is parameterized on Server_session_obj. The vanilla value is Server_session<...>
; but to add capabilities sub-class Server_session_impl as explained in its doc header and proceed from there. For example see shm::classic::Server_session. Server_session_impl has its own customization point(s).per_app_setup_func
to ctor.Error_code()
always.Generally the impl should be reasonably easy to follow by reading the method bodies, at least if one understands the general ipc::session client-server paradigm.
The thing to understand strategically, as usual, is the thread design. This is an acceptor, so one might have expected it to be implemented similarly to, say, Native_socket_stream_acceptor – maintaining a thread W in which to do some/most work; including maintaining a "deficit" queue of oustanding async_accept() requests and a "surplus" queue of ready Server_session
s to emit. This was an option, but I (ygoldfel) felt that piggy-backing handling of events directly onto the unspecified handler-invoking threads of the internally used objects would produce a much simpler data structure and state machine. (As a side effect, the behavior described in "FIFO" section above occurs. Also as a side effect, the error-emission behavior described in "Error handling" above occurs. Basically: each async_accept()'s internal handling is independent of the others. They share (almost) no state; there is no single async-chain and explicit queues unlike in various other boost.asio-like classes in ipc. Each async_accept() triggers async op 1, the handler for which triggers async op 2, the handler for which emits the result to user. This is simpler to implement, but it also results in sensible API contract behavior, I feel.)
Here is how it works.
For each potential Server_session
– i.e., for each async_accept() – there are 2 steps that must occur asynchronously before one is ready to emit to the user-supplied handler:
static_cast<>
to Server_session
and emitted to the user.Nomenclature: We call the N_s_s_a::async_accept()
handler-invoking thread: Wa. It is officially an unspecified thread or threads, but, by contract, handlers are executed non-concurrently, so it can be considered one thread (and actually it is as of this writing). We call the Server_session::async_accept_log_in() handler-invoking thread: Ws. This is really a separate thread for each Server_session
, so in fact 2 different async-accept requests can cause 2 handlers to invoke simultaneously.
So each time one calls async_accept() from thread U (i.e., user thread(s) from which they must never invoke mutating stuff concurrently), that kicks off transport::Native_socket_stream_acceptor::async_accept(), which fires handler in thread Wa; we then kick off Server_session::async_accept_log_in()
, which fires handler in thread Ws, where we finalize the Server_session
and emit it to user. There is no cross-posting to some other worker thread W (but read on).
The obvious question is, do the handlers, some of which (as noted) may run concurrently to each other (request 1's Ws handler can co-execute with request 2's Wa handler; and request 2's Wa handler can co-execute with request 3's Wa handler), mess each other over by mutatingly accessing common data? Let's consider the state involved.
For each async-accept request, the amassed data are independent from any other's; they are passed around throughout the 2 async ops per request via lambda captures. There is, however, one caveat to this: Suppose S->accept_log_in(F)
is invoked on not-yet-ready (incomplete) Server_session* S
; suppose F is invoked with a truthy Error_code (it failed). We are now sitting in thread Ws: and S should be destroyed. But invoking dtor of S from within S's own handler is documented to be not-okay and results in a deadlock/infinite dtor execution, or if the system can detect it, at best an abort due to a thread trying to join itself. So:
*this
dtor runs before then, the S dtors will still run, as each queued lambda's captures are destroyed.)However note that State::m_incomplete_sessions is added-to in thread Wa but removed-from in various threads Ws. Therefore it is protected by a mutex; simple enough.
Server_session_t | See Server_session_obj. Its API must exactly equal (or be a superset of) that of vanilla Server_session. (Its impl may perform extra steps; for example async_accept_log_in() might set up a per-session SHM arena.) |
Session_server_t | The class that is in fact private ly sub-classing us. This is necessary for this_session_srv(). See its doc header for discussion. |
Definition at line 143 of file session_server_impl.hpp.
using ipc::session::Session_server_impl< Session_server_t, Server_session_t >::Channels = typename Server_session_obj::Channels |
Short-hand for Session_mv::Channels.
Definition at line 160 of file session_server_impl.hpp.
|
private |
Internally used ref-counted handle to a Server_session_dtl_obj, suitable for capturing and passing around lambdas.
It is shared_ptr
, not unique_ptr
, for two reasons. Primarily, it is so that it can be captured via Incomplete_session_observer to avoid a leak that would result from capturing Incomplete_session in a lambda passed-to an async op on an Incomplete_session itself. unique_ptr
cannot be observed via weak_ptr
; shared_ptr
can.
Secondarily, a unique_ptr
cannot be captured in a lambda in the first place.
Definition at line 335 of file session_server_impl.hpp.
|
private |
weak_ptr
observer of an Incomplete_session.
Capturing this, instead of Incomplete_session itself, allows for the underlying Incomplete_session to be destroyed while the lambda still exists.
Definition at line 341 of file session_server_impl.hpp.
|
private |
Short-hand for set of Incomplete_session, with fast insertion and removal by key Incomplete_session itself.
Definition at line 344 of file session_server_impl.hpp.
|
private |
Short-hand for Mutex lock.
Definition at line 350 of file session_server_impl.hpp.
using ipc::session::Session_server_impl< Session_server_t, Server_session_t >::Mdt_reader_ptr = typename Server_session_obj::Mdt_reader_ptr |
Short-hand for Session_mv::Mdt_reader_ptr.
Definition at line 157 of file session_server_impl.hpp.
|
private |
Short-hand for State::m_mutex type.
Definition at line 347 of file session_server_impl.hpp.
|
private |
Short-hand for concrete Server_session_dtl type, which each async_accept() creates internally, completes the log-in process upon, and then up-casts to Server_session_obj to emit to user via move-assignment.
Server_session_dtl_obj is equal to Server_session_obj – it adds no data – but exposes certain internally invoked APIs that the user shall not access.
Definition at line 321 of file session_server_impl.hpp.
using ipc::session::Session_server_impl< Session_server_t, Server_session_t >::Server_session_obj = Server_session_t |
Useful short-hand for the concrete Server_session
type emitted by async_accept().
Definition at line 154 of file session_server_impl.hpp.
using ipc::session::Session_server_impl< Session_server_t, Server_session_t >::Session_server_obj = Session_server_t |
See this_session_srv().
Definition at line 151 of file session_server_impl.hpp.
|
explicit |
See Session_server ctor; it does that.
In addition:
takes and memorizes a functor that takes a Client_app const ref that identifies the app that wants to open the session, performs unspecified synchronous steps, and returns an Error_code indicating success or reason for failure which dooms that async_accept().
per_app_setup_func
It is not intended for per-session setup. Server_session_dtl_obj should take care of that where it makes sense – it does after all represent the individual budding session peer. However our sub-class (e.g., shm::classic::Session_server) may need to keep track of per-distinct-Client_app resources (e.g., the per-app-scope SHM arena) which must exist before the opposing Client_session-type object completes its setup (e.g., by opening the aforementioned per-Client_app/multi-instance-scope SHM arena). It can detect a new Client_app is logging-in and set that up in the nick of time.
Per_app_setup_func | See above. Signature: Error_code F(const Client_app&) . |
logger_ptr | See Session_server ctor. |
srv_app_ref | See Session_server ctor. |
cli_app_master_set_ref | See Session_server ctor. |
err_code | See Session_server ctor. Additional Error_code generated: see per_app_setup_func . |
per_app_setup_func | See above. |
this_session_srv_arg | The object that is, in fact, private ly sub-classing *this (and calling this ctor). See this_session_srv(). The value is only saved but not dereferenced inside the ctor. |
Definition at line 437 of file session_server_impl.hpp.
References ipc::session::Server_session_dtl< Server_session_t >::base(), ipc::session::build_conventional_shared_name_prefix(), ipc::util::Shared_name::ct(), ipc::session::ensure_resource_owner_is_app(), ipc::session::App::m_group_id, ipc::session::App::m_name, ipc::session::Server_app::m_permissions_level_for_client_apps, ipc::session::Session_server_impl< Session_server_t, Server_session_t >::m_srv_app_ref, ipc::session::Session_server_impl< Session_server_t, Server_session_t >::m_state, ipc::session::App::m_user_id, ipc::util::op_with_possible_bipc_exception(), ipc::util::OPEN_OR_CREATE, ipc::util::PRODUCER_CONSUMER_RESOURCE_PERMISSIONS_LVL_MAP, ipc::session::error::S_MUTEX_BIPC_MISC_LIBRARY_ERROR, ipc::session::error::S_RESOURCE_OWNER_UNEXPECTED, ipc::util::set_resource_permissions(), and ipc::util::shared_resource_permissions().
ipc::session::Session_server_impl< Session_server_t, Server_session_t >::~Session_server_impl |
See Session_server dtor.
Definition at line 715 of file session_server_impl.hpp.
void ipc::session::Session_server_impl< Session_server_t, Server_session_t >::async_accept | ( | Server_session_obj * | target_session, |
Channels * | init_channels_by_srv_req, | ||
Mdt_reader_ptr * | mdt_from_cli_or_null, | ||
Channels * | init_channels_by_cli_req, | ||
N_init_channels_by_srv_req_func && | n_init_channels_by_srv_req_func, | ||
Mdt_load_func && | mdt_load_func, | ||
Task_err && | on_done_func | ||
) |
See Session_server method.
In addition: invokes per_app_setup_func()
(from ctor) once the connecting Client_app becomes known; if that returns truthy Error_code then this method emits that error.
Task_err | See Session_server method. |
N_init_channels_by_srv_req_func | See Session_server method. |
Mdt_load_func | See Session_server method. |
target_session | See Session_server method. |
init_channels_by_srv_req | See Session_server method. |
mdt_from_cli_or_null | See Session_server method. |
init_channels_by_cli_req | See Session_server method. |
n_init_channels_by_srv_req_func | See Session_server method. |
mdt_load_func | See Session_server method. |
on_done_func | See Session_server method. |
Definition at line 746 of file session_server_impl.hpp.
References ipc::util::Shared_name::ct(), ipc::transport::error::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER, and ipc::session::error::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER.
|
protected |
Utility for sub-classes: ensures that task()
is invoked near the end of *this
dtor's execution, after all other (mutable) state has been destroyed, including stopping/joining any threads performing async async_accept() ops.
It may be invoked at most once.
The value it adds: A long story best told by specific example. See the original use case which is in shm::classic::Session_server; it sets up certain SHM cleanup steps to occur, when the session-server is destroyed.
At the time task()
runs, the calling instance of the sub-class has been destroyed – thus it is, e.g., usually wrong to capture your this
in the task
lambda, except for logging. get_logger()
and get_log_component()
(which are in this super-class) are still okay to use.
Task | Function object invoked as void with no args. |
task | task() shall execute before dtor returns. |
Definition at line 987 of file session_server_impl.hpp.
Session_server_impl< Session_server_t, Server_session_t >::Session_server_obj * ipc::session::Session_server_impl< Session_server_t, Server_session_t >::this_session_srv |
Returns pointer to the object that is private
ly sub-classing us.
In other words this equals static_cast<const Session_server_obj*>(this)
, where this class is the base of Session_server_obj, but up-casting from a private
base is not allowed.
I (ygoldfel) acknowledge this is somewhat odd. Why should a sub-class, per se, care or know about its super-class? This at least vaguely indicates some sort of design mistake. In fact this is needed, as of this writing, because shm::classic::Server_session_impl::async_accept_log_in() gets a Session_server_impl ptr, which it knows points to an object that's really the core of a shm::classic::Session_server, and it needs to interact with the SHM-classic-specific aspect of that guy's API. So it calls this accessor here, essentially as a way to up-cast from a private
base (which is not allowed by C++). Why can't it "just" take a shm::classic::Session_server*
then? Answer: because Session_server_impl uses, like, virtual
-less polymorphism to invoke async_accept_log_in()
regardless of which object it's calling it on.... It's hard to summarize here in words in any way that'll make sense, but if one looks at the relevant code it makes sense. Eventually. Bottom line is, this way, it can just pass-in this
, and then shm::classic::Server_session_impl::async_accept_log_in() can call this_session_srv() to get at the super-class version of this
.
I feel it is not criminal – internal things are working together in a way that they logically intend to – but intuitively it feels like there's a smoother way to design it. Probably.
virtual
hierarchies Session_server
, Server_session
, Session_server_impl
, Server_session_impl
cooperate internally, as there is some funky stuff going on, particularly Session_server_impl::this_session_srv().Definition at line 980 of file session_server_impl.hpp.
void ipc::session::Session_server_impl< Session_server_t, Server_session_t >::to_ostream | ( | std::ostream * | os | ) | const |
See Server_session method.
os | See Server_session method. |
Definition at line 974 of file session_server_impl.hpp.
|
related |
Prints string representation of the given Session_server_impl
to the given ostream
.
os | Stream to which to write. |
val | Object to serialize. |
os
. Definition at line 993 of file session_server_impl.hpp.
|
private |
See ctor.
Definition at line 412 of file session_server_impl.hpp.
|
private |
See sub_class_set_deinit_func(). .empty()
unless that was called at least once.
Definition at line 421 of file session_server_impl.hpp.
|
private |
See ctor.
Definition at line 415 of file session_server_impl.hpp.
const Server_app& ipc::session::Session_server_impl< Session_server_t, Server_session_t >::m_srv_app_ref |
See Session_server public data member.
Definition at line 285 of file session_server_impl.hpp.
Referenced by ipc::session::Session_server_impl< Session_server_t, Server_session_t >::Session_server_impl().
|
private |
See State.
Definition at line 418 of file session_server_impl.hpp.
Referenced by ipc::session::Session_server_impl< Session_server_t, Server_session_t >::Session_server_impl().
|
private |
See this_session_srv().
Definition at line 409 of file session_server_impl.hpp.