// 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
/// CompletionCallback
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.
///
/// Note: Blocking completion callbacks are only allowed from
/// from background threads.
CompletionCallback() {
cc_ = PP_BlockUntilComplete();
}
/// A constructor for creating a CompletionCallback
.
///
/// @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 CompletionCallback
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
/// PP_CompletionCallback_Flag
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
/// PP_CompletionCallback_Flag
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 CompletionCallback
.
/// Normally, the system runs a CompletionCallback
after an
/// asynchronous operation completes, but programs may wish to run the
/// CompletionCallback
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 pp_errors.h
(excluding
/// PP_OK_COMPLETIONPENDING
). 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
/// PP_COMPLETIONCALLBACK_FLAG_OPTIONAL
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
/// PP_CompletionCallback
///
/// @return A PP_CompletionCallback
.
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
/// PP_CompletionCallback_Flag
flags.
int32_t flags() const { return cc_.flags; }
/// MayForce() is used when implementing functions taking callbacks.
/// If the callback is required and result
indicates that it has
/// not been scheduled, it will be forced on the main thread.
///
/// Example:
///
/// @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
/// pp_errors.h
. Positive values indicate additional information
/// such as bytes read.
///
/// @return PP_OK_COMPLETIONPENDING
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 CompletionCallback
corresponding to a NULL callback.
CompletionCallback BlockUntilComplete();
/// 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 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 factory_;
/// pp::FileIO fio_;
/// char buf_[4096];
/// int64_t offset_;
/// };
///
/// @endcode
///
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.
/// NewCallback() is equivalent to NewRequiredCallback() below.
///
/// @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(Dispatcher0(method));
}
/// NewRequiredCallback() allocates a new, single-use
/// CompletionCallback
that will always run. 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 NewRequiredCallback(Method method) {
CompletionCallback cc = NewCallback(method);
cc.set_flags(cc.flags() & ~PP_COMPLETIONCALLBACK_FLAG_OPTIONAL);
return cc;
}
/// 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;
}
/// NewCallback() allocates a new, single-use CompletionCallback
.
/// The CompletionCallback
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:
/// 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(Dispatcher1(method, a));
}
/// NewRequiredCallback() allocates a new, single-use
/// CompletionCallback
that will always run. 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 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
/// 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;
}
/// NewCallback() allocates a new, single-use
/// CompletionCallback
.
/// The CompletionCallback
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:
/// 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(Dispatcher2(method, a, b));
}
/// NewRequiredCallback() allocates a new, single-use
/// CompletionCallback
that will always run. 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 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
/// 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;
}
/// NewCallback() allocates a new, single-use
/// CompletionCallback
.
/// The CompletionCallback
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:
///
/// 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(Dispatcher3(method, a, b, c));
}
/// NewRequiredCallback() allocates a new, single-use
/// CompletionCallback
that will always run. 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 NewRequiredCallback(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;
}
/// 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;
}
private:
class BackPointer {
public:
typedef CompletionCallbackFactory 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
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(user_data);
T* object = self->back_pointer_->GetObject();
if (object)
self->dispatcher_(object, result);
delete self;
}
private:
typedef CallbackData Self;
BackPointer* back_pointer_;
Dispatcher dispatcher_;
};
template
class Dispatcher0 {
public:
Dispatcher0(Method method) : method_(method) {
}
void operator()(T* object, int32_t result) {
(object->*method_)(result);
}
private:
Method method_;
};
template
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
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_;
};
template
class Dispatcher3 {
public:
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) {
(object->*method_)(result, a_, b_, c_);
}
private:
Method method_;
A a_;
B b_;
C c_;
};
void InitBackPointer() {
back_pointer_ = new BackPointer(this);
back_pointer_->AddRef();
}
void ResetBackPointer() {
back_pointer_->DropFactory();
back_pointer_->Release();
}
template
CompletionCallback NewCallbackHelper(const Dispatcher& dispatcher) {
PP_DCHECK(object_); // Expects a non-null object!
return CompletionCallback(
&CallbackData::Thunk,
new CallbackData(back_pointer_, dispatcher));
}
// Disallowed:
CompletionCallbackFactory(const CompletionCallbackFactory&);
CompletionCallbackFactory& operator=(const CompletionCallbackFactory&);
T* object_;
BackPointer* back_pointer_;
};
} // namespace pp
#endif // PPAPI_CPP_COMPLETION_CALLBACK_H_