Ice 3.9
C++ API Reference
Loading...
Searching...
No Matches
OutgoingAsync.h
1// Copyright (c) ZeroC, Inc.
2
3#ifndef ICE_OUTGOING_ASYNC_H
4#define ICE_OUTGOING_ASYNC_H
5
6#include "CommunicatorF.h"
7#include "ConnectionF.h"
8#include "ConnectionIF.h"
9#include "InputStream.h"
10#include "LocalExceptions.h"
11#include "ObjectAdapterF.h"
12#include "ObserverHelper.h"
13#include "OutputStream.h"
14#include "Proxy.h"
15#include "RequestHandlerF.h"
16#include "TimerTask.h"
17
18#include <cassert>
19#include <exception>
20#include <string_view>
21
22#if defined(_MSC_VER)
23# pragma warning(push)
24# pragma warning(disable : 4250) // ... : inherits ... via dominance
25# pragma warning(disable : 4251) // class ... needs to have dll-interface to be used by clients of class ...
26#elif defined(__clang__)
27# pragma clang diagnostic push
28# pragma clang diagnostic ignored "-Wshadow-uncaptured-local"
29# pragma clang diagnostic ignored "-Wweak-vtables"
30#endif
31
32namespace IceInternal
33{
34 class OutgoingAsyncBase;
35 class RetryException;
36 class CollocatedRequestHandler;
37
38 enum AsyncStatus
39 {
40 AsyncStatusQueued = 0,
41 AsyncStatusSent = 1,
42 AsyncStatusInvokeSentCallback = 2
43 };
44
45 class ICE_API OutgoingAsyncCompletionCallback
46 {
47 public:
48 virtual ~OutgoingAsyncCompletionCallback();
49
50 protected:
51 // Returns true if handleInvokeSent handles sent callbacks.
52 virtual bool handleSent(bool done, bool alreadySent) noexcept = 0;
53
54 // Returns true if handleInvokeException handles exception callbacks.
55 virtual bool handleException(std::exception_ptr) noexcept = 0;
56
57 // Returns true if handleInvokeResponse handles response callbacks.
58 // This function can unmarshal the response and throw an exception.
59 virtual bool handleResponse(bool) = 0;
60
61 virtual void handleInvokeSent(bool, OutgoingAsyncBase*) const = 0;
62 virtual void handleInvokeException(std::exception_ptr, OutgoingAsyncBase*) const = 0;
63 virtual void handleInvokeResponse(bool, OutgoingAsyncBase*) const = 0;
64 };
65
66 //
67 // Base class for handling asynchronous invocations. This class is
68 // responsible for the handling of the output stream and the child
69 // invocation observer.
70 //
71 class ICE_API OutgoingAsyncBase : public virtual OutgoingAsyncCompletionCallback,
72 public std::enable_shared_from_this<OutgoingAsyncBase>
73 {
74 public:
75 virtual bool sent();
76 virtual bool exception(std::exception_ptr);
77 virtual bool response();
78
79 void invokeSentAsync();
80 void invokeExceptionAsync();
81 void invokeResponseAsync();
82
83 void invokeSent();
84 void invokeException();
85 void invokeResponse();
86
87 virtual void cancelable(const IceInternal::CancellationHandlerPtr&);
88 void cancel();
89
90 void
91 attachRemoteObserver(const Ice::ConnectionInfoPtr& c, const Ice::EndpointPtr& endpt, std::int32_t requestId);
92
93 void attachCollocatedObserver(const Ice::ObjectAdapterPtr& adapter, std::int32_t requestId);
94
95 Ice::OutputStream* getOs() { return &_os; }
96
97 Ice::InputStream* getIs() { return &_is; }
98
99 protected:
100 OutgoingAsyncBase(const InstancePtr&);
101
102 bool sentImpl(bool);
103 bool exceptionImpl(std::exception_ptr);
104 bool responseImpl(bool, bool);
105
106 void cancel(std::exception_ptr);
107
108 void warning(std::string_view callbackName, std::exception_ptr eptr) const;
109
110 //
111 // This virtual method is necessary for the communicator flush
112 // batch requests implementation.
113 //
114 virtual IceInternal::InvocationObserver& getObserver() { return _observer; }
115
116 const InstancePtr _instance;
117 Ice::ConnectionPtr _cachedConnection;
118 bool _sentSynchronously{false};
119 bool _doneInSent{false};
120 unsigned char _state{0};
121
122 std::mutex _m;
123 using Lock = std::lock_guard<std::mutex>;
124
125 std::exception_ptr _ex;
126 std::exception_ptr _cancellationException;
127
128 InvocationObserver _observer;
129 ObserverHelperT<Ice::Instrumentation::ChildInvocationObserver> _childObserver;
130
131 Ice::OutputStream _os;
132 Ice::InputStream _is;
133
134 CancellationHandlerPtr _cancellationHandler;
135
136 static const unsigned char OK;
137 static const unsigned char Sent;
138 };
139
140 using OutgoingAsyncBasePtr = std::shared_ptr<OutgoingAsyncBase>;
141
142 //
143 // Base class for proxy based invocations. This class handles the
144 // retry for proxy invocations. It also ensures the child observer is
145 // correctly notified of failures and makes sure the retry task is
146 // correctly canceled when the invocation completes.
147 //
148 class ICE_API ProxyOutgoingAsyncBase : public OutgoingAsyncBase, public TimerTask
149 {
150 public:
151 virtual AsyncStatus invokeRemote(const Ice::ConnectionIPtr&, bool, bool) = 0;
152 virtual AsyncStatus invokeCollocated(CollocatedRequestHandler*) = 0;
153
154 bool exception(std::exception_ptr) override;
155
156 void retryException();
157
158 // Retries the invocation; when the retry attempt fails, completes the invocation with the exception. This
159 // function never throws.
160 void retry();
161
162 void abort(std::exception_ptr);
163
164 std::shared_ptr<ProxyOutgoingAsyncBase> shared_from_this()
165 {
166 return std::static_pointer_cast<ProxyOutgoingAsyncBase>(OutgoingAsyncBase::shared_from_this());
167 }
168
169 protected:
170 ProxyOutgoingAsyncBase(Ice::ObjectPrx);
171 ~ProxyOutgoingAsyncBase() override;
172
173 void invokeImpl(bool);
174 bool sentImpl(bool);
175 bool exceptionImpl(std::exception_ptr);
176 bool responseImpl(bool, bool);
177
178 void runTimerTask() override;
179
180 const Ice::ObjectPrx _proxy;
181 RequestHandlerPtr _handler;
183
184 private:
185 int handleRetryAfterException(std::exception_ptr);
186 int checkRetryAfterException(std::exception_ptr);
187
188 int _cnt{0};
189 bool _sent{false};
190 };
191
192 using ProxyOutgoingAsyncBasePtr = std::shared_ptr<ProxyOutgoingAsyncBase>;
193
194 //
195 // Class for handling Slice operation invocations
196 //
197 class ICE_API OutgoingAsync : public ProxyOutgoingAsyncBase
198 {
199 public:
200 OutgoingAsync(Ice::ObjectPrx, bool);
201
202 void prepare(std::string_view operation, Ice::OperationMode mode, const Ice::Context& context);
203
204 bool sent() override;
205 bool response() override;
206
207 AsyncStatus invokeRemote(const Ice::ConnectionIPtr&, bool, bool) override;
208 AsyncStatus invokeCollocated(CollocatedRequestHandler*) override;
209
210 void abort(std::exception_ptr);
211 void invoke(std::string_view);
212 void invoke(
213 std::string_view,
215 std::optional<Ice::FormatType>,
216 const Ice::Context&,
217 const std::function<void(Ice::OutputStream*)>&);
218 void throwUserException();
219
220 Ice::OutputStream* startWriteParams(std::optional<Ice::FormatType> format)
221 {
222 _os.startEncapsulation(_encoding, format);
223 return &_os;
224 }
225 void endWriteParams() { _os.endEncapsulation(); }
226 void writeEmptyParams() { _os.writeEmptyEncapsulation(_encoding); }
227 void writeParamEncaps(const std::byte* encaps, std::int32_t size)
228 {
229 if (size == 0)
230 {
231 _os.writeEmptyEncapsulation(_encoding);
232 }
233 else
234 {
235 _os.writeEncapsulation(encaps, size);
236 }
237 }
238
239 protected:
240 const Ice::EncodingVersion _encoding;
241 std::function<void(const Ice::UserException&)> _userException;
242 bool _synchronous;
243 };
244
245 using OutgoingAsyncPtr = std::shared_ptr<OutgoingAsync>;
246
247 class ICE_API LambdaInvoke : public virtual OutgoingAsyncCompletionCallback
248 {
249 public:
250 LambdaInvoke(std::function<void(std::exception_ptr)> exception, std::function<void(bool)> sent)
251 : _exception(std::move(exception)),
252 _sent(std::move(sent))
253 {
254 }
255
256 protected:
257 bool handleSent(bool, bool) noexcept final;
258 bool handleException(std::exception_ptr) noexcept final;
259 bool handleResponse(bool) final;
260
261 void handleInvokeSent(bool, OutgoingAsyncBase*) const final;
262 void handleInvokeException(std::exception_ptr, OutgoingAsyncBase*) const final;
263 void handleInvokeResponse(bool, OutgoingAsyncBase*) const final;
264
265 std::function<void(std::exception_ptr)> _exception;
266 std::function<void(bool)> _sent;
267 std::function<void(bool)> _response;
268 };
269
270 template<typename R> class PromiseInvoke : public virtual OutgoingAsyncCompletionCallback
271 {
272 public:
273 [[nodiscard]] std::future<R> getFuture() { return _promise.get_future(); }
274
275 protected:
276 bool handleSent(bool, bool) noexcept override { return false; }
277
278 bool handleException(std::exception_ptr ex) noexcept final
279 {
280 _promise.set_exception(ex);
281 return false;
282 }
283
284 bool handleResponse(bool ok) final
285 {
286 _response(ok);
287 return false;
288 }
289
290 void handleInvokeSent(bool, OutgoingAsyncBase*) const final { assert(false); }
291
292 void handleInvokeException(std::exception_ptr, OutgoingAsyncBase*) const final { assert(false); }
293
294 void handleInvokeResponse(bool, OutgoingAsyncBase*) const final { assert(false); }
295
296 std::promise<R> _promise;
297 std::function<void(bool)> _response;
298 };
299
300 template<typename T> class OutgoingAsyncT : public OutgoingAsync
301 {
302 public:
303 using OutgoingAsync::OutgoingAsync;
304
305 void invoke(
306 std::string_view operation,
308 std::optional<Ice::FormatType> format,
309 const Ice::Context& ctx,
310 const std::function<void(Ice::OutputStream*)>& write,
311 std::function<void(const Ice::UserException&)> userException)
312 {
313 _read = [](Ice::InputStream* stream)
314 {
315 T v{};
316 stream->read(v);
317 return v;
318 };
319 _userException = std::move(userException);
320 OutgoingAsync::invoke(operation, mode, format, ctx, write);
321 }
322
323 void invoke(
324 std::string_view operation,
326 std::optional<Ice::FormatType> format,
327 const Ice::Context& ctx,
328 const std::function<void(Ice::OutputStream*)>& write,
329 std::function<void(const Ice::UserException&)> userException,
330 std::function<T(Ice::InputStream*)> read)
331 {
332 _read = std::move(read);
333 _userException = std::move(userException);
334 OutgoingAsync::invoke(operation, mode, format, ctx, write);
335 }
336
337 protected:
338 std::function<T(Ice::InputStream*)> _read;
339 };
340
341 template<> class OutgoingAsyncT<void> : public OutgoingAsync
342 {
343 public:
344 using OutgoingAsync::OutgoingAsync;
345
346 void invoke(
347 std::string_view operation,
349 std::optional<Ice::FormatType> format,
350 const Ice::Context& ctx,
351 const std::function<void(Ice::OutputStream*)>& write,
352 std::function<void(const Ice::UserException&)> userException)
353 {
354 _userException = std::move(userException);
355 OutgoingAsync::invoke(operation, mode, format, ctx, write);
356 }
357 };
358
359 template<typename R> class LambdaOutgoing : public OutgoingAsyncT<R>, public LambdaInvoke
360 {
361 public:
362 LambdaOutgoing(
363 Ice::ObjectPrx proxy,
364 std::function<void(R)> response,
365 std::function<void(std::exception_ptr)> ex,
366 std::function<void(bool)> sent)
367 : OutgoingAsyncT<R>(std::move(proxy), false),
368 LambdaInvoke(std::move(ex), std::move(sent))
369 {
370 _response = [this, response = std::move(response)](bool ok)
371 {
372 if (!ok)
373 {
374 this->throwUserException();
375 }
376 else if (response)
377 {
378 assert(this->_read);
379 this->_is.startEncapsulation();
380 R v = this->_read(&this->_is);
381 this->_is.endEncapsulation();
382 try
383 {
384 response(std::move(v));
385 }
386 catch (...)
387 {
388 this->warning("response", std::current_exception());
389 }
390 }
391 };
392 }
393 };
394
395 template<> class LambdaOutgoing<void> : public OutgoingAsyncT<void>, public LambdaInvoke
396 {
397 public:
398 LambdaOutgoing(
399 Ice::ObjectPrx proxy,
400 std::function<void()> response,
401 std::function<void(std::exception_ptr)> ex,
402 std::function<void(bool)> sent)
403 : OutgoingAsyncT<void>(std::move(proxy), false),
404 LambdaInvoke(std::move(ex), std::move(sent))
405 {
406 _response = [this, response = std::move(response)](bool ok)
407 {
408 if (!ok)
409 {
410 this->throwUserException();
411 }
412 else if (response)
413 {
414 if (!this->_is.b.empty())
415 {
416 this->_is.skipEmptyEncapsulation();
417 }
418 try
419 {
420 response();
421 }
422 catch (...)
423 {
424 this->warning("response", std::current_exception());
425 }
426 }
427 };
428 }
429 };
430
431 template<typename R> class PromiseOutgoing : public OutgoingAsyncT<R>, public PromiseInvoke<R>
432 {
433 public:
434 PromiseOutgoing(Ice::ObjectPrx proxy, bool sync) : OutgoingAsyncT<R>(std::move(proxy), sync)
435 {
436 this->_response = [this](bool ok)
437 {
438 if (ok)
439 {
440 assert(this->_read);
441 this->_is.startEncapsulation();
442 R v = this->_read(&this->_is);
443 this->_is.endEncapsulation();
444 this->_promise.set_value(std::move(v));
445 }
446 else
447 {
448 this->throwUserException();
449 }
450 };
451 }
452 };
453
454 template<> class PromiseOutgoing<void> : public OutgoingAsyncT<void>, public PromiseInvoke<void>
455 {
456 public:
457 PromiseOutgoing(Ice::ObjectPrx proxy, bool sync) : OutgoingAsyncT<void>(std::move(proxy), sync)
458 {
459 this->_response = [&](bool ok)
460 {
461 if (this->_is.b.empty())
462 {
463 //
464 // If there's no response (oneway, batch-oneway proxies), we just set the promise
465 // on completion without reading anything from the input stream. This is required for
466 // batch invocations.
467 //
468 this->_promise.set_value();
469 }
470 else if (ok)
471 {
472 this->_is.skipEmptyEncapsulation();
473 this->_promise.set_value();
474 }
475 else
476 {
477 this->throwUserException();
478 }
479 };
480 }
481
482 bool handleSent(bool done, bool) noexcept final
483 {
484 if (done)
485 {
486 PromiseInvoke<void>::_promise.set_value();
487 }
488 return false;
489 }
490 };
491
492 template<typename R, typename Obj, typename Fn, typename... Args>
493 [[nodiscard]] inline std::future<R> makePromiseOutgoing(bool sync, Obj obj, Fn fn, Args&&... args)
494 {
495 auto outAsync = std::make_shared<PromiseOutgoing<R>>(*obj, sync);
496 (obj->*fn)(outAsync, std::forward<Args>(args)...);
497 return outAsync->getFuture();
498 }
499
500 template<typename R, typename Re, typename E, typename S, typename Obj, typename Fn, typename... Args>
501 [[nodiscard]] inline std::function<void()> makeLambdaOutgoing(Re r, E e, S s, Obj obj, Fn fn, Args&&... args)
502 {
503 auto outAsync = std::make_shared<LambdaOutgoing<R>>(*obj, std::move(r), std::move(e), std::move(s));
504 (obj->*fn)(outAsync, std::forward<Args>(args)...);
505 return [outAsync]() { outAsync->cancel(); };
506 }
507}
508
509#if defined(_MSC_VER)
510# pragma warning(pop)
511#elif defined(__clang__)
512# pragma clang diagnostic pop
513#endif
514
515#endif
std::shared_ptr< ConnectionInfo > ConnectionInfoPtr
A shared pointer to a ConnectionInfo.
Definition ConnectionF.h:21
std::shared_ptr< ObjectAdapter > ObjectAdapterPtr
A shared pointer to an ObjectAdapter.
std::shared_ptr< Endpoint > EndpointPtr
A shared pointer to an Endpoint.
Definition EndpointF.h:20
OperationMode
Specifies if an operation is idempotent, which affects the retry behavior of the Ice client runtime.
@ Normal
A non-idempotent operation (the default).
std::shared_ptr< Connection > ConnectionPtr
A shared pointer to a Connection.
Definition ConnectionF.h:18
@ UserException
The dispatch completed with a Slice user exception.
Definition ReplyStatus.h:34
std::map< std::string, std::string, std::less<> > Context
Represents additional information carried by an Ice request.
Definition Context.h:28