From e10b2b28a02d87eb9903966efd944790e3fc2877 Mon Sep 17 00:00:00 2001
From: "brettw@chromium.org"
 <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>
Date: Wed, 14 Mar 2012 23:17:14 +0000
Subject: Add C++ wrappers for output parameters.

Define helper routines for doing array output using the new PP_OutputArray
struct. Define routines in the completion callback factory for doing output
parameters as parameters to the callback function.

BUG=
TEST=


Review URL: http://codereview.chromium.org/9651002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126781 0039d316-1c4b-4281-b951-d872f2087c98
---
 ppapi/utility/completion_callback_factory.h | 424 ++++++++++++++++++++++++----
 1 file changed, 374 insertions(+), 50 deletions(-)

(limited to 'ppapi/utility')

diff --git a/ppapi/utility/completion_callback_factory.h b/ppapi/utility/completion_callback_factory.h
index e0b1f37..964cd02 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&);
-- 
cgit v1.1