// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef PPAPI_UTILITY_COMPLETION_CALLBACK_FACTORY_H_ #define PPAPI_UTILITY_COMPLETION_CALLBACK_FACTORY_H_ #include "ppapi/cpp/completion_callback.h" #include "ppapi/utility/non_thread_safe_ref_count.h" /// @file /// This file defines the API to create CompletionCallback objects that are /// bound to member functions. namespace pp { // TypeUnwrapper -------------------------------------------------------------- namespace internal { // The TypeUnwrapper converts references and const references to the // underlying type used for storage and passing as an argument. It is for // internal use only. template struct TypeUnwrapper { typedef T StorageType; }; template struct TypeUnwrapper { typedef T StorageType; }; template struct TypeUnwrapper { typedef T StorageType; }; } // namespace internal // ---------------------------------------------------------------------------- /// CompletionCallbackFactory may be used to create CompletionCallback /// objects that are bound to member functions. /// /// If a factory is destroyed, then any pending callbacks will be cancelled /// preventing any bound member functions from being called. The CancelAll() /// method allows pending callbacks to be cancelled without destroying the /// factory. /// /// Note: CompletionCallbackFactory isn't /// thread safe, but you can make it more thread-friendly by passing a /// thread-safe refcounting class as the second template element. However, it /// only guarantees safety for creating a callback from another thread, the /// callback itself needs to execute on the same thread as the thread that /// creates/destroys the factory. With this restriction, it is safe to create /// the CompletionCallbackFactory on the main thread, create /// callbacks from any thread and pass them to CallOnMainThread(). /// /// Example: /// /// @code /// class MyClass { /// public: /// // If an compiler warns on following using |this| in the initializer /// // list, use PP_ALLOW_THIS_IN_INITIALIZER_LIST macro. /// MyClass() : factory_(this) { /// } /// /// void OpenFile(const pp::FileRef& file) { /// pp::CompletionCallback cc = factory_.NewCallback(&MyClass::DidOpen); /// int32_t rv = file_io_.Open(file, PP_FileOpenFlag_Read, cc); /// CHECK(rv == PP_OK_COMPLETIONPENDING); /// } /// /// private: /// void DidOpen(int32_t result) { /// if (result == PP_OK) { /// // The file is open, and we can begin reading. /// // ... /// } else { /// // Failed to open the file with error given by 'result'. /// } /// } /// /// pp::CompletionCallbackFactory factory_; /// }; /// @endcode /// /// Passing additional parameters to your callback /// /// As a convenience, the CompletionCallbackFactory can optionally /// create a closure with up to three bound parameters that it will pass to /// your callback function. This can be useful for passing information about /// the request to your callback function, which is especially useful if your /// class has multiple asynchronous callbacks pending. /// /// For the above example, of opening a file, let's say you want to keep some /// description associated with your request, you might implement your OpenFile /// and DidOpen callback as follows: /// /// @code /// void OpenFile(const pp::FileRef& file) { /// std::string message = "Opening file!"; /// pp::CompletionCallback cc = factory_.NewCallback(&MyClass::DidOpen, /// message); /// int32_t rv = file_io_.Open(file, PP_FileOpenFlag_Read, cc); /// CHECK(rv == PP_OK_COMPLETIONPENDING); /// } /// void DidOpen(int32_t result, const std::string& message) { /// // "message" will be "Opening file!". /// ... /// } /// @endcode /// /// Optional versus required callbacks /// /// When you create an "optional" callback, the browser may return the results /// synchronously if they are available. This can allow for higher performance /// in some cases if data is available quickly (for example, for network loads /// where there may be a lot of data coming quickly). In this case, the /// callback will never be run. /// /// When creating a new callback with the factory, there will be data allocated /// on the heap that tracks the callback information and any bound arguments. /// This data is freed when the callback executes. In the case of optional /// callbacks, since the browser will never issue the callback, the internal /// tracking data will be leaked. /// /// Therefore, if you use optional callbacks, it's important to manually /// issue the callback to free up this data. The typical pattern is: /// /// @code /// pp::CompletionCallback callback = callback_factory.NewOptionalCallback( /// &MyClass::OnDataReady); /// int32_t result = interface->GetData(callback); /// if (result != PP_OK_COMPLETIONPENDING) /// callback.Run(result); /// @endcode /// /// Because of this additional complexity, it's generally recommended that /// you not use optional callbacks except when performance is more important /// (such as loading large resources from the network). In most other cases, /// the performance difference will not be worth the additional complexity, /// and most functions may never actually have the ability to complete /// synchronously. /// /// Completion callbacks with output /// /// For some API calls, the browser returns data to the caller via an output /// parameter. These can be difficult to manage since the output parameter /// must remain valid for as long as the callback is pending. Note also that /// CancelAll (or destroying the callback factory) does not cancel the /// callback from the browser's perspective, only the execution of the callback /// in the plugin code, and the output parameter will still be written to! /// This means that you can't use class members as output parameters without /// risking crashes. /// /// To make this case easier, the CompletionCallbackFactory can allocate and /// manage the output data for you and pass it to your callback function. This /// makes such calls more natural and less error-prone. /// /// To create such a callback, use NewCallbackWithOutput and specify a callback /// function that takes the output parameter as its second argument. Let's say /// you're calling a function GetFile which asynchronously returns a /// pp::FileRef. GetFile's signature will be int32_t GetFile(const /// CompletionCallbackWithOutput& callback); and your /// calling code would look like this: /// /// @code /// void RequestFile() { /// file_interface->GetFile(callback_factory_.NewCallbackWithOutput( /// &MyClass::GotFile)); /// } /// void GotFile(int32_t result, const pp::FileRef& file) { /// if (result == PP_OK) { /// ...use file... /// } else { /// ...handle error... /// } /// } /// @endcode /// /// As with regular completion callbacks, you can optionally add up to three /// bound arguments. These are passed following the output argument. /// /// Your callback may take the output argument as a copy (common for small /// types like integers, a const reference (common for structures and /// resources to avoid an extra copy), or as a non-const reference. One /// optimization you can do if your callback function may take large arrays /// is to accept your output argument as a non-const reference and to swap() /// the argument with a vector of your own to store it. This means you don't /// have to copy the buffer to consume it. template class CompletionCallbackFactory { public: /// This constructor creates a CompletionCallbackFactory /// bound to an object. If the constructor is called without an argument, /// the default value of NULL is used. The user then must call /// Initialize() to initialize the object. /// /// param[in] object Optional parameter. An object whose member functions /// are to be bound to CompletionCallbacks created by this /// CompletionCallbackFactory. The default value of this /// parameter is NULL. explicit CompletionCallbackFactory(T* object = NULL) : object_(object) { InitBackPointer(); } /// Destructor. ~CompletionCallbackFactory() { ResetBackPointer(); } /// CancelAll() cancels all CompletionCallbacks allocated from /// this factory. void CancelAll() { ResetBackPointer(); InitBackPointer(); } /// Initialize() binds the CallbackFactory to a particular /// object. Use this when the object is not available at /// CallbackFactory creation, and the NULL default /// is passed to the constructor. The object may only be initialized once, /// either by the constructor, or by a call to Initialize(). /// /// @param[in] object The object whose member functions are to be bound to /// the CompletionCallback created by this /// CompletionCallbackFactory. void Initialize(T* object) { PP_DCHECK(object); PP_DCHECK(!object_); // May only initialize once! object_ = object; } /// GetObject() returns the object that was passed at initialization to /// Intialize(). /// /// @return the object passed to the constructor or Intialize(). T* GetObject() { return object_; } /// NewCallback allocates a new, single-use CompletionCallback. /// The CompletionCallback must be run in order for the memory /// allocated by the methods to be freed. /// /// @param[in] method The method to be invoked upon completion of the /// operation. /// /// @return A CompletionCallback. template CompletionCallback NewCallback(Method method) { PP_DCHECK(object_); return NewCallbackHelper(new Dispatcher0(method)); } /// NewOptionalCallback() allocates a new, single-use /// CompletionCallback that might not run if the method /// taking it can complete synchronously. Thus, if after passing the /// CompletionCallback to a Pepper method, the method does not return /// PP_OK_COMPLETIONPENDING, then you should manually call the /// CompletionCallback's Run method, or memory will be leaked. /// /// @param[in] method The method to be invoked upon completion of the /// operation. /// /// @return A CompletionCallback. template CompletionCallback NewOptionalCallback(Method method) { CompletionCallback cc = NewCallback(method); cc.set_flags(cc.flags() | PP_COMPLETIONCALLBACK_FLAG_OPTIONAL); return cc; } /// NewCallbackWithOutput() allocates a new, single-use /// CompletionCallback where the browser will pass an additional /// parameter comtaining the result of the request. The /// CompletionCallback must be run in order for the memory /// allocated by the methods to be freed. /// /// @param[in] method The method to be invoked upon completion of the /// operation. /// /// @return A CompletionCallback. template CompletionCallbackWithOutput< typename internal::TypeUnwrapper::StorageType> NewCallbackWithOutput(void (T::*method)(int32_t, Output)) { return NewCallbackWithOutputHelper(new DispatcherWithOutput0< typename internal::TypeUnwrapper::StorageType, void (T::*)(int32_t, Output)>(method)); } /// NewCallback() allocates a new, single-use CompletionCallback. /// The CompletionCallback must be run in order for the memory /// allocated by the methods to be freed. /// /// @param[in] method The method to be invoked upon completion of the /// operation. Method should be of type: /// void (T::*)(int32_t result, const A& a) /// /// @param[in] a Passed to method when the completion callback /// runs. /// /// @return A CompletionCallback. template CompletionCallback NewCallback(Method method, const A& a) { PP_DCHECK(object_); return NewCallbackHelper(new Dispatcher1(method, a)); } /// NewOptionalCallback() allocates a new, single-use /// CompletionCallback that might not run if the method /// taking it can complete synchronously. Thus, if after passing the /// CompletionCallback to a Pepper method, the method does not return /// PP_OK_COMPLETIONPENDING, then you should manually call the /// CompletionCallback's Run method, or memory will be leaked. /// /// @param[in] method The method to be invoked upon completion of the /// operation. Method should be of type: /// void (T::*)(int32_t result, const A& a) /// /// @param[in] a Passed to method when the completion callback /// runs. /// /// @return A CompletionCallback. template CompletionCallback NewOptionalCallback(Method method, const A& a) { CompletionCallback cc = NewCallback(method, a); cc.set_flags(cc.flags() | PP_COMPLETIONCALLBACK_FLAG_OPTIONAL); return cc; } /// NewCallbackWithOutput() allocates a new, single-use /// CompletionCallback where the browser will pass an additional /// parameter comtaining the result of the request. The /// CompletionCallback must be run in order for the memory /// allocated by the methods to be freed. /// /// @param[in] method The method to be invoked upon completion of the /// operation. /// /// @param[in] a Passed to method when the completion callback /// runs. /// /// @return A CompletionCallback. template CompletionCallbackWithOutput< typename internal::TypeUnwrapper::StorageType> NewCallbackWithOutput(void (T::*method)(int32_t, Output, A), const A& a) { return NewCallbackWithOutputHelper(new DispatcherWithOutput1< typename internal::TypeUnwrapper::StorageType, void (T::*)(int32_t, Output, A), typename internal::TypeUnwrapper::StorageType>(method, a)); } /// NewCallback() allocates a new, single-use /// CompletionCallback. /// The CompletionCallback must be run in order for the memory /// allocated by the methods to be freed. /// /// @param method The method taking the callback. Method should be of type: /// void (T::*)(int32_t result, const A& a, const B& b) /// /// @param[in] a Passed to method when the completion callback /// runs. /// /// @param[in] b Passed to method when the completion callback /// runs. /// /// @return A CompletionCallback. template CompletionCallback NewCallback(Method method, const A& a, const B& b) { PP_DCHECK(object_); return NewCallbackHelper(new Dispatcher2(method, a, b)); } /// NewOptionalCallback() allocates a new, single-use /// CompletionCallback that might not run if the method /// taking it can complete synchronously. Thus, if after passing the /// CompletionCallback to a Pepper method, the method does not return /// PP_OK_COMPLETIONPENDING, then you should manually call the /// CompletionCallback's Run method, or memory will be leaked. /// /// @param[in] method The method taking the callback. Method should be of /// type: /// void (T::*)(int32_t result, const A& a, const B& b) /// /// @param[in] a Passed to method when the completion callback /// runs. /// /// @param[in] b Passed to method when the completion callback /// runs. /// /// @return A CompletionCallback. template CompletionCallback NewOptionalCallback(Method method, const A& a, const B& b) { CompletionCallback cc = NewCallback(method, a, b); cc.set_flags(cc.flags() | PP_COMPLETIONCALLBACK_FLAG_OPTIONAL); return cc; } /// NewCallbackWithOutput() allocates a new, single-use /// CompletionCallback where the browser will pass an additional /// parameter comtaining the result of the request. The /// CompletionCallback must be run in order for the memory /// allocated by the methods to be freed. /// /// @param[in] method The method to be invoked upon completion of the /// operation. /// /// @param[in] a Passed to method when the completion callback /// runs. /// /// @param[in] b Passed to method when the completion callback /// runs. /// /// @return A CompletionCallback. template CompletionCallbackWithOutput< typename internal::TypeUnwrapper::StorageType> NewCallbackWithOutput(void (T::*method)(int32_t, Output, A, B), const A& a, const B& b) { return NewCallbackWithOutputHelper(new DispatcherWithOutput2< typename internal::TypeUnwrapper::StorageType, void (T::*)(int32_t, Output, A, B), typename internal::TypeUnwrapper::StorageType, typename internal::TypeUnwrapper::StorageType>(method, a, b)); } /// NewCallback() allocates a new, single-use /// CompletionCallback. /// The CompletionCallback must be run in order for the memory /// allocated by the methods to be freed. /// /// @param method The method taking the callback. Method should be of type: /// /// void (T::*)(int32_t result, const A& a, const B& b, const C& c) /// /// /// @param[in] a Passed to method when the completion callback /// runs. /// /// @param[in] b Passed to method when the completion callback /// runs. /// /// @param[in] c Passed to method when the completion callback /// runs. /// /// @return A CompletionCallback. template CompletionCallback NewCallback(Method method, const A& a, const B& b, const C& c) { PP_DCHECK(object_); return NewCallbackHelper(new Dispatcher3(method, a, b, c)); } /// NewOptionalCallback() allocates a new, single-use /// CompletionCallback that might not run if the method /// taking it can complete synchronously. Thus, if after passing the /// CompletionCallback to a Pepper method, the method does not return /// PP_OK_COMPLETIONPENDING, then you should manually call the /// CompletionCallback's Run method, or memory will be leaked. /// /// @param[in] method The method taking the callback. Method should be of /// type: /// /// void (T::*)(int32_t result, const A& a, const B& b, const C& c) /// /// /// @param[in] a Passed to method when the completion callback /// runs. /// /// @param[in] b Passed to method when the completion callback /// runs. /// /// @param[in] c Passed to method when the completion callback /// runs. /// /// @return A CompletionCallback. template CompletionCallback NewOptionalCallback(Method method, const A& a, const B& b, const C& c) { CompletionCallback cc = NewCallback(method, a, b, c); cc.set_flags(cc.flags() | PP_COMPLETIONCALLBACK_FLAG_OPTIONAL); return cc; } /// NewCallbackWithOutput() allocates a new, single-use /// CompletionCallback where the browser will pass an additional /// parameter comtaining the result of the request. The /// CompletionCallback must be run in order for the memory /// allocated by the methods to be freed. /// /// @param method The method to be run. /// /// @param[in] a Passed to method when the completion callback /// runs. /// /// @param[in] b Passed to method when the completion callback /// runs. /// /// @param[in] c Passed to method when the completion callback /// runs. /// /// @return A CompletionCallback. template CompletionCallbackWithOutput< typename internal::TypeUnwrapper::StorageType> NewCallbackWithOutput(void (T::*method)(int32_t, Output, A, B, C), const A& a, const B& b, const C& c) { return NewCallbackWithOutputHelper(new DispatcherWithOutput3< typename internal::TypeUnwrapper::StorageType, void (T::*)(int32_t, Output, A, B, C), typename internal::TypeUnwrapper::StorageType, typename internal::TypeUnwrapper::StorageType, typename internal::TypeUnwrapper::StorageType>(method, a, b, c)); } private: class BackPointer { public: typedef CompletionCallbackFactory FactoryType; explicit BackPointer(FactoryType* factory) : factory_(factory) { } void AddRef() { ref_.AddRef(); } void Release() { if (ref_.Release() == 0) delete this; } void DropFactory() { factory_ = NULL; } T* GetObject() { return factory_ ? factory_->GetObject() : NULL; } private: RefCount ref_; FactoryType* factory_; }; template class CallbackData { public: // Takes ownership of the given dispatcher pointer. CallbackData(BackPointer* back_pointer, Dispatcher* dispatcher) : back_pointer_(back_pointer), dispatcher_(dispatcher) { back_pointer_->AddRef(); } ~CallbackData() { back_pointer_->Release(); delete dispatcher_; } Dispatcher* dispatcher() { return dispatcher_; } static void Thunk(void* user_data, int32_t result) { Self* self = static_cast(user_data); T* object = self->back_pointer_->GetObject(); // Please note that |object| may be NULL at this point. But we still need // to call into Dispatcher::operator() in that case, so that it can do // necessary cleanup. (*self->dispatcher_)(object, result); delete self; } private: typedef CallbackData Self; BackPointer* back_pointer_; // We own a ref to this refcounted object. Dispatcher* dispatcher_; // We own this pointer. // Disallow copying & assignment. CallbackData(const CallbackData&); CallbackData& operator=(const CallbackData&); }; template class Dispatcher0 { public: Dispatcher0() : method_(NULL) {} explicit Dispatcher0(Method method) : method_(method) { } void operator()(T* object, int32_t result) { if (object) (object->*method_)(result); } private: Method method_; }; template class DispatcherWithOutput0 { public: typedef Output OutputType; typedef internal::CallbackOutputTraits Traits; DispatcherWithOutput0() : method_(NULL), output_() { } DispatcherWithOutput0(Method method) : method_(method), output_() { } void operator()(T* object, int32_t result) { // We must call Traits::StorageToPluginArg() even if we don't need to call // the callback anymore, otherwise we may leak resource or var references. if (object) (object->*method_)(result, Traits::StorageToPluginArg(output_)); else Traits::StorageToPluginArg(output_); } typename Traits::StorageType* output() { return &output_; } private: Method method_; typename Traits::StorageType output_; }; template class Dispatcher1 { public: Dispatcher1() : method_(NULL), a_() { } Dispatcher1(Method method, const A& a) : method_(method), a_(a) { } void operator()(T* object, int32_t result) { if (object) (object->*method_)(result, a_); } private: Method method_; A a_; }; template class DispatcherWithOutput1 { public: typedef Output OutputType; typedef internal::CallbackOutputTraits Traits; DispatcherWithOutput1() : method_(NULL), a_(), output_() { } DispatcherWithOutput1(Method method, const A& a) : method_(method), a_(a), output_() { } void operator()(T* object, int32_t result) { // We must call Traits::StorageToPluginArg() even if we don't need to call // the callback anymore, otherwise we may leak resource or var references. if (object) (object->*method_)(result, Traits::StorageToPluginArg(output_), a_); else Traits::StorageToPluginArg(output_); } typename Traits::StorageType* output() { return &output_; } private: Method method_; A a_; typename Traits::StorageType output_; }; template class Dispatcher2 { public: Dispatcher2() : method_(NULL), a_(), b_() { } Dispatcher2(Method method, const A& a, const B& b) : method_(method), a_(a), b_(b) { } void operator()(T* object, int32_t result) { if (object) (object->*method_)(result, a_, b_); } private: Method method_; A a_; B b_; }; template class DispatcherWithOutput2 { public: typedef Output OutputType; typedef internal::CallbackOutputTraits Traits; DispatcherWithOutput2() : method_(NULL), a_(), b_(), output_() { } DispatcherWithOutput2(Method method, const A& a, const B& b) : method_(method), a_(a), b_(b), output_() { } void operator()(T* object, int32_t result) { // We must call Traits::StorageToPluginArg() even if we don't need to call // the callback anymore, otherwise we may leak resource or var references. if (object) (object->*method_)(result, Traits::StorageToPluginArg(output_), a_, b_); else Traits::StorageToPluginArg(output_); } typename Traits::StorageType* output() { return &output_; } private: Method method_; A a_; B b_; typename Traits::StorageType output_; }; template class Dispatcher3 { public: Dispatcher3() : method_(NULL), a_(), b_(), c_() { } Dispatcher3(Method method, const A& a, const B& b, const C& c) : method_(method), a_(a), b_(b), c_(c) { } void operator()(T* object, int32_t result) { if (object) (object->*method_)(result, a_, b_, c_); } private: Method method_; A a_; B b_; C c_; }; template class DispatcherWithOutput3 { public: typedef Output OutputType; typedef internal::CallbackOutputTraits Traits; DispatcherWithOutput3() : method_(NULL), a_(), b_(), c_(), output_() { } DispatcherWithOutput3(Method method, const A& a, const B& b, const C& c) : method_(method), a_(a), b_(b), c_(c), output_() { } void operator()(T* object, int32_t result) { // We must call Traits::StorageToPluginArg() even if we don't need to call // the callback anymore, otherwise we may leak resource or var references. if (object) { (object->*method_)(result, Traits::StorageToPluginArg(output_), a_, b_, c_); } else { Traits::StorageToPluginArg(output_); } } typename Traits::StorageType* output() { return &output_; } private: Method method_; A a_; B b_; C c_; typename Traits::StorageType output_; }; void InitBackPointer() { back_pointer_ = new BackPointer(this); back_pointer_->AddRef(); } void ResetBackPointer() { back_pointer_->DropFactory(); back_pointer_->Release(); } // Takes ownership of the dispatcher pointer, which should be heap allocated. template CompletionCallback NewCallbackHelper(Dispatcher* dispatcher) { PP_DCHECK(object_); // Expects a non-null object! return CompletionCallback( &CallbackData::Thunk, new CallbackData(back_pointer_, dispatcher)); } // Takes ownership of the dispatcher pointer, which should be heap allocated. template CompletionCallbackWithOutput< typename internal::TypeUnwrapper< typename Dispatcher::OutputType>::StorageType> NewCallbackWithOutputHelper(Dispatcher* dispatcher) { PP_DCHECK(object_); // Expects a non-null object! CallbackData* data = new CallbackData(back_pointer_, dispatcher); return CompletionCallbackWithOutput( &CallbackData::Thunk, data, data->dispatcher()->output()); } // Disallowed: CompletionCallbackFactory(const CompletionCallbackFactory&); CompletionCallbackFactory& operator=(const CompletionCallbackFactory&); T* object_; BackPointer* back_pointer_; }; } // namespace pp #endif // PPAPI_UTILITY_COMPLETION_CALLBACK_FACTORY_H_