blob: 3cec39a05cc9ca95696dc52d0b714db3090807b9 [file] [log] [blame]
// Copyright (c) 2010 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/cpp/logging.h"
#include "ppapi/cpp/non_thread_safe_ref_count.h"
namespace pp {
// A CompletionCallback provides a wrapper around PP_CompletionCallback.
class CompletionCallback {
public:
// Use this special constructor to create a 'blocking' CompletionCallback
// that may be passed to a method to indicate that the calling thread should
// be blocked until the asynchronous operation corresponding to the method
// completes.
struct Block {};
CompletionCallback(Block) {
cc_ = PP_BlockUntilComplete();
}
CompletionCallback(PP_CompletionCallback_Func func, void* user_data) {
cc_ = PP_MakeCompletionCallback(func, user_data);
}
// Call this method to explicitly 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.
void Run(int32_t result) {
PP_DCHECK(cc_.func);
PP_RunCompletionCallback(&cc_, result);
}
const PP_CompletionCallback& pp_completion_callback() const { return cc_; }
protected:
PP_CompletionCallback cc_;
};
// 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.
//
// NOTE: by default, CompletionCallbackFactory<T> 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 USAGE:
//
// class MyHandler {
// public:
// MyHandler() : factory_(this), offset_(0) {
// }
//
// void ProcessFile(const FileRef& file) {
// CompletionCallback cc = factory_.NewCallback(&MyHandler::DidOpen);
// int32_t rv = fio_.Open(file, PP_FileOpenFlag_Read, cc);
// if (rv != PP_ERROR_WOULDBLOCK)
// cc.Run(rv);
// }
//
// 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_.NewCallback(&MyHandler::DidRead);
// int32_t rv = fio_.Read(offset_, buf_, sizeof(buf_),
// cc.pp_completion_callback());
// if (rv != PP_ERROR_WOULDBLOCK)
// 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_;
// };
//
template <typename T, typename RefCount = NonThreadSafeRefCount>
class CompletionCallbackFactory {
public:
explicit CompletionCallbackFactory(T* object = NULL)
: object_(object) {
InitBackPointer();
}
~CompletionCallbackFactory() {
ResetBackPointer();
}
// Cancels all CompletionCallbacks allocated from this factory.
void CancelAll() {
ResetBackPointer();
InitBackPointer();
}
void Initialize(T* object) {
PP_DCHECK(object);
PP_DCHECK(!object_); // May only initialize once!
object_ = object;
}
T* GetObject() {
return object_;
}
// Allocates a new, single-use CompletionCallback. The CompletionCallback
// must be run in order for the memory allocated by NewCallback to be freed.
// If after passing the CompletionCallback to a PPAPI method, the method does
// not return PP_ERROR_WOULDBLOCK, then you should manually call the
// CompletionCallback's Run method otherwise memory will be leaked.
template <typename Method>
CompletionCallback NewCallback(Method method) {
PP_DCHECK(object_);
return NewCallbackHelper(Dispatcher0<Method>(method));
}
// A copy of "a" will be passed to "method" when the completion callback
// runs.
//
// Method should be of type:
// void (T::*)(int32_t result, const A& a)
//
template <typename Method, typename A>
CompletionCallback NewCallback(Method method, const A& a) {
PP_DCHECK(object_);
return NewCallbackHelper(Dispatcher1<Method, A>(method, a));
}
// A copy of "a" and "b" will be passed to "method" when the completion
// callback runs.
//
// Method should be of type:
// void (T::*)(int32_t result, const A& a, const B& b)
//
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));
}
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_