async_patterns:: DispatcherBound
#include <dispatcher_bound.h>
|DispatcherBound
Summary
Thread-unsafe asynchronous types should be used from synchronized dispatchers (e.g. a single-threaded async loop). Because the dispatcher may be running code to manipulate such objects, one should not use the same objects from other unrelated threads and cause data races.
However, it may not always be possible for an entire tree of objects to live on the same async dispatcher, due to design or legacy constraints. |DispatcherBound| helps one divide classes along dispatcher boundaries.
An example:
// |Background| always lives on a background dispatcher, provided // at construction time. class Background { public: explicit Background() { // Perform some asynchronous work. The work is canceled if // |Background| is destroyed. task_.Post(async_get_default_dispatcher()); } private: void DoSomething(); // |task_| manages an async task that borrows the containing // |Background| object and is not thread safe. It must be destroyed // on the dispatcher to ensure that task cancellation is not racy. async::TaskClosureMethod<Background, &Background::DoSomething> task_{this}; }; class Owner { public: // Asynchronously constructs a |Background| object on its dispatcher. // Code in |Owner| and code in |Background| may run concurrently. // // The dispatcher will not be attached to the current thread, but will // be attached to the loop thread. This way, the |Background| object // can obtain a dispatcher from its constructor using // |async_get_default_dispatcher|. explicit Owner() : background_loop_(&kAsyncLoopConfigNoAttachToCurrentThread), background_{background_loop_.dispatcher(), std::in_place} {} private: // The async loop which will manage |Background| objects. // This will always be paired with a |DispatcherBound| object. async::Loop background_loop_; // The |DispatcherBound| which manages |Background| on its loop. // During destruction, |background_| will schedule the asynchronous // destruction of the wrapped |Background| object on the dispatcher. async_patterns::DispatcherBoundbackground_; };
|DispatcherBound| itself is thread-compatible.
Safety of sending arguments
When constructing |T| and calling member functions of |T|, it is possible to pass additional arguments if the constructor or member function requires it. The argument will be forwarded from the caller's thread into a heap data structure, and later moved into the thread which would run the dispatcher task asynchronously. Each argument must be safe to send to a different thread. See |async_patterns::BindForSending| for the detailed requirements.
Inheritance
Direct Known Subclasses:async_patterns::TestDispatcherBound< T >
Constructors and Destructors |
|
---|---|
DispatcherBound(async_dispatcher_t *dispatcher, std::in_place_t, Args &&... args)
Arguments after |std::in_place| are sent to the constructor of |T|.
|
|
DispatcherBound(async_dispatcher_t *dispatcher)
Constructs a |DispatcherBound| that does not hold an instance of |T|.
|
|
DispatcherBound(DispatcherBound &&)
Typically, asynchronous classes would contain internal self-pointers that make moving dangerous, so we disable moves here for now.
|
|
DispatcherBound(const DispatcherBound &)
|
|
~DispatcherBound()
If |has_value|, asynchronously destroys the managed |T| on a task posted to the dispatcher.
|
Public functions |
|
---|---|
AsyncCall(Member T::*member, Args &&... args)
|
auto
Asynchronously calls |member|, a pointer to member function of |T|, using the provided |args|.
|
emplace(Args &&... args)
|
void
Asynchronously constructs |T| on a task posted to the dispatcher.
|
has_value() const
|
bool
Returns if this object holds an instance of |T|.
|
operator=(DispatcherBound &&) noexcept=delete
|
|
operator=(const DispatcherBound &) noexcept=delete
|
|
reset()
|
void
If |has_value|, asynchronously destroys the managed |T| on a task posted to the dispatcher.
|
Protected functions |
|
---|---|
CheckArgs(fit::parameter_pack< Args...>)
|
constexpr void
|
UnsafeAsyncCallImpl(Callable && callable, Args &&... args)
|
auto
Calls an arbitrary |callable| asynchronously on the |dispatcher_|.
|
Public functions
AsyncCall
auto AsyncCall( Member T::*member, Args &&... args )
Asynchronously calls |member|, a pointer to member function of |T|, using the provided |args|.
|AsyncCall| returns a |PendingCall| object that lets you asynchronously monitor the result. You may either:
- Make a fire-and-forget call, by discarding the returned object, or
- Get a promise carrying the return value of the function by calling
promise()
on the object, yielding a |fpromise::promise|, or - Call
Then()
on the object and pass a |Callback|.
See |PendingCall| for details.
In particular, if |member| returns void, you could attach promises/callbacks that take void to asynchronously get notified when |member| has finished execution.
Example:
class Owner { public: Owner(async_dispatcher_t* owner_dispatcher) : receiver_{this, owner_dispatcher} { background_.emplace(); // Tell |background_| to |DoSomething|, then send back the return // value to |Owner| using |receiver_|. background_ .AsyncCall(&Background::DoSomething) .Then(receiver_.Once(&Owner::DoneSomething)); } void DoneSomething(Result result) { // |Background::DoSomething| has completed with |result|... } private: async::Loop background_loop_; async_patterns::DispatcherBoundbackground_{background_loop_.dispatcher()}; async_patterns::Receiver receiver_; };
See |async_patterns::BindForSending| for detailed requirements on |args|.
If |Background::DoSomething| is an overloaded member function, you may disambiguate it by spelling out its signature:
background_.AsyncCall<void(Result)>(&Background::DoSomething);
The task will be synchronously called if the dispatcher is shutdown.
DispatcherBound
DispatcherBound( async_dispatcher_t *dispatcher, std::in_place_t, Args &&... args )
Arguments after |std::in_place| are sent to the constructor of |T|.
See |async_patterns::BindForSending| for detailed requirements on |args|.
If you'd like to pass a |dispatcher| to |T| as a constructor argument, see |async_patterns::PassDispatcher|.
If the dispatcher is shutdown, |T| will be synchronously constructed.
DispatcherBound
DispatcherBound( async_dispatcher_t *dispatcher )
Constructs a |DispatcherBound| that does not hold an instance of |T|.
One may later construct |T| using |emplace| on the |dispatcher|.
DispatcherBound
DispatcherBound( DispatcherBound && ) noexcept=delete
Typically, asynchronous classes would contain internal self-pointers that make moving dangerous, so we disable moves here for now.
DispatcherBound
DispatcherBound( const DispatcherBound & ) noexcept=delete
emplace
void emplace( Args &&... args )
Asynchronously constructs |T| on a task posted to the dispatcher.
If this object already holds an instance of |T|, that older instance will be asynchronously destroyed on the dispatcher.
If |T2| is specified, it must be same as |T| or a subclass. Then an instance of |T2| will be constructed. This can be useful for mocking: |T| may be some interface, and when constructing the object, either a fake (in unit tests) or a real concrete type (in production) will be specified.
If you'd like to pass a |dispatcher| to |T| as a constructor argument, see |async_patterns::PassDispatcher|.
See |async_patterns::BindForSending| for detailed requirements on |args|.
has_value
bool has_value() const
Returns if this object holds an instance of |T|.
operator=
DispatcherBound & operator=( DispatcherBound && ) noexcept=delete
operator=
DispatcherBound & operator=( const DispatcherBound & ) noexcept=delete
reset
void reset()
If |has_value|, asynchronously destroys the managed |T| on a task posted to the dispatcher.
If the dispatcher is shutdown, |T| will be synchronously destroyed.
~DispatcherBound
~DispatcherBound()
If |has_value|, asynchronously destroys the managed |T| on a task posted to the dispatcher.
If the dispatcher is shutdown, |T| will be synchronously destroyed.
Protected functions
CheckArgs
constexpr void CheckArgs( fit::parameter_pack< Args...> )
UnsafeAsyncCallImpl
auto UnsafeAsyncCallImpl( Callable && callable, Args &&... args )
Calls an arbitrary |callable| asynchronously on the |dispatcher_|.