Chromium Code Reviews| Index: base/closure.h |
| diff --git a/base/closure.h b/base/closure.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ca891c7879bd9dfa65a630ba4dee9cbf545e6cd7 |
| --- /dev/null |
| +++ b/base/closure.h |
| @@ -0,0 +1,338 @@ |
| +// Copyright (c) 2009 The Chromium Authors. All rights reserved. |
|
brettw
2011/01/04 19:12:17
Copyright is 2 years out of date.
awong
2011/01/04 20:57:13
Done.
|
| +// 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) |
|
brettw
2011/01/04 19:12:17
I think we'd want the implementation of all of the
awong
2011/01/04 20:57:13
Right...I'd like to avoid breaking out a cc file f
|
| + : tracked_(tracked) { |
| + } |
| + |
| + ~ClosureState() { |
| + release_thunk_(); |
|
willchan no longer on Chromium
2011/01/04 22:27:23
Why? We don't do this for Tasks.
|
| + } |
| + |
| + tracked_objects::Tracked* tracked() const { |
| + return tracked_.get(); |
| + } |
| + |
| + private: |
| + scoped_ptr<tracked_objects::Tracked> tracked_; |
|
willchan no longer on Chromium
2011/01/04 22:27:23
I'm not sure, but this might be a design flaw. Cl
|
| +}; |
|
brettw
2011/01/04 19:12:17
DISALLOW_COPY_AND_ASSIGN?
awong
2011/01/04 20:57:13
Done.
|
| + |
| + |
| +// 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() { 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: |
|
brettw
2011/01/04 19:12:17
I find the existence of this "wrong" code confusin
awong
2011/01/04 20:57:13
Removed the incorrect code, and added a comment.
|
| +// |
| +// 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 -- |
|
brettw
2011/01/04 19:12:17
I'd make sure everything inside the class is inden
awong
2011/01/04 20:57:13
fixed.
|
| +// 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) { |
|
brettw
2011/01/04 19:12:17
I've never seen us write "::std" (everywhere we ju
awong
2011/01/04 20:57:13
Done.
|
| + 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())) { |
|
brettw
2011/01/04 19:12:17
All these wrapped initializers should be indented
awong
2011/01/04 20:57:13
Done.
|
| + 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())) { |
|
brettw
2011/01/04 19:12:17
bind makes an object on the heap underneath, right
brettw
2011/01/04 19:14:58
Actually NewRunnableMethod of course make a "new"
awong
2011/01/04 20:57:13
Right...really, the tr1::function<> reference shou
willchan no longer on Chromium
2011/01/04 22:27:23
I don't think tr1::bind exposes the allocator as a
|
| + 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) |
|
brettw
2011/01/04 19:12:17
In NewRunnableMethod we pass the arguments as cons
awong
2011/01/04 20:57:13
Eek...good point. I was copying the tr1::bind API
willchan no longer on Chromium
2011/01/04 21:09:22
Is this correct? NewRunnableMethod() will create
|
| + : 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); |
| + } |
| + |
| + 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()) {} |
|
brettw
2011/01/04 19:12:17
Ditto for non-inlining these.
awong
2011/01/04 20:57:13
deferring addressing this for now.
|
| + |
| + ~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 { |
|
brettw
2011/01/04 19:12:17
Since this calls an uppercase (non-inlinable-for-f
awong
2011/01/04 20:57:13
Was replicating the ScopedRunnableMethodFactory AP
|
| + // 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> { |
|
brettw
2011/01/04 19:12:17
The definition of this can probably be moved to th
awong
2011/01/04 20:57:13
deferred via TODO at the top of the file.
|
| + 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_; |
| +}; |
|
brettw
2011/01/04 19:12:17
DISALLOW_COPY_AND_ASSIGN?
awong
2011/01/04 20:57:13
Done.
|
| + |
| +} // namespace base |
| + |
| +#endif // BASE_CLOSURE_H_ |