/* * Copyright 2014 The WebRTC Project Authors. All rights reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #ifndef RTC_BASE_ASYNC_INVOKER_H_ #define RTC_BASE_ASYNC_INVOKER_H_ #include #include #include #include "api/scoped_refptr.h" #include "rtc_base/async_invoker_inl.h" #include "rtc_base/bind.h" #include "rtc_base/constructor_magic.h" #include "rtc_base/event.h" #include "rtc_base/ref_counted_object.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" namespace rtc { // Invokes function objects (aka functors) asynchronously on a Thread, and // owns the lifetime of calls (ie, when this object is destroyed, calls in // flight are cancelled). AsyncInvoker can optionally execute a user-specified // function when the asynchronous call is complete, or operates in // fire-and-forget mode otherwise. // // AsyncInvoker does not own the thread it calls functors on. // // A note about async calls and object lifetimes: users should // be mindful of object lifetimes when calling functions asynchronously and // ensure objects used by the function _cannot_ be deleted between the // invocation and execution of the functor. AsyncInvoker is designed to // help: any calls in flight will be cancelled when the AsyncInvoker used to // make the call is destructed, and any calls executing will be allowed to // complete before AsyncInvoker destructs. // // The easiest way to ensure lifetimes are handled correctly is to create a // class that owns the Thread and AsyncInvoker objects, and then call its // methods asynchronously as needed. // // Example: // class MyClass { // public: // void FireAsyncTaskWithResult(Thread* thread, int x) { // // Specify a callback to get the result upon completion. // invoker_.AsyncInvoke(RTC_FROM_HERE, // thread, Bind(&MyClass::AsyncTaskWithResult, this, x), // &MyClass::OnTaskComplete, this); // } // void FireAnotherAsyncTask(Thread* thread) { // // No callback specified means fire-and-forget. // invoker_.AsyncInvoke(RTC_FROM_HERE, // thread, Bind(&MyClass::AnotherAsyncTask, this)); // // private: // int AsyncTaskWithResult(int x) { // // Some long running process... // return x * x; // } // void AnotherAsyncTask() { // // Some other long running process... // } // void OnTaskComplete(int result) { result_ = result; } // // AsyncInvoker invoker_; // int result_; // }; // // More details about threading: // - It's safe to construct/destruct AsyncInvoker on different threads. // - It's safe to call AsyncInvoke from different threads. // - It's safe to call AsyncInvoke recursively from *within* a functor that's // being AsyncInvoked. // - However, it's *not* safe to call AsyncInvoke from *outside* a functor // that's being AsyncInvoked while the AsyncInvoker is being destroyed on // another thread. This is just inherently unsafe and there's no way to // prevent that. So, the user of this class should ensure that the start of // each "chain" of invocations is synchronized somehow with the AsyncInvoker's // destruction. This can be done by starting each chain of invocations on the // same thread on which it will be destroyed, or by using some other // synchronization method. class AsyncInvoker : public MessageHandlerAutoCleanup { public: AsyncInvoker(); ~AsyncInvoker() override; // Call |functor| asynchronously on |thread|, with no callback upon // completion. Returns immediately. template void AsyncInvoke(const Location& posted_from, Thread* thread, FunctorT&& functor, uint32_t id = 0) { std::unique_ptr closure( new FireAndForgetAsyncClosure( this, std::forward(functor))); DoInvoke(posted_from, thread, std::move(closure), id); } // Call |functor| asynchronously on |thread| with |delay_ms|, with no callback // upon completion. Returns immediately. template void AsyncInvokeDelayed(const Location& posted_from, Thread* thread, FunctorT&& functor, uint32_t delay_ms, uint32_t id = 0) { std::unique_ptr closure( new FireAndForgetAsyncClosure( this, std::forward(functor))); DoInvokeDelayed(posted_from, thread, std::move(closure), delay_ms, id); } // Synchronously execute on |thread| all outstanding calls we own // that are pending on |thread|, and wait for calls to complete // before returning. Optionally filter by message id. // The destructor will not wait for outstanding calls, so if that // behavior is desired, call Flush() before destroying this object. void Flush(Thread* thread, uint32_t id = MQID_ANY); // Cancels any outstanding calls we own that are pending on any thread, and // which have not yet started to execute. This does not wait for any calls // that have already started executing to complete. void Clear(); private: void OnMessage(Message* msg) override; void DoInvoke(const Location& posted_from, Thread* thread, std::unique_ptr closure, uint32_t id); void DoInvokeDelayed(const Location& posted_from, Thread* thread, std::unique_ptr closure, uint32_t delay_ms, uint32_t id); // Used to keep track of how many invocations (AsyncClosures) are still // alive, so that the destructor can wait for them to finish, as described in // the class documentation. // // TODO(deadbeef): Using a raw std::atomic like this is prone to error and // difficult to maintain. We should try to wrap this functionality in a // separate class to reduce the chance of errors being introduced in the // future. std::atomic pending_invocations_; // Reference counted so that if the AsyncInvoker destructor finishes before // an AsyncClosure's destructor that's about to call // "invocation_complete_->Set()", it's not dereferenced after being // destroyed. scoped_refptr> invocation_complete_; // This flag is used to ensure that if an application AsyncInvokes tasks that // recursively AsyncInvoke other tasks ad infinitum, the cycle eventually // terminates. std::atomic destroying_; friend class AsyncClosure; RTC_DISALLOW_COPY_AND_ASSIGN(AsyncInvoker); }; } // namespace rtc #endif // RTC_BASE_ASYNC_INVOKER_H_