/* * 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 "absl/base/attributes.h" #include "api/scoped_refptr.h" #include "rtc_base/async_invoker_inl.h" #include "rtc_base/event.h" #include "rtc_base/third_party/sigslot/sigslot.h" #include "rtc_base/thread.h" namespace rtc { // DEPRECATED - do not use. // // 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 DEPRECATED_AsyncInvoker : public MessageHandlerAutoCleanup { public: DEPRECATED_AsyncInvoker(); ~DEPRECATED_AsyncInvoker() override; DEPRECATED_AsyncInvoker(const DEPRECATED_AsyncInvoker&) = delete; DEPRECATED_AsyncInvoker& operator=(const DEPRECATED_AsyncInvoker&) = delete; // 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); } // 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 destructor finishes before an // AsyncClosure's destructor that's about to call // "invocation_complete_->Set()", it's not dereferenced after being destroyed. rtc::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; }; } // namespace rtc #endif // RTC_BASE_ASYNC_INVOKER_H_