| // 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_ |