Chromium Code Reviews| Index: base/closure.h |
| diff --git a/base/closure.h b/base/closure.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c720349f6439ff454a2f7d531b9cfd32f896c3d2 |
| --- /dev/null |
| +++ b/base/closure.h |
| @@ -0,0 +1,339 @@ |
| +// Copyright (c) 2009 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 BASE_CLOSURE_H_ |
| +#define BASE_CLOSURE_H_ |
| +#pragma once |
| + |
| +#include <tr1/functional> |
| + |
| +#include "base/ref_counted.h" |
| +#include "base/scoped_ptr.h" |
| +#include "base/tracked.h" |
| + |
| +namespace base { |
| + |
| +extern void DoNothing(); // Defined in base/task.h. |
| + |
| +// We are trying to mimic value-object semantics of tr1::function in the |
| +// Closure API. Thus, we need to detach the tracked object, and some other |
| +// state as a shared state. |
| +// |
| +// As is, there are some threadsafety issues, but the general idea should be |
| +// correct. |
| +class ClosureState : public RefCountedThreadSafe<ClosureState> { |
| + public: |
| + // TODO(ajwong): Put these behind an accessor. |
| + ::std::tr1::function<void()> release_thunk_; |
| + |
| + explicit ClosureState(tracked_objects::Tracked* tracked) |
| + : release_thunk_(&DoNothing), |
| + tracked_(tracked) { |
| + } |
| + |
| + ~ClosureState() { |
| + release_thunk_(); |
| + } |
| + |
| + tracked_objects::Tracked* tracked() const { |
| + return tracked_.get(); |
| + } |
| + |
| + private: |
| + scoped_ptr<tracked_objects::Tracked> tracked_; |
| +}; |
| + |
| + |
| +// These are needed to implement a wrapper that disables refcounting on |
| +// objects used with a Closure. |
| +// |
| +// TODO(ajwong): If we implement this similar to std::tr1::reference_wrapper we |
| +// can probably simplify the ObjectTraits below and remove one specialization. |
| +template <typename O> |
| +class UnretainedWrapper { |
| + public: |
| + explicit UnretainedWrapper(O* o) : obj_(o) {} |
| + O* get() const { return obj_; } |
| + |
| + private: |
| + O* obj_; |
| +}; |
| + |
| +template <typename O> |
| +UnretainedWrapper<O> Unretained(O* o) { |
| + return UnretainedWrapper<O>(o); |
| +} |
| + |
| +// Note, Closure does not support binding a member function to a reference |
| +// object. That is, this will not compile: |
| +// |
| +// Foo f; |
| +// Closure c = Closure(&Foo::func, f); |
| +// |
| +// You have to pass in a pointer to it. |
| +// |
| +// Foo f; |
| +// Closure c = Closure(&Foo::func, &f); |
| +// |
| +// We can make the syntax work out, but it's extra complex and given that we |
| +// expect Closure's to refcount the member functions they abstract unless |
| +// explicitly told not to, allowing pass by reference doesn't make much sense. |
| +class Closure { |
| + public: |
| + |
| +// --- Traits object defintions -- |
| +// This handle changing the syntax, and refcounting for the various types of |
| +// parameters that can be passed in. |
| + |
| + template <typename O, bool is_member_function_pointer> |
| + struct ObjectTraits; |
| + |
| + // Handle regular pointers. |
| + template <typename O> |
| + struct ObjectTraits<O*, true> { |
| + static O* Unwrap(O* o) { |
| + return o; |
| + } |
| + |
| + static ::std::tr1::function<void()> RefThunk(O* o) { |
|
willchan no longer on Chromium
2011/01/05 00:30:17
Do we really need to use Thunks here? Is the flex
awong
2011/01/05 03:17:42
Looking at this more, I don't think it's quite tha
willchan no longer on Chromium
2011/01/05 19:44:09
I never said template parameter. I said parameter
|
| + return ::std::tr1::bind(&O::AddRef, o); |
| + } |
| + static ::std::tr1::function<void()> ReleaseThunk(O* o) { |
| + return ::std::tr1::bind(&O::Release, o); |
| + } |
| + }; |
| + |
| + // If it's not a member function, there is no refcounting, and pass through |
| + // the object. |
| + template <typename O> |
| + struct ObjectTraits<O, false> { |
| + static O Unwrap(O o) { |
| + return o; |
| + } |
| + |
| + static ::std::tr1::function<void()> RefThunk(O o) { |
| + return ::std::tr1::bind(&DoNothing); |
| + } |
| + static ::std::tr1::function<void()> ReleaseThunk(O o) { |
| + return ::std::tr1::bind(&DoNothing); |
| + } |
| + }; |
| + |
| + // Object wrapped in Unretained() should not refcount anything, and should |
| + // unwrap to a pointer to their type. |
| + template <typename O> |
| + struct ObjectTraits<UnretainedWrapper<O>, true> { |
| + static O* Unwrap(UnretainedWrapper<O> o) { |
| + return o.get(); |
| + } |
| + |
| + static ::std::tr1::function<void()> RefThunk(UnretainedWrapper<O> o) { |
| + return ::std::tr1::bind(&DoNothing); |
| + } |
| + |
| + static ::std::tr1::function<void()> ReleaseThunk(UnretainedWrapper<O> o) { |
| + return ::std::tr1::bind(&DoNothing); |
| + } |
| + }; |
| + |
| + |
| +// ---- Constructors ---- |
| + template <typename Sig> |
| + Closure(Sig f) |
| + : state_(new ClosureState(new tracked_objects::Tracked())) { |
| + func_ = ::std::tr1::bind(f); |
| + state_->release_thunk_ = ::std::tr1::bind(&DoNothing); |
| + } |
| + |
| + template <typename Sig, typename A1> |
| + Closure(Sig f, A1 a1) |
| + : state_(new ClosureState(new tracked_objects::Tracked())) { |
| + using std::tr1::is_member_function_pointer; |
| + func_ = ::std::tr1::bind( |
| + f, |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >::Unwrap(a1)); |
| + |
| + // Handle the refcounting. |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >::RefThunk(a1)(); |
| + state_->release_thunk_ = |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >:: |
| + ReleaseThunk(a1); |
| + } |
| + |
| + template <typename Sig, typename A1, typename A2> |
| + Closure(Sig f, A1 a1, A2 a2) |
| + : state_(new ClosureState(new tracked_objects::Tracked())) { |
| + using std::tr1::is_member_function_pointer; |
| + func_ = ::std::tr1::bind( |
| + f, |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >::Unwrap(a1), |
| + a2); |
| + |
| + // Handle the refcounting. |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >::RefThunk(a1)(); |
| + state_->release_thunk_ = |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >:: |
| + ReleaseThunk(a1); |
| + } |
| + |
| + template <typename Sig, typename A1, typename A2, typename A3> |
| + Closure(Sig f, A1 a1, A2 a2, A3 a3) |
| + : state_(new ClosureState(new tracked_objects::Tracked())) { |
| + using std::tr1::is_member_function_pointer; |
| + func_ = ::std::tr1::bind( |
| + f, |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >::Unwrap(a1), |
| + a2, |
| + a3); |
| + |
| + // Handle the refcounting. |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >::RefThunk(a1)(); |
| + state_->release_thunk_ = |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >:: |
| + ReleaseThunk(a1); |
| + } |
| + |
| + template <typename Sig, typename A1, typename A2, typename A3, |
| + typename A4> |
| + Closure(Sig f, A1 a1, A2 a2, A3 a3, A4 a4) |
| + : state_(new ClosureState(new tracked_objects::Tracked())) { |
| + using std::tr1::is_member_function_pointer; |
| + func_ = ::std::tr1::bind( |
| + f, |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >::Unwrap(a1), |
| + a2, |
| + a3, |
| + a4); |
| + |
| + // Handle the refcounting. |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >::RefThunk(a1)(); |
| + state_->release_thunk_ = |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >:: |
| + ReleaseThunk(a1); |
| + } |
| + |
| + template <typename Sig, typename A1, typename A2, typename A3, |
| + typename A4, typename A5> |
| + Closure(Sig f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) |
| + : state_(new ClosureState(new tracked_objects::Tracked())) { |
| + using std::tr1::is_member_function_pointer; |
| + func_ = ::std::tr1::bind( |
| + f, |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >::Unwrap(a1), |
| + a2, |
| + a3, |
| + a4, |
| + a5); |
| + |
| + // Handle the refcounting. |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >::RefThunk(a1)(); |
| + state_->release_thunk_ = |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >:: |
| + ReleaseThunk(a1); |
| + } |
| + |
| + template <typename Sig, typename A1, typename A2, typename A3, |
| + typename A4, typename A5, typename A6> |
| + Closure(Sig f, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) |
| + : state_(new ClosureState(new tracked_objects::Tracked())) { |
| + using std::tr1::is_member_function_pointer; |
| + func_ = ::std::tr1::bind( |
| + f, |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >::Unwrap(a1), |
| + a2, |
| + a3, |
| + a4, |
| + a5, |
| + a6); |
| + |
| + // Handle the refcounting. |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >::RefThunk(a1)(); |
| + state_->release_thunk_ = |
| + ObjectTraits<A1, is_member_function_pointer<Sig>::value >:: |
| + ReleaseThunk(a1); |
| + } |
| + |
| + // This is to make Closure mimic tr1::function<void()>'s interface. It could |
| + // also just be void Run(), but mimicing the interface allows us to |
| + // substitute Closure in whereever tr1::function<void()> could be used in the |
| + // tr1 libraries. |
| + void operator()() { func_(); } |
| + |
| + tracked_objects::Tracked* tracked() { |
| + return state_->tracked(); |
| + } |
| + |
| + void Cancel() { |
| + func_ = ::std::tr1::bind(&DoNothing); |
|
willchan no longer on Chromium
2011/01/05 00:30:17
can't this just be std::tr1::function()?
awong
2011/01/05 03:17:42
That actually asserted.
However, I can remove the
willchan no longer on Chromium
2011/01/05 19:44:09
Oh really? Ok, whatever.
|
| + } |
| + |
| + private: |
| + // Don't let us convert from tr1 quickly. |
| + Closure(::std::tr1::function<void(void)> f) |
| + : state_(new ClosureState(new tracked_objects::Tracked())) { |
| + func_ = ::std::tr1::bind(f); |
| + } |
| + |
| + // This is the tr1 function pointer. |
| + ::std::tr1::function<void()> func_; |
| + |
| + scoped_refptr<ClosureState> state_; |
| +}; |
| + |
| +// Wraps a Closure or a tr1::function<void()) to automatically cancel a task |
| +// when the ClosureCanceller is deleted. This allows a caller to "nop" all |
| +// outstanding callbacks registered with the ClosureCanceller. |
| +class ClosureCanceller { |
| + public: |
| + ClosureCanceller() : cancel_state_(new CancelState()) {} |
| + |
| + ~ClosureCanceller() { |
| + cancel_state_->is_canceled = true; |
| + } |
| + |
| + template <typename T> |
| + Closure Wrap(T c) { |
| + using ::std::tr1::bind; |
| + return base::Closure(bind(&ClosureCanceller::Run<T>, cancel_state_, c)); |
| + } |
| + |
| + bool empty() const { |
| + // The ClosureCanceller has the only reference, no tasks are outstanding. |
| + return cancel_state_->HasOneRef(); |
| + } |
| + |
| + void RevokeAll() { |
| + // Cancel all outstanding, then create a new cancel state so this object may |
| + // be reused. |
| + cancel_state_->is_canceled = true; |
| + cancel_state_ = new CancelState(); |
| + } |
| + |
| + private: |
| + // The ScopedRunnableMethodFactory uses a WeakPtr. This is because it is |
| + // actually reimplementing the storage for the task object, so a pointer is |
| + // necessary and thus WeakPtr can be overloaded to serve as a flag. |
| + // |
| + // In this design, it seems overkill to use WeakPtr instead of a simple flag |
| + // class (which WeakPtr eventually devolves into anyways). |
| + class CancelState : public RefCounted<CancelState> { |
| + public: |
| + CancelState() : is_canceled(false) {} |
| + |
| + bool is_canceled; |
| + }; |
| + |
| + template <typename T> |
| + static void Run(scoped_refptr<CancelState> cancel_state, T c) { |
| + if (!cancel_state->is_canceled) { |
| + c(); |
| + } |
| + } |
| + |
| + scoped_refptr<CancelState> cancel_state_; |
| +}; |
| + |
| +} // namespace base |
| + |
| +#endif // BASE_CLOSURE_H_ |