Flow-IPC 3.0.0
Flow-IPC project: Full implementation reference.
context_server.hpp
Go to the documentation of this file.
1/* Flow-IPC: Shared Memory
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
19#pragma once
20
22#include "ipc/session/app.hpp"
23#include "ipc/session/error.hpp"
24#include "ipc/common.hpp"
25#include <flow/log/log.hpp>
26#include <kj/async.h>
27#include <kj/string.h>
28#include <boost/move/unique_ptr.hpp>
29#include <boost/shared_ptr.hpp>
30
32{
33
34// Types.
35
36/**
37 * An object of this type facilitates the establishment, in server style, of zero-copy-enabled capnp-RPC
38 * conversation(s) with opposing process's or processes' `Client_context`s. On each successful connect, it
39 * produces a Server_context, each of which represents one such conversation until its destruction,
40 * exactly mirroring the opposing Client_context in PEER state.
41 *
42 * @see ipc::transport::struc::shm::rpc namespace doc header for an overview of Flow-IPC capnp-RPC support, including
43 * how the present class relates to the others there.
44 *
45 * @note No Server_context object coming from a `this->accept()` must exist past the destruction of `*this`.
46 * Context_server maintains certain important commonly shared resources, including SHM, and essentially
47 * precedes and must outlive all relevant IPC work. (This mirrors the relationship between
48 * session::Session_server and `session::Server_session`s.)
49 *
50 * @see Before continuing to read here, please read Client_context doc header. It is simpler, as it involves
51 * only one session (conversation); and in particular a Client_context after successful
52 * Client_context::sync_connect() (a/k/a having reached PEER state) is *exactly* equivalent in abilities
53 * to a Server_context produced by a `*this` (via successful accept()). In any case the Background
54 * section is appropriate here too; we assume knowledge of that.
55 *
56 * ### Background ###
57 * Armed with the aforementioned knowledge, consider the server side of things. For each potential capnp-RPC
58 * session, individually, the steps are fairly similar: accept (instead of connect); then a Server_context
59 * shall bundle the required `VatNetwork` and Flow-IPC session::Session (in PEER state). From that point on
60 * it's exactly the same: make `RpcSystem`, etc.
61 *
62 * The two differences are as follows:
63 * - Client_context::sync_connect() is non-blocking and synchronous: it either works quickly, or it fails quickly.
64 * Whereas Context_server::accept() naturally must await an incoming connection (it is asynchronous).
65 * Since we're in capnp-RPC/KJ land here, we've made accept() `kj::Promise`-based: accept() returns a
66 * `Promise` which is essentially fulfilled once an async-accept succeeds. It'll contain chiefly
67 * the resulting Server_context. Armed with that you proceed ~identically to how you might on the opposing side.
68 * - (Aside: Customarily with capnp-RPC, the guy who connected is *also* set-up to be the client-side of the
69 * "interface bootstrap" operation, and conversely the guy that accepted provides the bootstrap
70 * interface *implementation*. I.e. they'd use (typically) `makeRpcClient()` and `makeRpcServer()` respectively.
71 * Reminder: That is not mandatory at all; which side chooses to connect versus accept is entirely
72 * orthogonal to how one uses the resulting `VatNetwork` after that.)
73 * - If this session-server accepts conversations with 2+ different session-client applications
74 * (e.g., `/bin/x` and `/bin/y` are different apps), then you may need to identify which one you're talking
75 * to w/r/t a given Server_context. Use session::Server_session::client_app() accessor to yield the
76 * session::Client_app (including session::Client_app::m_name). So if accept() returned a `Promise`, and
77 * it was fulfilled with object `X` then: `X.m_context->session()->client_app()->m_name`.
78 * - Client_context represents a single conversation: once in PEER state, it cannot exit that state and cannot
79 * be reused to connect again, nor in parallel. Similarly Server_context is the mirror image of that (except
80 * it is *always* in PEER state to begin-with). However a Context_server can generate *multiple* active/overlapping
81 * conversations with opposing peers. E.g., on one successful accept() it is possible/typical to invoke another.
82 * Thus 2+ `Server_context`s can be active simultaneously.
83 * - Of course this is not mandatory. Many server applications maintain only a single ongoing session in a
84 * process. Up to you!
85 *
86 * As to the latter point: Context_server is *not* intended to represent an automatically self-perpetuating
87 * server (accepting machine). It is up to you to invoke accept(), when you want a potential new session.
88 * This is intentional: We want to simplify integrating Flow-IPC ipc::session machinery with zero-copy-enabled
89 * capnp-RPC (our rpc::Session_vat_network being the key), but we want you to be able to form your own
90 * accept loop (if you even want it to be a loop).
91 *
92 * @see Try Ez_rpc_server -- analogous to `capnp::EzRpcServer`. With that, you pass-in `capnp::Capability::Client`
93 * interface impl(s) and launch it -- at which point it'll auto-run an accept loop that will allow for
94 * an arbitrary number of concurrently-served capnp-RPC sessions.
95 *
96 * ### How to use Server_context ###
97 * Call accept(). Typically chain it with your handler of a successful async-accept op. Namely that might look
98 * like:
99 *
100 * ~~~
101 * ipc::transport::struc::shm::rpc::Context_server ctx_srv{..., &kj_async_io_context,
102 * ..., // session::Server_app identifying *this* app.
103 * ..., // Master session::Client_app set.};
104 * // It is listening now.
105 * decltype(ctx_srv)::Server_context_obj::Ptr ctx_ptr;
106 * ctx_srv.accept() // Somewhat more advanced args to accept() are optional.
107 * .then([&](auto&& accept_result) // Type of accept_result is Context_server::Accept_result: a simple struct.
108 * {
109 * ctx_ptr = std::move(accept_result.m_context);
110 * }).wait(kj_async_io_context.waitScope);
111 * auto& ctx = *ctx_ptr;
112 * // ctx is exactly equivalent to opposing Client_context post-.sync_connect()! E.g.:
113 * auto* network = ctx.vat_network();
114 * // ...Create RpcSystem using `network` -- off you go!
115 * ~~~
116 *
117 * (Of course this is KJ: You can combine the above promise chain with whatever would kick off your RPC-system
118 * loop after the above snippet, so there's only one `.wait(kj_async_io_context.waitScope)` to combine both
119 * async ops. Or have two .`wait()`s in series. Explaining KJ/promises: not in our scope here.)
120 *
121 * Bottom line: Once you've got your `ctx` as above, the API/semantics of it and the opposing-side Client_context
122 * in PEER state (after `.sync_connect()`) are *exactly* identical. So see its doc header w/r/t PEER state!
123 * E.g. the section "For Flow-IPC-savvy users / Direct SHM use" applies exactly here as well. In addition these
124 * may be of interest:
125 * - session::Server_session::client_app() (explained above).
126 * - (SHM-classic) session::shm::classic::Session_server::app_shm() (cross-session SHM arena).
127 * - session::shm::classic::Session_server::pool_size_limit_mi() (before affected accept()).
128 * - (SHM-jemalloc) session::shm::arena_lend::jemalloc::Session_server::app_shm() (cross-session SHM arena).
129 *
130 * @tparam Session_server_t
131 * A concrete type a-la session::Session_server but SHM-enabled. As of this writing
132 * out of the box the available types mirror those listed in Client_context doc header under:
133 * How to use Client_context.
134 * Naturally, the opposing Client_context must be parameterized in a compatible fashion.
135 * (E.g., `Client_context<ipc::session::shm::classic::Client_session<...knobs...>>` <=>
136 * `Context_server<ipc::session::shm::classic::Session_server<...same-knobs...>>`.)
137 */
138template<typename Session_server_t>
140 public flow::log::Log_context,
141 public kj::TaskSet::ErrorHandler,
142 private boost::noncopyable
143{
144public:
145 // Types.
146
147 /// Convenience alias for template arg.
148 using Session_server_obj = Session_server_t;
149 static_assert(Session_server_obj::Server_session_obj::S_SHM_ENABLED,
150 "Context_server facilitates the use of Flow-IPC zero-copy (SHM) facilities for "
151 "zero-copy performance in using capnp-RPC; therefore the supplied Session_server_t template "
152 "parameter must be a SHM-enabled Session_server variant, session::shm::*::Session_server.");
153 static_assert(Session_server_obj::Server_session_obj::Channel_obj::S_HAS_NATIVE_HANDLE_PIPE,
154 "Context_server, per its protocol with the opposing Client_context, receives a native handle -- "
155 "the stream-socket underlying each capnp-RPC conversation -- over the first init-channel; "
156 "therefore the supplied Session_server_t must be configured to emit Native_handle-bearing "
157 "channels (e.g., see aliases in session::shm::*::rpc namespaces).");
159 /// The Server_context concrete type generated by accept(); counterpart to Client_context.
161
162 /// Bundles the results of a successful accept(); until then all elements default-cted.
164 {
165 // Data.
166
167 /// The `kj::Own`-like handle to established Server_context opposite the other guy's PEER-state Client_context.
169 /// Opened init-channels, numbering as many as you requested (see accept() doc header); may well be `.empty()`.
170 typename Session_server_obj::Channels m_init_channels_by_srv_req;
171 /// Opened init-channels, numbering as many as the opposing client requested; may well be `.empty()`.
172 typename Session_server_obj::Channels m_init_channels_by_cli_req;
173 };
174
175 // Constructors/destructor.
176
177 /**
178 * Constructs us: immediately begins listening for incoming session-open attempts from allowed client applications'
179 * `Client_context`s. Use accept() to establish such a capnp-RPC (zero-copy-ified) session by yielding (mainly)
180 * a Server_context (exactly identical in capabilities to the opposing PEER-state Client_context).
181 *
182 * @warning `srv_app_ref` and `cli_app_master_set_ref` -- and the `Client_app`s to which the latter
183 * (transitively) refers -- must remain alive past this ctor: their *addresses*
184 * are stored and accessed at various points later. As usual, unless null, same for `*logger_ptr`.
185 * **The required lifetime of these objects may exceed that of the constructed `*this`.**
186 * Therefore:
187 * @see Please read Session_vat_network doc header section "Object lifetimes." It explains how/why and more
188 * importantly how long these objects must stay alive. Promise: it's pretty easy. (Hard part is to not
189 * assume they just need to outlive `*this`. The design of capnp-RPC/KJ is the reason for this.)
190 * @note Spoiler alert: An easy approach is to keep the `App` and `*logger_ptr` objects alive past
191 * `*kj_io`.
192 *
193 * ### Context (white-boxy info) ###
194 * Listening to incoming capnp-RPC connections means to first listen for Flow-IPC session-open attempts,
195 * as documented briefly in session::Session_server ctor doc header; namely (to briefly recap)
196 * "this will write to the CNS (PID file) and then listen on Unix domain
197 * server socket whose abstract address is based off its contents," followed by further communication to
198 * establish a solid, safe session with an allowed opposing application. Therefore:
199 *
200 * ### Error conditions ###
201 * This ctor, on failure, throws an exception `flow::error::Runtime_error`. The `Error_code`
202 * may be accessed via `.code()` (also `.code().message()`, `.what()`) of the exception object. #Error_code
203 * generated: those documented for session::Server_session ctor; as of this writing:
204 * - interprocess-mutex-related errors (probably from boost.interprocess) w/r/t writing the CNS (PID file);
205 * - file-related system errors w/r/t writing the CNS (PID file) (see `Session_server` docs for background);
206 * - errors emitted by transport::Native_socket_stream_acceptor ctor (see that ctor's doc header; but note
207 * that they comprise name-too-long and name-conflict errors which ipc::session specifically exists to
208 * avoid).
209 *
210 * @note To comport with capnp-RPC/KJ style code flow, this API does not have a standard Flow-style
211 * optional `Error_code*` out-arg through which to communicate success/errors instead of exceptions if desired;
212 * it shall always fire an exception on error, otherwise succeed.
213 *
214 * @param logger_ptr
215 * Logger to use for logging subsequently. (You may use null to forego this completely.)
216 * See lifetime warning above.
217 * @param kj_io
218 * A KJ event loop context.
219 * @param srv_app_ref
220 * Properties of this server application. Among other things this lists identification info about
221 * which opposing applications are allowed to speak with us.
222 * See lifetime warning above.
223 * @param cli_app_master_set_ref
224 * The set of all known `Client_app`s. Technically, from our PoV, it need only list the `Client_app`s
225 * whose names are in `srv_app_ref.m_allowed_client_apps`. Refer to session::App doc header for
226 * best practices on maintaining this master list.
227 * See lifetime warning above.
228 */
229 explicit Context_server(flow::log::Logger* logger_ptr, kj::AsyncIoContext* kj_io,
230 const session::Server_app& srv_app_ref,
231 const session::Client_app::Master_set& cli_app_master_set_ref);
232
233 /**
234 * Destroys this acceptor which will stop listening in the background; as well as destroy certain cross-session
235 * (perhaps SHM-related) resources. The latter is knowingly vague but leads to the following specific and important
236 * requirement:
237 *
238 * ### Rule ###
239 * Any Server_context (connected capnp-RPC+ session object) emitted to you via an `accept()`-returned
240 * `kj::Promise` must not live past entry to this destructor. So, you must follow the RAII-style ordering:
241 * A up, B up, B down, A down (A being Context_server, B being Server_context here).
242 *
243 * ### Corollaries ###
244 * Just to help you out with some "Cs" beyond the A and B: Server_context itself contains:
245 * - A session::Session (SHM-enabled). If you constructed native SHM objects from its `.session_shm()` arena,
246 * we remind you must nullify them before letting `Session` (therefore Server_context) be destroyed.
247 * - A Session_vat_network (impl `capnp::VatNetwork`). You likely created an #Rpc_system to engage in
248 * capnp-RPC using it; therefore we remind you must destroy it before letting `VatNetwork` (therefore
249 * Server_context) be destroyed.
250 */
252
253 // Methods.
254
255 /**
256 * Asynchronously waits for an opposing Client_context to request capnp-RPC session, returning a KJ `Promise`
257 * that is fulfilled on successful establishment of a Server_context (in PEER state); or rejected with
258 * a `kj::Exception` indicating what went wrong. Essentially this convenience operation has two main aspects:
259 * - It bundles together the acceptance of a Flow-IPC session::Session and the creation of a `capnp::VatNetwork`
260 * (similar to a vanilla `capnp::TwoPartyVatNetwork` but zero-copy-enabled using Flow-IPC SHM resources from
261 * said `Session`). So: one async op; and then you can make an `RpcSystem` and go capnp-RPC-ing happily; and
262 * (optionally) make use of certain Flow-IPC `Session` features as well (see class doc header and other accept()
263 * overload).
264 * - It expresses this semantically in a KJ/capnp-RPC-style API, integrating with a KJ event loop using
265 * promises and exceptions.
266 *
267 * @note Upon getting the Accept_result::m_context, which is a new PEER-state Server_context, you may be
268 * interested in knobs accessible through Server_context::vat_network(), which gives access to the
269 * Session_vat_network core of the resulting Server_context.
270 *
271 * @see the other accept() overload which allows access to a couple side Flow-IPC ipc::session features via
272 * additional args.
273 *
274 * Multiple accept() calls can be queued while no session-open is pending; though informally we suggest:
275 * - (either) accept() (yielding eventually a fulfilled promise), handle it until it is finished (disconnected),
276 * then:
277 * - (either) exit;
278 * - (or) rinse/repeat (issue the next accept()... etc.);
279 * - (or) accept() (yielding eventually a fulfilled promise), and once it is fulfilled:
280 * - issue the next accept(), with the same handler `.then()` continuation; then immediately:
281 * - kick off the handling of this new session as part of the same event loop, until it disconnects.
282 *
283 * In the latter case you will have set-up a multi-concurrent-capnp-RPC-session event loop. In the other case(s),
284 * you will have set-up a single-capnp-RPC-session-at-a-time event loop.
285 *
286 * ### Multiple opposing applications ###
287 * To those fluent in Flow-IPC ipc::session the following is nothing new. For others we remind:
288 * It is possible (via the `session::App`-related args given to `*this` ctor) to have 2+ different applications
289 * (e.g., `/bin/x` is different app from `/bin/y`) establish sessions with a `*this`. It is likely, in that
290 * case, that what you will do strongly depends on which app connected w/r/t a given resulting Server_context.
291 * To identify the application use `S.session()->client_app()->m_name`, where `S` is the Server_context;
292 * and `m_name` is session::App::m_name identifying the `Client_app`. At that point one might bootstrap using
293 * this capnp-RPC interface or that one, depending on which app is opposing us.
294 *
295 * ### Error conditions ###
296 * Mechanically, an error is indicated via the returned `kj::Promise` being rejected with `kj::Exception`.
297 * KJ `Exception`s do not hold #Error_code values, but `.getDescription()` will contain its string representation
298 * and its `.message()` plus context info. Semantically:
299 *
300 * - That no opposing app connected to us in some amount of time is not considered an error.
301 * Lack of interest is a-OK!
302 * - An error means that a connection attempt *did* begin, but that it could not complete for some reason.
303 *
304 * So now the key question: what are you supposed to about it?
305 * - Informally our recommendation is, at least in a generally highly-available daemon situation:
306 * Log about it; then issue a replacement accept() (try again). Reason: Assuming you've set up your
307 * safety story properly -- the session::App info is all accurate and consistent, etc. -- this usually
308 * means something like an unexpected disconnect in the middle of the fairly hefty ipc::Session
309 * hand-shaking. Less likely is the system being out of some resource such as reaching a SHM kernel object
310 * limit or something. Since the likeliest reason is recoverable, and could happen when generally
311 * things are fine, it is best to assume that and continue as normal.
312 * - But! Ensure any such errors are carefully investigated (e.g., trigger alerts humans must look at).
313 * - Mechanically the easiest way too heed this is: `.then(F, E)`, where `F()` the usual success handler --
314 * while `E` is the error handler; which would in this case log but not re-propagate the exception as
315 * the default does (blowing up your event loop).
316 *
317 * However you are also free to treat it as a fatal loop-ending error by letting the `kj::Exception` propagate
318 * as normal.
319 *
320 * On failure, #Error_code generated and loaded (via `.getDescription()` only) into the `kj::Exception`:
321 * - those generated by session::Session_server::async_accept() (see doc header);
322 * - those emitted at the very start of the internal protocol, once said session is established
323 * (as of this writing, in practice, by transport::Native_socket_stream::async_receive_native_handle();
324 * see its doc header);
325 * - those generated by Session_vat_network ctor (unlikely).
326 *
327 * @param enable_hndl_transport
328 * Identical to Client_context ctor arg of same name but from our side.
329 * @return `kj::Promise` fulfilled on (and with) successful Server_context creation; rejected if a connection
330 * was initiated, but something went wrong with it. The Accept_result shall contain at least
331 * a non-null Server_context (which in turns contains a `capnp::VatNetwork` with which to make an
332 * #Rpc_system); plus any init-channels requested by other side. See other accept() regarding
333 * us-requested init-channels; see Client_context::sync_connect() regarding them-requested init-channels.
334 * Destroying `*this` shall auto-cancel this `Promise` (thus free any continuation lambdas of yours, etc.).
335 */
336 kj::Promise<Accept_result> accept(bool enable_hndl_transport = true);
337
338 /**
339 * Identical to other accept() except provides access to the server-side-requested-init-channels feature.
340 *
341 * All notes for other accept() apply to us. In addition:
342 *
343 * With this or that accept(), Flow-IPC may establish additional `Channel`s outside of the central capnp-RPC
344 * conversation -- but only ones requested in the opposing Client_context::sync_connect() call via
345 * `init_channels_by_cli_req_pre_sized` arg. These shall be placed in the returned promise's fulfilled-object
346 * Accept_result::m_init_channels_by_cli_req (or it shall be left empty if none requested).
347 *
348 * However: only with *this* accept() shall Flow-IPC establish `Channel`s requested by *this* side.
349 * This works as follows:
350 * - Asynchronously, in an unspecified thread that is not the calling thread of this method,
351 * during the session-establishment process, Flow-IPC shall call your function
352 * `n_init_channels_by_srv_req_func()`.
353 * - It will give it 2 basic pieces of info that might be useful in making the decision.
354 * - It shall expect the function to return the # of init-channels we are requesting.
355 * - Then it will do so (and fulfill the `Promise` as normal).
356 *
357 * @note Attention: We reiterate: n_init_channels_by_srv_req_func() is called from an unspecified thread,
358 * not the KJ-event-loop; while the rest of asynchronous action happens in the KJ event loop.
359 * This might be surprising, so just please be aware.
360 *
361 * @tparam N_init_channels_by_srv_req_func
362 * Function type matching signature
363 * `size_t F(const session::Client_app& app, size_t n_init_channels_by_cli_req)`, where
364 * `app` identifies the opposing application; `n_init_channels_by_cli_req` equals the forthcoming
365 * `Accept_result::m_init_channels_by_cli_req`'s `.size()`.
366 * @param enable_hndl_transport
367 * See other accept().
368 * @param n_init_channels_by_srv_req_func
369 * See `N_init_channels_by_srv_req_func`. Reminder: invoked from unspecified thread that is *not*
370 * belonging to the accept() a/k/a `*kj_io` given to ctor. So if you plan on doing something fancy
371 * watch out for synchronization of data.
372 * @return See other accept(); additionally with this form the resulting Accept_result shall also feature
373 * any init-channels requested by this side (via `n_init_channels_by_srv_req_func()` returning non-zero).
374 */
375 template<typename N_init_channels_by_srv_req_func>
376 kj::Promise<Accept_result> accept(bool enable_hndl_transport,
377 N_init_channels_by_srv_req_func&& n_init_channels_by_srv_req_func);
378
379 /**
380 * Identical to accept() overload 2 but allows the potential capnp-RPC session to disable zero-copy transport.
381 * That is, if it is so decided, underlying messages are sent in the mode compatible with vanilla capnp-RPC.
382 * In order for this to occur, some knobs must be set on each side: on this side, in this call;
383 * and on the opposing side.
384 *
385 * Starting with the former:
386 * - Asynchronously, in the KJ event loop thread, during the session-establishment process,
387 * Flow-IPC shall call your function `transport_method_func()`.
388 * - It will give it 1 basic piece of info that might be useful in making the decision.
389 * - It shall expect the function to return `true` to, indeed, disable zero-copy transport; or `false`
390 * to proceed with zero-copy (as the other accept() overloads would).
391 * - Then it will do so (and fulfill the `Promise` as normal).
392 *
393 * Assuming `transport_method_func` *does* return `true`, some knobs must be set appropriately at the opposing
394 * process. To wit:
395 * - If using Ez_rpc_client: use ctor arg `sans_shm_transport = true`.
396 * - If using Client_context: use Client_context::sync_connect_sans_shm_transport().
397 * - If using Session_vat_network: use the "special mode" ctor arg (see Session_vat_network ctor doc headers).
398 * - Or use `capnp::VatNetwork` or `capnp::EzRpcClient`.
399 *
400 * @note Attention: We reiterate: `n_init_channels_by_srv_req_func()` is called from an unspecified thread,
401 * not the KJ-event-loop; while the rest of asynchronous action (including `transport_method_func()`)
402 * happens in the KJ event loop. This might be surprising, so just please be aware.
403 *
404 * @tparam Transport_method_func
405 * Function type matching signature
406 * `bool F(const session::Client_app& app)`, where `app` identifies the opposing application.
407 * @tparam N_init_channels_by_srv_req_func
408 * See accept() overload 2.
409 * @param enable_hndl_transport
410 * See accept() overload 2.
411 * @param n_init_channels_by_srv_req_func
412 * See accept() overload 2.
413 * @param transport_method_func
414 * See above.
415 * @return See accept() overload 2.
416 *
417 * @todo For the rpc::Context_server::accept() advanced overload(s) that take an
418 * `n_init_channels_by_srv_req_func` arg, it would be a nice improvement to the contract to
419 * guarantee issuing the call to that user-function from the KJ-event-loop thread, like the rest of
420 * the async action. (E.g., the following async parts execute in the KJ style: promise fulfillment;
421 * `transport_method_func()` call if applicable.) This API change would not be
422 * a breaking change. The reason it is not already done is that the Flow-IPC
423 * capnp-RPC-support layer treats the non-RPC-aware ipc::session layer (normally used directly by user but not here)
424 * as a black box; and the session::Session_server::async_accept() contract -- in this particular detail --
425 * did not need to be KJ-friendly (or even KJ-aware) in its design, so it was not. (capnp-RPC support did not
426 * exist at the time.) So the way session::Session_server invokes `n_init_channels_by_srv_req_func()` is
427 * inverted versus what the KJ flow needs. To implement this improvement, session::Session_server would need an
428 * added async hook for the give-me-the-desired-channel-count step, and Context_server::accept() impl
429 * would make use of it.
430 */
431 template<typename N_init_channels_by_srv_req_func, typename Transport_method_func>
432 kj::Promise<Accept_result> accept(bool enable_hndl_transport,
433 N_init_channels_by_srv_req_func&& n_init_channels_by_srv_req_func,
434 Transport_method_func&& transport_method_func);
435
436 /**
437 * Returns pointer to #Session_server_obj. The #Session_server_obj remains valid, if and only if `*this` exists.
438 *
439 * @see class doc header for overview of which features accessible through this #Session_server_obj are available
440 * (and which are not).
441 *
442 * @return See above.
443 */
445
446 /**
447 * The value, if not zero, to which each Server_context produced by accept() shall have its
448 * `vat_network()->streaming_flow_window_ki()` set, before the accept() promise is fulfilled with it; zero
449 * means each such Session_vat_network is left at its own default
450 * (Session_vat_network_base::S_STREAMING_FLOW_WINDOW_KI). The latter (zero) is the initial value.
451 *
452 * @see Session_vat_network::streaming_flow_window_ki() for what the knob means and when to bother with it.
453 *
454 * @see the mutator overload, which can be used to change this value.
455 *
456 * @return See above.
457 */
459
460 /**
461 * Sets the value as returned by `streaming_flow_window_default_ki()` accessor. See its doc header.
462 *
463 * It applies to `Server_context`s produced by accept() promises fulfilled after this call; any already-produced
464 * ones are unaffected (though one can always set their `vat_network()->streaming_flow_window_ki()` directly).
465 * It has no effect on `Server_context`s operating in non-zero-copy mode (see `transport_method_func` in accept()).
466 *
467 * @param limit_ki_or_0
468 * The new value. Zero means: do not touch each new Session_vat_network's default.
469 */
470 void streaming_flow_window_default_ki(size_t limit_ki_or_0);
471
472 /**
473 * Implements `kj::TaskSet::ErrorHandler` API. In our case logs a WARNING. Do not call directly (as a user).
474 *
475 * @param exc
476 * See `kj::TaskSet`.
477 */
478 void taskFailed(kj::Exception&& exc) override;
479
480private:
481 // Types.
482
483 /**
484 * Short-hand for copyable handle to Accept_result; needed in particular when a relevant `kj::Promise` must
485 * be `.fork()`ed (Accept_result is not copyable, only movable).
486 */
487 using Accept_result_ptr = boost::shared_ptr<Accept_result>;
488
489 // Data.
490
491 /// See streaming_flow_window_default_ki().
493
494 /// KJ event loop context.
495 kj::AsyncIoContext* const m_kj_io;
496
497 /// Session in NULL state (until sync_connect() succeeds) or PEER state (subsequently).
499
500 /**
501 * Promises/promise chains to auto-cancel on destruction. Already fulfilled/rejected ones apparently self-eject
502 * or something (we need not erase them, only `.add()` them).
503 */
504 kj::TaskSet m_kj_tasks;
505}; // class Context_server
506
507// Template implementations.
508
509template<typename Session_server_t>
510Context_server<Session_server_t>::Context_server(flow::log::Logger* logger_ptr, kj::AsyncIoContext* kj_io,
511 const session::Server_app& srv_app_ref,
512 const session::Client_app::Master_set& cli_app_master_set_ref) :
513 flow::log::Log_context(logger_ptr, Log_component::S_RPC),
514 m_streaming_flow_window_default_ki(0),
515 m_kj_io(kj_io),
516 m_session_server(get_logger(), srv_app_ref, cli_app_master_set_ref,
517 // Let it throw on catastrophic error! E.g., writing CNS (PID file); dealing with IPC-mutex.
518 nullptr),
519 m_kj_tasks(*this)
520{
521 FLOW_LOG_INFO("rpc::Ctx_server [" << *this << "]: "
522 "Created. ipc::session::Session_server [" << m_session_server << "] listening.");
523}
524
525template<typename Session_server_t>
527{
528 FLOW_LOG_INFO("rpc::Ctx_server [" << *this << "]: "
529 "Shutting down. ipc::session::Session_server shall shut down. "
530 "Note that if any previously spawned `Server_context`s still live, there may be trouble.");
531}
532
533template<typename Session_server_t>
534kj::Promise<typename Context_server<Session_server_t>::Accept_result>
536{
537 return accept(enable_hndl_transport, [](auto&&...) -> size_t { return 0; });
538}
539
540/// @cond
541// -^- Doxygen, please ignore the following. Doxygen 1.9.4 doesn't recognize this/gives weird error. @todo Fix.
542
543template<typename Session_server_t>
544template<typename N_init_channels_by_srv_req_func>
545kj::Promise<typename Context_server<Session_server_t>::Accept_result>
546 Context_server<Session_server_t>::accept(bool enable_hndl_transport,
547 N_init_channels_by_srv_req_func&& n_init_channels_by_srv_req_func)
548{
549 return accept(enable_hndl_transport, std::move(n_init_channels_by_srv_req_func),
550 [](auto&&...) { return false; }); // Enable zero-copy, period.
551}
552
553template<typename Session_server_t>
554template<typename N_init_channels_by_srv_req_func, typename Transport_method_func>
555kj::Promise<typename Context_server<Session_server_t>::Accept_result>
556 Context_server<Session_server_t>::accept(bool enable_hndl_transport,
557 N_init_channels_by_srv_req_func&& n_init_channels_by_srv_req_func,
558 Transport_method_func&& transport_method_func)
559{
561 using util::Blob_mutable;
562 using flow::util::ostream_op_string;
563 using kj::newPromiseAndFulfiller;
564 using kj::newPromiseAndCrossThreadFulfiller;
565 using kj::heapString;
566 using Kj_exception = kj::Exception;
567 using boost::make_shared;
568 using boost::shared_ptr;
569 using Channels = decltype(Accept_result::m_init_channels_by_cli_req);
570
571 /* It's like the user-desired Accept_result, but before we can get the Server_context we must get its constituent
572 * Server_session -- and incidentally the two `Channels`es that'll be in Accept_result are a part of that process. */
573 struct Session_accept_result
574 {
575 typename Session_server_obj::Server_session_obj m_target_session;
576 Channels m_init_channels_by_srv_req;
577 /* .front() is reserved for the capnp-RPC transport-handle exchange (see below). We will get it out of there
578 * before giving them Accept_result with the rest of these (if any). */
579 Channels m_init_channels_by_cli_req;
580 };
581
582 /* Bundles the objects involved in receiving the capnp-RPC transport handle over the client's first
583 * init-channel (see continuation below); kept alive via shared_ptr through the async op + continuation. */
584 struct Transport_hndl_receive
585 {
586 typename Channels::value_type::Async_io_obj m_chan;
587 Native_handle m_hndl;
588 uint8_t m_protocol_dummy_payload;
589
590 /* If the continuation below never consumed m_hndl (canceled; or threw first), it is ours to close.
591 * m_chan must die first: its thread is what potentially writes m_hndl. */
592 ~Transport_hndl_receive() { m_chan = decltype(m_chan){}; m_hndl.close(); }
593 };
594
595 // We are in thread U.
596
597 /* Overview (no-error path):
598 * -# Thread U: Kick off Session_server::async_accept(F) and return (a promise for an Accept_result),
599 * upon creating (and keeping-alive via shared_ptr) the target objects it will set on success --
600 * a Server_session and any init-channels (including one necessary for our capnp-RPC purposes).
601 * -# Thread W (once an async-accept actually comes in later): Our handler F() is invoked.
602 * We cannot keep working in thread W, as can't mess with most KJ stuff in a thread differing from thread U.
603 * We don't much want to either; it is even informally recommended to typically off-load work from any
604 * thread W like Session_server's. So we use a cross-thread fulfiller to signal thread U's event loop,
605 * where we've prepared a promise (thread_w_to_u_paf.promise) whose continuation will finish the
606 * asynchronous job of this method.
607 * -# Thread U: Got signalled, so continue: We have the Server_session and any init-channels filled out.
608 * Per protocol the client now sends the capnp-RPC transport handle over init-channel 0; so kick off
609 * an async-receive of that handle. Its completion handler fires in a channel-internal thread; so
610 * bridge back to thread U via another cross-thread fulfiller (same pattern as step 2).
611 * - Could we simply assume that, upon successful asynchronous session-accept, the native-handle would
612 * arrive quickly (hence we could use a regular promise+future -- making the code shorter and basically
613 * synchronous in this step)? Yes, if we trust the opposing side to be well-behaved... but no, in that
614 * while (as explained elsewhere) we do trust their being non-hostile and even non-buggy (Flow-IPC-wise),
615 * they could still get stuck for some reason, maybe not Flow-IPC-related. So: it's better not to assume
616 * the async_receive_native_handle() will be instant.
617 * -# Thread U: Got the transport handle; destroy the by-now-purposeless channel; bundle stuff into a new
618 * Server_context (which will construct the Vat_network from the Server_session and that handle; and own
619 * those from now on); then slightly bundle *that* guy and remaining init-channels into a new
620 * Accept_result (just a simple struct).
621 * -# Thread U: Fulfill the promise from step 1 by loading it with the Accept_result.
622 *
623 * The error path is similar enough; just steps 3-5 detect the problem (bad Error_code from session-accept or
624 * handle-receive; or OK Error_code, but Server_context fails to be created, presumably Vat_network setup being
625 * the culprit) and reject instead of fulfilling, through that same fulfiller. */
626
627 /* The promise for the final result. Technicality: at the end, we'll need to .fork() it so as to both return
628 * it and add a branch to m_kj_tasks (so that a rejection the user never observes is at least logged via
629 * taskFailed()); this makes it required that the type is copyable, not just movable; so throw a shared_ptr<>
630 * around it (hence Accept_result_ptr, not Accept_result). */
631 auto accept_result_paf = newPromiseAndFulfiller<Accept_result_ptr>();
632
633 /* The promise -- which can/will be fulfilled from thread W -- that kicks off the promise chain.
634 * We won't use rejection or anything fancy; just load it with the Error_code (even if it implies error),
635 * while the rest of the results of Session_server::async_accept() will be set by it from thread W, while
636 * we keep those target objects alive (&session_accept_result->...). */
637 auto thread_w_to_u_paf = newPromiseAndCrossThreadFulfiller<Error_code>();
638 auto session_accept_result = make_shared<Session_accept_result>();
639
640 m_session_server.async_accept(&session_accept_result->m_target_session,
641 &session_accept_result->m_init_channels_by_srv_req,
642 nullptr, // As advertised, cut out the metadata feature.
643 &session_accept_result->m_init_channels_by_cli_req,
644
645 /* We've cut out the mdt-passing feature in this capnp-RPC-focused API, so
646 * ignore that arg and pass them the remaining 2 only, to match our contract. */
647 [n_init_channels_by_srv_req_func = std::move(n_init_channels_by_srv_req_func)]
648 (const Client_app& app, size_t n_init_channels_by_cli_req, auto&&) mutable -> size_t
649 { return n_init_channels_by_srv_req_func(app, n_init_channels_by_cli_req); },
650
651 [](auto&&...) {}, // As advertised, cut out the metadata feature.
652
653 // The completion handler (*will* execute barring a crash)....
654 [this,
655 // Keep it alive here, not just in accept_result_paf continuation below!
656 session_accept_result,
657 // Something uses function<> inside; captures must be copyable; wrap in shared_ptr.
658 thread_w_to_u_fulfiller = make_shared<decltype(thread_w_to_u_paf.fulfiller)>
659 (std::move(thread_w_to_u_paf.fulfiller))]
660 (const Error_code& err_code)
661 {
662 // We are in thread W (of Session_server).
663
664 if (err_code == session::error::Code::S_OBJECT_SHUTDOWN_ABORTED_COMPLETION_HANDLER)
665 {
666 return; // Stuff (namely Context_server) is shutting down. GTFO. (Relevant promises will be canceled in dtor.)
667 }
668 // else
669
670 FLOW_LOG_TRACE("rpc::Ctx_server [" << *this << "]: "
671 "In Session_server::async_accept() completion handler got result "
672 "[" << err_code <<"] [" << err_code.message() << "]; pinging our promise continuation handler "
673 "back in the original thread's KJ event loop with this result.");
674
675 (*thread_w_to_u_fulfiller)->fulfill(Error_code{err_code});
676 });
677
678 // Back here in thread U, prep to be signalled by thread W.
679 m_kj_tasks.add // (Also save in m_kj_tasks for auto-canceling in dtor, if we don't fulfill/reject by then.)
680 (thread_w_to_u_paf.promise
681 .then([this, enable_hndl_transport,
682 transport_method_func = std::move(transport_method_func),
683 session_accept_result = std::move(session_accept_result), // Keep it alive here too!
684 accept_result_fulfiller = std::move(accept_result_paf.fulfiller)]
685 (const Error_code& err_code) // Get the Error_code our thread-W handler sent to us.
686 mutable
687 {
688 // We are in thread U (executed from within m_kj_io event loop, later).
689
690 if (err_code)
691 {
692 FLOW_LOG_WARNING("rpc::Ctx_server [" << *this << "]: "
693 "In async-accept promise continuation reacting to session-async-accept which resulted in "
694 "failure ([" << err_code <<"] [" << err_code.message() << "]). Rejecting promise to "
695 "inform our original caller.");
696
697 // They require a Kj_exception which doesn't have a slot for a general Error_code; give 'em a string though.
698 accept_result_fulfiller->reject
699 (Kj_exception{Kj_exception::Type::FAILED, __FILE__, __LINE__,
700 heapString(ostream_op_string("rpc::Ctx_server [", *this,
701 "]: ipc::session::Session::async_accept() failed with code ([",
702 err_code, "] [", err_code.message(), "]).").c_str())});
703 return;
704 }
705 // else: async_accept() succeeded. Finalize any out-args and the main guy: make Server_context.
706
707 auto& init_chans_cli = session_accept_result->m_init_channels_by_cli_req;
708 if (init_chans_cli.empty())
709 {
710 FLOW_LOG_WARNING("rpc::Ctx_server [" << *this << "]: "
711 "In async-accept promise continuation reacting to session-async-accept which succeeded; "
712 "but opposing peer did not request even one init-channel, which a properly functioning "
713 "Client_context or equivalent must do by our protocol, so we can use the first such "
714 "channel to feed into Vat_network. Rejecting promise to inform our original caller.");
715 accept_result_fulfiller->reject
716 (Kj_exception{Kj_exception::Type::FAILED, __FILE__, __LINE__,
717 heapString(ostream_op_string("rpc::Ctx_server [", *this,
718 "]: ipc::session::Session::async_accept() succeeded; but opposing "
719 "peer did not request even one init-channel, which a properly "
720 "functioning Client_context or equivalent must do by our "
721 "protocol, so we can use the first such channel to feed "
722 "into Vat_network.").c_str())});
723 return;
724 } // if (init_chans_cli.empty())
725
726 auto chan_for_rpc = std::move(init_chans_cli.front());
727 // Boink the now-empty Channel slot 0. Anything remaining = any init-channels requested by opposing client user.
728 init_chans_cli.erase(init_chans_cli.begin(), ++init_chans_cli.begin());
729
730 /* Per our protocol with the opposing Client_context (see its class doc header @todo regarding
731 * Protocol_negotiator), chan_for_rpc exists for one purpose: the client sends -- over it, to us -- a single
732 * native handle: one end of a pre-connected stream-socket pair, over which the two Session_vat_network peer
733 * objects (theirs and ours) shall subsequently transmit their (small) messages. (See
734 * Client_context::sync_connect_impl() for the counterpart logic and further discussion; e.g., why not simply
735 * use chan_for_rpc's own low-level transport.) So: async-receive that handle; destroy the channel (its work
736 * is then done); and use the handle to cook-up the Server_context (which feeds it to its Session_vat_network).
737 *
738 * Mechanically: we receive via the async-I/O API (less fuss than the sync_io-pattern API for a one-off op);
739 * its completion handler fires in a channel-internal thread; so -- exactly as with the session-accept above --
740 * we bridge back to thread U via a cross-thread fulfiller and finish the job in the continuation below. */
741
742 const bool sans_shm_transport = transport_method_func(*(session_accept_result->m_target_session.client_app()));
743
744 auto rcv = make_shared<Transport_hndl_receive>();
745 rcv->m_chan = chan_for_rpc.async_io_obj();
746
747 auto hndl_w_to_u_paf = newPromiseAndCrossThreadFulfiller<Error_code>();
748 rcv->m_chan.hndl_rcv()->async_receive_native_handle
749 (&rcv->m_hndl, Blob_mutable{&rcv->m_protocol_dummy_payload,
750 sizeof(Transport_hndl_receive::m_protocol_dummy_payload)},
751 // (Something uses function<> inside; captures must be copyable; wrap in shared_ptr.)
752 [hndl_w_to_u_fulfiller = make_shared<decltype(hndl_w_to_u_paf.fulfiller)>(std::move(hndl_w_to_u_paf.fulfiller))]
753 (const Error_code& err_code, size_t)
754 {
755 // We are in a thread W (of the async-I/O channel).
756 (*hndl_w_to_u_fulfiller)->fulfill(Error_code{err_code});
757 });
758
759 m_kj_tasks.add
760 (hndl_w_to_u_paf.promise
761 .then([this, enable_hndl_transport, sans_shm_transport,
762 rcv, // Keep the async-I/O channel and receive-target objects alive!
763 session_accept_result = std::move(session_accept_result), // Keep alive here too.
764 accept_result_fulfiller = std::move(accept_result_fulfiller)]
765 (const Error_code& err_code) mutable
766 {
767 // We are in thread U (the client's handle-bearing message arrived; or the channel got hosed).
768
769 if (err_code || rcv->m_hndl.null())
770 {
771 FLOW_LOG_WARNING("rpc::Ctx_server [" << *this << "]: "
772 "In async-accept promise continuation: session-async-accept had succeeded; but receiving "
773 "the capnp-RPC transport handle over the client's first init-channel then failed "
774 "(code [" << err_code << "] [" << err_code.message() << "]; "
775 "null handle with no error would mean opposing peer violated protocol). "
776 "Rejecting promise to inform our original caller.");
777 accept_result_fulfiller->reject
778 (Kj_exception{Kj_exception::Type::FAILED, __FILE__, __LINE__,
779 heapString(ostream_op_string("rpc::Ctx_server [", *this,
780 "]: session-async-accept had succeeded; but receiving the "
781 "capnp-RPC transport handle over the client's first "
782 "init-channel then failed (code [",
783 err_code, "] [",
784 err_code.message(),
785 "]; null handle with no error would mean "
786 "opposing peer violated protocol).").c_str())});
787 return;
788 }
789 // else: got the transport handle. The channel's work is done; destroy it.
790 rcv->m_chan = {};
791
792 // Everything is ready. Make Server_context. Its contract says it could throw Kj_exception.
793
794 auto& init_chans_cli = session_accept_result->m_init_channels_by_cli_req;
795 auto& session = session_accept_result->m_target_session;
796
797 // Use the Server_context_dtl attorney to access the otherwise internal Server_context ctor.
798 typename Server_context_obj::Ptr srv_context;
799 try
800 {
801 srv_context = Server_context_dtl::ct_base<Server_context_obj>(get_logger(), m_kj_io,
802 std::move(session),
803 std::move(rcv->m_hndl),
804 enable_hndl_transport,
805 sans_shm_transport);
806 }
807 catch (const Kj_exception& exc)
808 {
809 FLOW_LOG_WARNING("rpc::Ctx_server [" << *this << "]: "
810 "In async-accept promise continuation reacting to session-async-accept which succeeded; "
811 "zero-copy-enabled? = [" << (!sans_shm_transport) << "]; "
812 "however Server_context ctor (presumably the Session_vat_network part of it) "
813 "threw exception [" << exc.getDescription().cStr() << "]. Rejecting promise with "
814 "that exception to inform our original caller.");
815 accept_result_fulfiller->reject(Kj_exception{exc});
816 return;
817 }
818 // Got here: noice!
819
820 if (m_streaming_flow_window_default_ki != 0)
821 {
822 // (No effect in non-zero-copy mode but harmless.)
823 srv_context->vat_network()->streaming_flow_window_ki(m_streaming_flow_window_default_ki);
824 }
825 // else { It'll use its own default. Either way it can be overridden, same as what we do 2 lines up. }
826
827 FLOW_LOG_INFO("rpc::Ctx_server [" << *this << "]: "
828 "In async-accept promise continuation reacting to session-async-accept which succeeded; "
829 "zero-copy-enabled? = [" << (!sans_shm_transport) << "]; "
830 "and we created the PEER-state Server_context with all the goodies (Vat_network et al). "
831 "Returning all results via fulfiller to inform original caller.");
832
833 // Can't make_shared<X>() while aggregate-initializing X ({ a, b, c }). Perf doesn't matter => use `new`.
834 accept_result_fulfiller
835 ->fulfill(Accept_result_ptr
836 {new Accept_result
837 { std::move(srv_context),
838 std::move(session_accept_result->m_init_channels_by_srv_req),
839 std::move(init_chans_cli) }});
840 })); // m_kj_tasks.add(hndl_w_to_u_paf.then()
841 })); // m_kj_tasks.add(thread_w_to_u_paf.then()
842
843 /* We want to return the final promise but also keep a branch of it in m_kj_tasks, so that if it is rejected,
844 * and the user is not listening (dropped their branch), taskFailed() still logs it. (Cancellation-on-dtor is
845 * already provided by the upstream chain having been added to m_kj_tasks above; this is not about that.) */
846
847 auto accept_result_promise_fork = accept_result_paf.promise.fork();
848 m_kj_tasks.add(accept_result_promise_fork.addBranch().ignoreResult());
849
850 /* As explained earlier, due to the .fork() technicality we had to use Accept_result_ptr as the promise target,
851 * but there's no need to impose that on the user: give them a plain Accept_result by move-ctor. Perf = meh. */
852 return accept_result_promise_fork.addBranch().then([](Accept_result_ptr&& accept_result_ptr)
853 {
854 return Accept_result{std::move(*accept_result_ptr)};
855 });
856} // Context_server::accept(3)
857
858// -v- Doxygen, please stop ignoring.
859/// @endcond
860
861template<typename Session_server_t>
862const typename Context_server<Session_server_t>::Session_server_obj&
864{
865 return m_session_server;
866}
867
868template<typename Session_server_t>
870{
871 return m_streaming_flow_window_default_ki;
872}
873
874template<typename Session_server_t>
876{
877 FLOW_LOG_TRACE("rpc::Ctx_server [" << *this << "]: User setting streaming flow control window default "
878 "for subsequently accepted sessions to [" << limit_ki_or_0 << "Ki] (0 = leave each at its default); "
879 "replacing current setting [" << m_streaming_flow_window_default_ki << "Ki].");
880 m_streaming_flow_window_default_ki = limit_ki_or_0;
881}
882
883template<typename Session_server_t>
885{
886 FLOW_LOG_WARNING("rpc::Ctx_server [" << *this << "]: "
887 "A promise was rejected (presumably an error such as accept failure) -- either an internal "
888 "one, or one the user did not catch -- so we will just log it: "
889 "[" << exc.getDescription().cStr() << "].");
890}
891
892template<typename Session_server_t>
893std::ostream& operator<<(std::ostream& os, const Context_server<Session_server_t>& val)
894{
895 return os << "[session_srv[" << val.session_server() << "]]@" << &val;
896}
897
898} // namespace ipc::transport::struc::shm::rpc
An object of this type facilitates the establishment, in server style, of zero-copy-enabled capnp-RPC...
boost::shared_ptr< Accept_result > Accept_result_ptr
Short-hand for copyable handle to Accept_result; needed in particular when a relevant kj::Promise mus...
kj::Promise< Accept_result > accept(bool enable_hndl_transport, N_init_channels_by_srv_req_func &&n_init_channels_by_srv_req_func, Transport_method_func &&transport_method_func)
Identical to accept() overload 2 but allows the potential capnp-RPC session to disable zero-copy tran...
size_t streaming_flow_window_default_ki() const
The value, if not zero, to which each Server_context produced by accept() shall have its vat_network(...
size_t m_streaming_flow_window_default_ki
See streaming_flow_window_default_ki().
kj::Promise< Accept_result > accept(bool enable_hndl_transport, N_init_channels_by_srv_req_func &&n_init_channels_by_srv_req_func)
Identical to other accept() except provides access to the server-side-requested-init-channels feature...
void taskFailed(kj::Exception &&exc) override
Implements kj::TaskSet::ErrorHandler API.
kj::Promise< Accept_result > accept(bool enable_hndl_transport=true)
Asynchronously waits for an opposing Client_context to request capnp-RPC session, returning a KJ Prom...
kj::TaskSet m_kj_tasks
Promises/promise chains to auto-cancel on destruction.
void streaming_flow_window_default_ki(size_t limit_ki_or_0)
Sets the value as returned by streaming_flow_window_default_ki() accessor.
Session_server_obj m_session_server
Session in NULL state (until sync_connect() succeeds) or PEER state (subsequently).
const Session_server_obj & session_server() const
Returns pointer to Session_server_obj.
Session_server_t Session_server_obj
Convenience alias for template arg.
Context_server(flow::log::Logger *logger_ptr, kj::AsyncIoContext *kj_io, const session::Server_app &srv_app_ref, const session::Client_app::Master_set &cli_app_master_set_ref)
Constructs us: immediately begins listening for incoming session-open attempts from allowed client ap...
~Context_server()
Destroys this acceptor which will stop listening in the background; as well as destroy certain cross-...
kj::AsyncIoContext *const m_kj_io
KJ event loop context.
The mirror of an opposing PEER-state Client_context, as generated via successful Context_server::acce...
boost::movelib::unique_ptr< Server_context > Ptr
Movable smart-pointer handle to a *this.
Provides Flow-IPC's integration with Cap'n Proto's Remote Procedure Call (RPC) layer,...
std::ostream & operator<<(std::ostream &os, const Capnp_message_builder< Shm_arena > &val)
Prints string representation of the given Capnp_message_builder to the given ostream.
boost::asio::mutable_buffer Blob_mutable
Short-hand for a mutable blob somewhere in memory, stored as exactly a void* and a size_t.
Definition: util_fwd.hpp:137
Log_component
The flow::log::Component payload enumeration containing various log components used by Flow-IPC inter...
Definition: common.hpp:338
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
Definition: common.hpp:313
An App that is used as a client in at least one client-server IPC split.
Definition: app.hpp:220
boost::unordered_map< std::string, Client_app > Master_set
Type for storing master repository of all Client_apps. See App doc header for discussion.
Definition: app.hpp:227
An App that is used as a server in at least one client-server IPC split.
Definition: app.hpp:241
Bundles the results of a successful accept(); until then all elements default-cted.
Session_server_obj::Channels m_init_channels_by_cli_req
Opened init-channels, numbering as many as the opposing client requested; may well be ....
Server_context_obj::Ptr m_context
The kj::Own-like handle to established Server_context opposite the other guy's PEER-state Client_cont...
Session_server_obj::Channels m_init_channels_by_srv_req
Opened init-channels, numbering as many as you requested (see accept() doc header); may well be ....
A monolayer-thin wrapper around a native handle, a/k/a descriptor a/k/a FD.
void close() noexcept
Little utility that returns m_native_handle to the OS.