Index: ppapi/utility/completion_callback_factory.h |
diff --git a/ppapi/utility/completion_callback_factory.h b/ppapi/utility/completion_callback_factory.h |
index e0b1f37bdcc7dfbc1a9de0fdf03833eff9572df7..964cd0274836ea499c59dd291629c14d9e658757 100644 |
--- a/ppapi/utility/completion_callback_factory.h |
+++ b/ppapi/utility/completion_callback_factory.h |
@@ -10,6 +10,27 @@ |
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 <typename T> struct TypeUnwrapper { |
+ typedef T StorageType; |
+}; |
+template <typename T> struct TypeUnwrapper<T&> { |
+ typedef T StorageType; |
+}; |
+template <typename T> struct TypeUnwrapper<const T&> { |
+ typedef T StorageType; |
+}; |
+ |
+} // namespace internal |
+ |
+// ---------------------------------------------------------------------------- |
+ |
/// CompletionCallbackFactory<T> may be used to create CompletionCallback |
/// objects that are bound to member functions. |
/// |
@@ -30,68 +51,137 @@ namespace pp { |
/// <strong>Example: </strong> |
/// |
/// @code |
-/// |
-/// class MyHandler { |
+/// class MyClass { |
/// public: |
/// // If an compiler warns on following using |this| in the initializer |
/// // list, use PP_ALLOW_THIS_IN_INITIALIZER_LIST macro. |
-/// MyHandler() : factory_(this), offset_(0) { |
+/// MyClass() : factory_(this) { |
/// } |
/// |
-/// void ProcessFile(const FileRef& file) { |
-/// CompletionCallback cc = factory_.NewCallback( |
-/// &MyHandler::DidOpen); |
-/// int32_t rv = fio_.Open(file, PP_FileOpenFlag_Read, cc); |
+/// 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: |
-/// CompletionCallback NewCallback() { |
-/// return factory_.NewCallback(&MyHandler::DidCompleteIO); |
-/// } |
-/// |
/// void DidOpen(int32_t result) { |
/// if (result == PP_OK) { |
/// // The file is open, and we can begin reading. |
-/// offset_ = 0; |
-/// ReadMore(); |
+/// // ... |
/// } else { |
/// // Failed to open the file with error given by 'result'. |
/// } |
/// } |
/// |
-/// void DidRead(int32_t result) { |
-/// if (result > 0) { |
-/// // buf_ now contains 'result' number of bytes from the file. |
-/// ProcessBytes(buf_, result); |
-/// offset_ += result; |
-/// ReadMore(); |
-/// } else { |
-/// // Done reading (possibly with an error given by 'result'). |
-/// } |
-/// } |
+/// pp::CompletionCallbackFactory<MyClass> factory_; |
+/// }; |
+/// @endcode |
/// |
-/// void ReadMore() { |
-/// CompletionCallback cc = |
-/// factory_.NewOptionalCallback(&MyHandler::DidRead); |
-/// int32_t rv = fio_.Read(offset_, buf_, sizeof(buf_), |
-/// cc.pp_completion_callback()); |
-/// if (rv != PP_OK_COMPLETIONPENDING) |
-/// cc.Run(rv); |
-/// } |
+/// <strong>Passing additional parameters to your callback</strong> |
/// |
-/// void ProcessBytes(const char* bytes, int32_t length) { |
-/// // Do work ... |
-/// } |
+/// As a convenience, the <code>CompletionCallbackFactory</code> 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. |
/// |
-/// pp::CompletionCallbackFactory<MyHandler> factory_; |
-/// pp::FileIO fio_; |
-/// char buf_[4096]; |
-/// int64_t offset_; |
-/// }; |
+/// 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 |
+/// |
+/// <strong>Optional versus required callbacks</strong> |
+/// |
+/// 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. |
+/// |
+/// <strong>Completion callbacks with output</strong> |
+/// |
+/// 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 <i>not</i> 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 <code>int32_t GetFile(const |
+/// CompletionCallbackWithOutput<pp::FileRef>& callback);</code> 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 <typename T, typename RefCount = NonThreadSafeRefCount> |
class CompletionCallbackFactory { |
public: |
@@ -155,7 +245,7 @@ class CompletionCallbackFactory { |
template <typename Method> |
CompletionCallback NewCallback(Method method) { |
PP_DCHECK(object_); |
- return NewCallbackHelper(Dispatcher0<Method>(method)); |
+ return NewCallbackHelper(new Dispatcher0<Method>(method)); |
} |
/// NewOptionalCallback() allocates a new, single-use |
@@ -176,6 +266,25 @@ class CompletionCallbackFactory { |
return cc; |
} |
+ /// NewCallbackWithOutput() allocates a new, single-use |
+ /// <code>CompletionCallback</code> where the browser will pass an additional |
+ /// parameter comtaining the result of the request. The |
+ /// <code>CompletionCallback</code> 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 <code>CompletionCallback</code>. |
+ template <typename Output> |
+ CompletionCallbackWithOutput< |
+ typename internal::TypeUnwrapper<Output>::StorageType> |
+ NewCallbackWithOutput(void (T::*method)(int32_t, Output)) { |
+ return NewCallbackWithOutputHelper(new DispatcherWithOutput0< |
+ typename internal::TypeUnwrapper<Output>::StorageType, |
+ void (T::*)(int32_t, Output)>(method)); |
+ } |
+ |
/// NewCallback() allocates a new, single-use <code>CompletionCallback</code>. |
/// The <code>CompletionCallback</code> must be run in order for the memory |
/// allocated by the methods to be freed. |
@@ -191,7 +300,7 @@ class CompletionCallbackFactory { |
template <typename Method, typename A> |
CompletionCallback NewCallback(Method method, const A& a) { |
PP_DCHECK(object_); |
- return NewCallbackHelper(Dispatcher1<Method, A>(method, a)); |
+ return NewCallbackHelper(new Dispatcher1<Method, A>(method, a)); |
} |
/// NewOptionalCallback() allocates a new, single-use |
@@ -216,6 +325,30 @@ class CompletionCallbackFactory { |
return cc; |
} |
+ /// NewCallbackWithOutput() allocates a new, single-use |
+ /// <code>CompletionCallback</code> where the browser will pass an additional |
+ /// parameter comtaining the result of the request. The |
+ /// <code>CompletionCallback</code> 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 <code>method</code> when the completion callback |
+ /// runs. |
+ /// |
+ /// @return A <code>CompletionCallback</code>. |
+ template <typename Output, typename A> |
+ CompletionCallbackWithOutput< |
+ typename internal::TypeUnwrapper<Output>::StorageType> |
+ NewCallbackWithOutput(void (T::*method)(int32_t, Output, A), |
+ const A& a) { |
+ return NewCallbackWithOutputHelper(new DispatcherWithOutput1< |
+ typename internal::TypeUnwrapper<Output>::StorageType, |
+ void (T::*)(int32_t, Output, A), |
+ typename internal::TypeUnwrapper<A>::StorageType>(method, a)); |
+ } |
+ |
/// NewCallback() allocates a new, single-use |
/// <code>CompletionCallback</code>. |
/// The <code>CompletionCallback</code> must be run in order for the memory |
@@ -234,7 +367,7 @@ class CompletionCallbackFactory { |
template <typename Method, typename A, typename B> |
CompletionCallback NewCallback(Method method, const A& a, const B& b) { |
PP_DCHECK(object_); |
- return NewCallbackHelper(Dispatcher2<Method, A, B>(method, a, b)); |
+ return NewCallbackHelper(new Dispatcher2<Method, A, B>(method, a, b)); |
} |
/// NewOptionalCallback() allocates a new, single-use |
@@ -263,6 +396,35 @@ class CompletionCallbackFactory { |
return cc; |
} |
+ /// NewCallbackWithOutput() allocates a new, single-use |
+ /// <code>CompletionCallback</code> where the browser will pass an additional |
+ /// parameter comtaining the result of the request. The |
+ /// <code>CompletionCallback</code> 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 <code>method</code> when the completion callback |
+ /// runs. |
+ /// |
+ /// @param[in] b Passed to <code>method</code> when the completion callback |
+ /// runs. |
+ /// |
+ /// @return A <code>CompletionCallback</code>. |
+ template <typename Output, typename A, typename B> |
+ CompletionCallbackWithOutput< |
+ typename internal::TypeUnwrapper<Output>::StorageType> |
+ NewCallbackWithOutput(void (T::*method)(int32_t, Output, A, B), |
+ const A& a, |
+ const B& b) { |
+ return NewCallbackWithOutputHelper(new DispatcherWithOutput2< |
+ typename internal::TypeUnwrapper<Output>::StorageType, |
+ void (T::*)(int32_t, Output, A, B), |
+ typename internal::TypeUnwrapper<A>::StorageType, |
+ typename internal::TypeUnwrapper<B>::StorageType>(method, a, b)); |
+ } |
+ |
/// NewCallback() allocates a new, single-use |
/// <code>CompletionCallback</code>. |
/// The <code>CompletionCallback</code> must be run in order for the memory |
@@ -287,7 +449,7 @@ class CompletionCallbackFactory { |
CompletionCallback NewCallback(Method method, const A& a, const B& b, |
const C& c) { |
PP_DCHECK(object_); |
- return NewCallbackHelper(Dispatcher3<Method, A, B, C>(method, a, b, c)); |
+ return NewCallbackHelper(new Dispatcher3<Method, A, B, C>(method, a, b, c)); |
} |
/// NewOptionalCallback() allocates a new, single-use |
@@ -321,12 +483,45 @@ class CompletionCallbackFactory { |
return cc; |
} |
+ /// NewCallbackWithOutput() allocates a new, single-use |
+ /// <code>CompletionCallback</code> where the browser will pass an additional |
+ /// parameter comtaining the result of the request. The |
+ /// <code>CompletionCallback</code> 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 <code>method</code> when the completion callback |
+ /// runs. |
+ /// |
+ /// @param[in] b Passed to <code>method</code> when the completion callback |
+ /// runs. |
+ /// |
+ /// @param[in] c Passed to <code>method</code> when the completion callback |
+ /// runs. |
+ /// |
+ /// @return A <code>CompletionCallback</code>. |
+ template <typename Output, typename A, typename B, typename C> |
+ CompletionCallbackWithOutput< |
+ typename internal::TypeUnwrapper<Output>::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<Output>::StorageType, |
+ void (T::*)(int32_t, Output, A, B, C), |
+ typename internal::TypeUnwrapper<A>::StorageType, |
+ typename internal::TypeUnwrapper<B>::StorageType, |
+ typename internal::TypeUnwrapper<C>::StorageType>(method, a, b, c)); |
+ } |
+ |
private: |
class BackPointer { |
public: |
typedef CompletionCallbackFactory<T, RefCount> FactoryType; |
- BackPointer(FactoryType* factory) |
+ explicit BackPointer(FactoryType* factory) |
: factory_(factory) { |
} |
@@ -355,7 +550,8 @@ class CompletionCallbackFactory { |
template <typename Dispatcher> |
class CallbackData { |
public: |
- CallbackData(BackPointer* back_pointer, const Dispatcher& dispatcher) |
+ // Takes ownership of the given dispatcher pointer. |
+ CallbackData(BackPointer* back_pointer, Dispatcher* dispatcher) |
: back_pointer_(back_pointer), |
dispatcher_(dispatcher) { |
back_pointer_->AddRef(); |
@@ -363,26 +559,34 @@ class CompletionCallbackFactory { |
~CallbackData() { |
back_pointer_->Release(); |
+ delete dispatcher_; |
} |
+ Dispatcher* dispatcher() { return dispatcher_; } |
+ |
static void Thunk(void* user_data, int32_t result) { |
Self* self = static_cast<Self*>(user_data); |
T* object = self->back_pointer_->GetObject(); |
if (object) |
- self->dispatcher_(object, result); |
+ (*self->dispatcher_)(object, result); |
delete self; |
} |
private: |
typedef CallbackData<Dispatcher> Self; |
- BackPointer* back_pointer_; |
- Dispatcher dispatcher_; |
+ BackPointer* back_pointer_; // We own a ref to this refcounted object. |
+ Dispatcher* dispatcher_; // We own this pointer. |
+ |
+ // Disallow copying & assignment. |
+ CallbackData(const CallbackData<Dispatcher>&); |
+ CallbackData<Dispatcher>& operator=(const CallbackData<Dispatcher>&); |
}; |
template <typename Method> |
class Dispatcher0 { |
public: |
- Dispatcher0(Method method) : method_(method) { |
+ Dispatcher0() : method_(NULL) {} |
+ explicit Dispatcher0(Method method) : method_(method) { |
} |
void operator()(T* object, int32_t result) { |
(object->*method_)(result); |
@@ -391,9 +595,31 @@ class CompletionCallbackFactory { |
Method method_; |
}; |
+ template <typename Output, typename Method> |
+ class DispatcherWithOutput0 { |
+ public: |
+ typedef Output OutputType; |
+ typedef internal::CallbackOutputTraits<Output> Traits; |
+ |
+ DispatcherWithOutput0() : method_(NULL) {} |
+ DispatcherWithOutput0(Method method) : method_(method) { |
+ } |
+ void operator()(T* object, int32_t result) { |
+ (object->*method_)(result, Traits::StorageToPluginArg(output_)); |
+ } |
+ typename Traits::StorageType* output() { |
+ return &output_; |
+ } |
+ private: |
+ Method method_; |
+ |
+ typename Traits::StorageType output_; |
+ }; |
+ |
template <typename Method, typename A> |
class Dispatcher1 { |
public: |
+ Dispatcher1() : method_(NULL) {} |
Dispatcher1(Method method, const A& a) |
: method_(method), |
a_(a) { |
@@ -406,9 +632,34 @@ class CompletionCallbackFactory { |
A a_; |
}; |
+ template <typename Output, typename Method, typename A> |
+ class DispatcherWithOutput1 { |
+ public: |
+ typedef Output OutputType; |
+ typedef internal::CallbackOutputTraits<Output> Traits; |
+ |
+ DispatcherWithOutput1() : method_(NULL) {} |
+ DispatcherWithOutput1(Method method, const A& a) |
+ : method_(method), |
+ a_(a) { |
+ } |
+ void operator()(T* object, int32_t result) { |
+ (object->*method_)(result, Traits::StorageToPluginArg(output_), a_); |
+ } |
+ typename Traits::StorageType* output() { |
+ return &output_; |
+ } |
+ private: |
+ Method method_; |
+ A a_; |
+ |
+ typename Traits::StorageType output_; |
+ }; |
+ |
template <typename Method, typename A, typename B> |
class Dispatcher2 { |
public: |
+ Dispatcher2() : method_(NULL) {} |
Dispatcher2(Method method, const A& a, const B& b) |
: method_(method), |
a_(a), |
@@ -423,9 +674,36 @@ class CompletionCallbackFactory { |
B b_; |
}; |
+ template <typename Output, typename Method, typename A, typename B> |
+ class DispatcherWithOutput2 { |
+ public: |
+ typedef Output OutputType; |
+ typedef internal::CallbackOutputTraits<Output> Traits; |
+ |
+ DispatcherWithOutput2() : method_(NULL) {} |
+ DispatcherWithOutput2(Method method, const A& a, const B& b) |
+ : method_(method), |
+ a_(a), |
+ b_(b) { |
+ } |
+ void operator()(T* object, int32_t result) { |
+ (object->*method_)(result, Traits::StorageToPluginArg(output_), a_, b_); |
+ } |
+ typename Traits::StorageType* output() { |
+ return &output_; |
+ } |
+ private: |
+ Method method_; |
+ A a_; |
+ B b_; |
+ |
+ typename Traits::StorageType output_; |
+ }; |
+ |
template <typename Method, typename A, typename B, typename C> |
class Dispatcher3 { |
public: |
+ Dispatcher3() : method_(NULL) {} |
Dispatcher3(Method method, const A& a, const B& b, const C& c) |
: method_(method), |
a_(a), |
@@ -442,6 +720,36 @@ class CompletionCallbackFactory { |
C c_; |
}; |
+ template <typename Output, typename Method, typename A, typename B, |
+ typename C> |
+ class DispatcherWithOutput3 { |
+ public: |
+ typedef Output OutputType; |
+ typedef internal::CallbackOutputTraits<Output> Traits; |
+ |
+ DispatcherWithOutput3() : method_(NULL) {} |
+ DispatcherWithOutput3(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) { |
+ (object->*method_)(result, Traits::StorageToPluginArg(output_), |
+ a_, b_, c_); |
+ } |
+ 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(); |
@@ -452,14 +760,30 @@ class CompletionCallbackFactory { |
back_pointer_->Release(); |
} |
+ // Takes ownership of the dispatcher pointer, which should be heap allocated. |
template <typename Dispatcher> |
- CompletionCallback NewCallbackHelper(const Dispatcher& dispatcher) { |
+ CompletionCallback NewCallbackHelper(Dispatcher* dispatcher) { |
PP_DCHECK(object_); // Expects a non-null object! |
return CompletionCallback( |
&CallbackData<Dispatcher>::Thunk, |
new CallbackData<Dispatcher>(back_pointer_, dispatcher)); |
} |
+ // Takes ownership of the dispatcher pointer, which should be heap allocated. |
+ template <typename Dispatcher> CompletionCallbackWithOutput< |
+ typename internal::TypeUnwrapper< |
+ typename Dispatcher::OutputType>::StorageType> |
+ NewCallbackWithOutputHelper(Dispatcher* dispatcher) { |
+ PP_DCHECK(object_); // Expects a non-null object! |
+ CallbackData<Dispatcher>* data = |
+ new CallbackData<Dispatcher>(back_pointer_, dispatcher); |
+ |
+ return CompletionCallbackWithOutput<typename Dispatcher::OutputType>( |
+ &CallbackData<Dispatcher>::Thunk, |
+ data, |
+ data->dispatcher()->output()); |
+ } |
+ |
// Disallowed: |
CompletionCallbackFactory(const CompletionCallbackFactory&); |
CompletionCallbackFactory& operator=(const CompletionCallbackFactory&); |