blob: 7dee4db156d78466f14603cf9a11d48fcb64131d [file] [log] [blame]
// Copyright (c) 2011 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_CPP_COMPLETION_CALLBACK_H_
#define PPAPI_CPP_COMPLETION_CALLBACK_H_
#include "ppapi/c/pp_completion_callback.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/cpp/logging.h"
#include "ppapi/cpp/non_thread_safe_ref_count.h"
/// @file
/// This file defines the API to create and run a callback.
namespace pp {
/// This API enables you to implement and receive callbacks when
/// Pepper operations complete asynchronously.
class CompletionCallback {
public:
/// The default constructor will create a blocking
/// <code>CompletionCallback</code> that can be passed to a method to
/// indicate that the calling thread should be blocked until the asynchronous
/// operation corresponding to the method completes.
///
/// <strong>Note:</strong> Blocking completion callbacks are only allowed from
/// from background threads.
CompletionCallback() {
cc_ = PP_BlockUntilComplete();
}
/// A constructor for creating a <code>CompletionCallback</code>.
///
/// @param[in] func The function to be called on completion.
/// @param[in] user_data The user data to be passed to the callback function.
/// This is optional and is typically used to help track state in case of
/// multiple pending callbacks.
CompletionCallback(PP_CompletionCallback_Func func, void* user_data) {
cc_ = PP_MakeCompletionCallback(func, user_data);
}
/// A constructor for creating a <code>CompletionCallback</code> with
/// specified flags.
///
/// @param[in] func The function to be called on completion.
/// @param[in] user_data The user data to be passed to the callback function.
/// This is optional and is typically used to help track state in case of
/// multiple pending callbacks.
/// @param[in] flags Bit field combination of
/// <code>PP_CompletionCallback_Flag</code> flags used to control how
/// non-NULL callbacks are scheduled by asynchronous methods.
CompletionCallback(PP_CompletionCallback_Func func, void* user_data,
int32_t flags) {
cc_ = PP_MakeCompletionCallback(func, user_data);
cc_.flags = flags;
}
/// The set_flags() function is used to set the flags used to control
/// how non-NULL callbacks are scheduled by asynchronous methods.
///
/// @param[in] flags Bit field combination of
/// <code>PP_CompletionCallback_Flag</code> flags used to control how
/// non-NULL callbacks are scheduled by asynchronous methods.
void set_flags(int32_t flags) { cc_.flags = flags; }
/// Run() is used to run the <code>CompletionCallback</code>.
/// Normally, the system runs a <code>CompletionCallback</code> after an
/// asynchronous operation completes, but programs may wish to run the
/// <code>CompletionCallback</code> manually in order to reuse the same code
/// paths.
///
/// @param[in] result The result of the operation to be passed to the
/// callback function. Non-positive values correspond to the error codes
/// from <code>pp_errors.h</code> (excluding
/// <code>PP_OK_COMPLETIONPENDING</code>). Positive values indicate
/// additional information such as bytes read.
void Run(int32_t result) {
PP_DCHECK(cc_.func);
PP_RunCompletionCallback(&cc_, result);
}
/// IsOptional() is used to determine the setting of the
/// <code>PP_COMPLETIONCALLBACK_FLAG_OPTIONAL</code> flag. This flag allows
/// any method taking such callback to complete synchronously
/// and not call the callback if the operation would not block. This is useful
/// when performance is an issue, and the operation bandwidth should not be
/// limited to the processing speed of the message loop.
///
/// On synchronous method completion, the completion result will be returned
/// by the method itself. Otherwise, the method will return
/// PP_OK_COMPLETIONPENDING, and the callback will be invoked asynchronously
/// on the main thread of Pepper execution.
///
/// @return true if this callback is optional, otherwise false.
bool IsOptional() const {
return (cc_.func == NULL ||
(cc_.flags & PP_COMPLETIONCALLBACK_FLAG_OPTIONAL) != 0);
}
/// The pp_completion_callback() function returns the underlying
/// <code>PP_CompletionCallback</code>
///
/// @return A <code>PP_CompletionCallback</code>.
const PP_CompletionCallback& pp_completion_callback() const { return cc_; }
/// The flags() function returns flags used to control how non-NULL callbacks
/// are scheduled by asynchronous methods.
///
/// @return An int32_t containing a bit field combination of
/// <code>PP_CompletionCallback_Flag</code> flags.
int32_t flags() const { return cc_.flags; }
/// MayForce() is used when implementing functions taking callbacks.
/// If the callback is required and <code>result</code> indicates that it has
/// not been scheduled, it will be forced on the main thread.
///
/// <strong>Example:</strong>
///
/// @code
///
/// int32_t OpenURL(pp::URLLoader* loader,
/// pp::URLRequestInfo* url_request_info,
/// const CompletionCallback& cc) {
/// if (loader == NULL || url_request_info == NULL)
/// return cc.MayForce(PP_ERROR_BADRESOURCE);
/// return loader->Open(*loader, *url_request_info, cc);
/// }
///
/// @endcode
///
/// @param[in] result PP_OK_COMPLETIONPENDING or the result of the completed
/// operation to be passed to the callback function. PP_OK_COMPLETIONPENDING
/// indicates that the callback has already been scheduled. Other
/// non-positive values correspond to error codes from
/// <code>pp_errors.h</code>. Positive values indicate additional information
/// such as bytes read.
///
/// @return <code>PP_OK_COMPLETIONPENDING</code> if the callback has been
/// forced, result parameter otherwise.
int32_t MayForce(int32_t result) const {
if (result == PP_OK_COMPLETIONPENDING || IsOptional())
return result;
Module::Get()->core()->CallOnMainThread(0, *this, result);
return PP_OK_COMPLETIONPENDING;
}
protected:
PP_CompletionCallback cc_;
};
/// BlockUntilComplete() is used in place of an actual completion callback
/// to request blocking behavior. If specified, the calling thread will block
/// until the function completes. Blocking completion callbacks are only
/// allowed from background threads.
///
/// @return A <code>CompletionCallback</code> corresponding to a NULL callback.
CompletionCallback BlockUntilComplete();
/// CompletionCallbackFactory<T> 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.
///
/// <strong>Note: </strong><code>CompletionCallbackFactory<T></code> 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 <code>CompletionCallbackFactory</code> on the main thread, create
/// callbacks from any thread and pass them to CallOnMainThread().
///
/// <strong>Example: </strong>
///
/// @code
///
/// class MyHandler {
/// public:
/// MyHandler() : factory_(this), offset_(0) {
/// }
///
/// void ProcessFile(const FileRef& file) {
/// CompletionCallback cc = factory_.NewRequiredCallback(
/// &MyHandler::DidOpen);
/// int32_t rv = fio_.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').
/// }
/// }
///
/// 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);
/// }
///
/// void ProcessBytes(const char* bytes, int32_t length) {
/// // Do work ...
/// }
///
/// pp::CompletionCallbackFactory<MyHandler> factory_;
/// pp::FileIO fio_;
/// char buf_[4096];
/// int64_t offset_;
/// };
///
/// @endcode
///
template <typename T, typename RefCount = NonThreadSafeRefCount>
class CompletionCallbackFactory {
public:
/// This constructor creates a <code>CompletionCallbackFactory</code>
/// bound to an object. If the constructor is called without an argument,
/// the default value of <code>NULL</code> 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
/// <code>CompletionCallbackFactory</code>. The default value of this
/// parameter is <code>NULL</code>.
explicit CompletionCallbackFactory(T* object = NULL)
: object_(object) {
InitBackPointer();
}
/// Destructor.
~CompletionCallbackFactory() {
ResetBackPointer();
}
/// CancelAll() cancels all <code>CompletionCallbacks</code> allocated from
/// this factory.
void CancelAll() {
ResetBackPointer();
InitBackPointer();
}
/// Initialize() binds the <code>CallbackFactory</code> to a particular
/// object. Use this when the object is not available at
/// <code>CallbackFactory</code> creation, and the <code>NULL</code> 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 <code>CompletionCallback</code> created by this
/// <code>CompletionCallbackFactory</code>.
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 <code>CompletionCallback</code>.
/// The <code>CompletionCallback</code> must be run in order for the memory
/// allocated by the methods to be freed.
/// NewCallback() is equivalent to NewRequiredCallback() below.
///
/// @param[in] method The method to be invoked upon completion of the
/// operation.
///
/// @return A <code>CompletionCallback</code>.
template <typename Method>
CompletionCallback NewCallback(Method method) {
PP_DCHECK(object_);
return NewCallbackHelper(Dispatcher0<Method>(method));
}
/// NewRequiredCallback() allocates a new, single-use
/// <code>CompletionCallback</code> that will always run. 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 Method>
CompletionCallback NewRequiredCallback(Method method) {
CompletionCallback cc = NewCallback(method);
cc.set_flags(cc.flags() & ~PP_COMPLETIONCALLBACK_FLAG_OPTIONAL);
return cc;
}
/// NewOptionalCallback() allocates a new, single-use
/// <code>CompletionCallback</code> 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 <code>CompletionCallback</code>.
template <typename Method>
CompletionCallback NewOptionalCallback(Method method) {
CompletionCallback cc = NewCallback(method);
cc.set_flags(cc.flags() | PP_COMPLETIONCALLBACK_FLAG_OPTIONAL);
return cc;
}
/// 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.
/// NewCallback() is equivalent to NewRequiredCallback() below.
///
/// @param[in] method The method to be invoked upon completion of the
/// operation. Method should be of type:
/// <code>void (T::*)(int32_t result, const A& a)</code>
///
/// @param[in] a Passed to <code>method</code> when the completion callback
/// runs.
///
/// @return A <code>CompletionCallback</code>.
template <typename Method, typename A>
CompletionCallback NewCallback(Method method, const A& a) {
PP_DCHECK(object_);
return NewCallbackHelper(Dispatcher1<Method, A>(method, a));
}
/// NewRequiredCallback() allocates a new, single-use
/// <code>CompletionCallback</code> that will always run. 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. Method should be of type:
/// <code>void (T::*)(int32_t result, const A& a)</code>
///
/// @param[in] a Passed to <code>method</code> when the completion callback
/// runs.
///
/// @return A <code>CompletionCallback</code>.
template <typename Method, typename A>
CompletionCallback NewRequiredCallback(Method method, const A& a) {
CompletionCallback cc = NewCallback(method, a);
cc.set_flags(cc.flags() & ~PP_COMPLETIONCALLBACK_FLAG_OPTIONAL);
return cc;
}
/// NewOptionalCallback() allocates a new, single-use
/// <code>CompletionCallback</code> 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:
/// <code>void (T::*)(int32_t result, const A& a)</code>
///
/// @param[in] a Passed to <code>method</code> when the completion callback
/// runs.
///
/// @return A <code>CompletionCallback</code>.
template <typename Method, typename A>
CompletionCallback NewOptionalCallback(Method method, const A& a) {
CompletionCallback cc = NewCallback(method, a);
cc.set_flags(cc.flags() | PP_COMPLETIONCALLBACK_FLAG_OPTIONAL);
return cc;
}
/// 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.
/// NewCallback() is equivalent to NewRequiredCallback() below.
///
/// @param method The method taking the callback. Method should be of type:
/// <code>void (T::*)(int32_t result, const A& a, const B& b)</code>
///
/// @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 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));
}
/// NewRequiredCallback() allocates a new, single-use
/// <code>CompletionCallback</code> that will always run. The
/// <code>CompletionCallback</code> 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:
/// <code>void (T::*)(int32_t result, const A& a, const B& b)</code>
///
/// @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 Method, typename A, typename B>
CompletionCallback NewRequiredCallback(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;
}
/// NewOptionalCallback() allocates a new, single-use
/// <code>CompletionCallback</code> 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:
/// <code>void (T::*)(int32_t result, const A& a, const B& b)</code>
///
/// @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 Method, typename A, typename B>
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;
}
private:
class BackPointer {
public:
typedef CompletionCallbackFactory<T, RefCount> FactoryType;
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 <typename Dispatcher>
class CallbackData {
public:
CallbackData(BackPointer* back_pointer, const Dispatcher& dispatcher)
: back_pointer_(back_pointer),
dispatcher_(dispatcher) {
back_pointer_->AddRef();
}
~CallbackData() {
back_pointer_->Release();
}
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);
delete self;
}
private:
typedef CallbackData<Dispatcher> Self;
BackPointer* back_pointer_;
Dispatcher dispatcher_;
};
template <typename Method>
class Dispatcher0 {
public:
Dispatcher0(Method method) : method_(method) {
}
void operator()(T* object, int32_t result) {
(object->*method_)(result);
}
private:
Method method_;
};
template <typename Method, typename A>
class Dispatcher1 {
public:
Dispatcher1(Method method, const A& a)
: method_(method),
a_(a) {
}
void operator()(T* object, int32_t result) {
(object->*method_)(result, a_);
}
private:
Method method_;
A a_;
};
template <typename Method, typename A, typename B>
class Dispatcher2 {
public:
Dispatcher2(Method method, const A& a, const B& b)
: method_(method),
a_(a),
b_(b) {
}
void operator()(T* object, int32_t result) {
(object->*method_)(result, a_, b_);
}
private:
Method method_;
A a_;
B b_;
};
void InitBackPointer() {
back_pointer_ = new BackPointer(this);
back_pointer_->AddRef();
}
void ResetBackPointer() {
back_pointer_->DropFactory();
back_pointer_->Release();
}
template <typename Dispatcher>
CompletionCallback NewCallbackHelper(const Dispatcher& dispatcher) {
PP_DCHECK(object_); // Expects a non-null object!
return CompletionCallback(
&CallbackData<Dispatcher>::Thunk,
new CallbackData<Dispatcher>(back_pointer_, dispatcher));
}
// Disallowed:
CompletionCallbackFactory(const CompletionCallbackFactory&);
CompletionCallbackFactory& operator=(const CompletionCallbackFactory&);
T* object_;
BackPointer* back_pointer_;
};
} // namespace pp
#endif // PPAPI_CPP_COMPLETION_CALLBACK_H_